Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/vector/gdevpdfu.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Output utilities for PDF-writing driver */
18
#include "memory_.h"
19
#include "jpeglib_.h"   /* for sdct.h */
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gscdefs.h"
23
#include "gsdsrc.h"
24
#include "gsfunc.h"
25
#include "gsfunc3.h"
26
#include "gdevpdfx.h"
27
#include "gdevpdfo.h"
28
#include "gdevpdfg.h"
29
#include "gdevpdtd.h"
30
#include "strimpl.h"
31
#include "sa85x.h"
32
#include "scfx.h"
33
#include "sdct.h"
34
#include "slzwx.h"
35
#include "spngpx.h"
36
#include "srlx.h"
37
#include "sarc4.h"
38
#include "smd5.h"
39
#include "sbrotlix.h"
40
#include "sstring.h"
41
#include "strmio.h"
42
#include "szlibx.h"
43
#include "gsagl.h"
44
45
#include "opdfread.h"
46
#include "gs_mgl_e.h"
47
#include "gs_mro_e.h"
48
49
#include "gdevpdtx.h"
50
#include "gdevpdts.h"
51
52
extern single_glyph_list_t SingleGlyphList[];
53
54
    /* Define the size of internal stream buffers. */
55
/* (This is not a limitation, it only affects performance.) */
56
31.8k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
31.8k
#  define Flate_filter_name "FlateDecode"
61
31.8k
#  define Flate_filter_template s_zlibE_template
62
31.8k
#  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.68M
  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
15.6k
{
319
15.6k
    int index = 0;
320
321
63.6M
    do {
322
63.6M
        if (opdfread_ps[index] == 0x00)
323
15.6k
            break;
324
63.6M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
63.6M
        index++;
326
63.6M
    } while (1);
327
15.6k
    return 0;
328
15.6k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
15.6k
{
332
15.6k
    int index = 0;
333
334
612k
    do {
335
612k
        if (gs_mro_e_ps[index] == 0x00)
336
15.6k
            break;
337
596k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
596k
        index++;
339
596k
    } while (1);
340
341
15.6k
    if (HaveTrueTypes) {
342
15.6k
        char Buffer[256];
343
15.6k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
15.6k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
15.6k
        stream_write(s, Buffer, strlen(Buffer));
347
65.9M
        while (entry->Glyph) {
348
65.9M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
65.9M
            stream_write(s, Buffer, strlen(Buffer));
350
65.9M
            entry++;
351
65.9M
        };
352
15.6k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
15.6k
        stream_write(s, Buffer, strlen(Buffer));
354
355
15.6k
        index = 0;
356
565k
        do {
357
565k
            if (gs_mgl_e_ps[index] == 0x00)
358
15.6k
                break;
359
549k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
549k
            index++;
361
549k
        } while (1);
362
15.6k
    }
363
15.6k
    return 0;
364
15.6k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
15.6k
{
369
15.6k
    int code;
370
371
15.6k
    code = write_opdfread(s);
372
15.6k
    if (code < 0)
373
0
        return code;
374
375
15.6k
    code = write_tt_encodings(s, HaveTrueTypes);
376
15.6k
    return code;
377
378
15.6k
}
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
39.3k
{
455
39.3k
    const char * const *argv = NULL;
456
39.3k
    const char *arg;
457
39.3k
    int towrite, length, i, j, argc;
458
459
39.3k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
39.3k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
39.3k
    length = 12;
463
747k
    for (i=0;i < argc; i++) {
464
708k
        arg = argv[i];
465
466
708k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
708k
        } else {
470
708k
            stream_write(s, (byte *)" ", 1);
471
708k
            length++;
472
708k
        }
473
474
708k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
708k
        else
477
708k
            towrite = strlen(arg);
478
479
708k
        length += towrite;
480
481
8.55M
        for (j=0;j < towrite;j++) {
482
7.84M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
7.84M
            } else {
485
7.84M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
7.84M
                } else {
488
7.84M
                    stream_write(s, (byte *)&arg[j], 1);
489
7.84M
                }
490
7.84M
            }
491
7.84M
        }
492
708k
    }
493
39.3k
    stream_write(s, (byte *)"\n", 1);
494
39.3k
    return 0;
495
39.3k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
15.6k
{
499
15.6k
    stream *s = pdev->strm;
500
501
15.6k
    if (pdev->ForOPDFRead) {
502
15.6k
        char cre_date_time[41];
503
15.6k
        int code, status, cre_date_time_len;
504
15.6k
        char BBox[256];
505
506
15.6k
        if (pdev->Eps2Write)
507
13.3k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
2.35k
        else
509
2.35k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
15.6k
        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
15.6k
        {
515
15.6k
            int pagecount = 1, j;
516
15.6k
            double urx=0, ury=0;
517
518
266k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
251k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
270k
                for (; pres != 0; pres = pres->next)
522
19.4k
                    if ((!pres->named || pdev->ForOPDFRead)
523
19.4k
                        && !pres->object->written) {
524
19.4k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
19.4k
                        if (ceil(page->MediaBox.x) > urx)
526
16.1k
                            urx = ceil(page->MediaBox.x);
527
19.4k
                        if (ceil(page->MediaBox.y) > ury)
528
15.8k
                            ury = ceil(page->MediaBox.y);
529
19.4k
                        pagecount++;
530
19.4k
                    }
531
251k
            }
532
15.6k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
8.40k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
7.29k
            else
535
7.29k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
536
15.6k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
15.6k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
8.40k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
7.29k
            else
540
7.29k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
541
15.6k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
15.6k
        }
543
15.6k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
15.6k
        cre_date_time[cre_date_time_len] = 0;
545
15.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
15.6k
                pdev->dname);
547
15.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
15.6k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
15.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
15.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
15.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
15.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
15.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
15.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
15.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
15.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
15.6k
        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
15.6k
        stream_puts(s, "10 dict dup begin\n");
573
15.6k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
15.6k
        if (pdev->Eps2Write) {
575
13.3k
            stream_puts(s, "/SetPageSize false def\n");
576
13.3k
            stream_puts(s, "/EPS2Write true def\n");
577
13.3k
        } else {
578
2.35k
            if (pdev->SetPageSize)
579
2.35k
                stream_puts(s, "/SetPageSize true def\n");
580
2.35k
            stream_puts(s, "/EPS2Write false def\n");
581
2.35k
        }
582
15.6k
        stream_puts(s, "end\n");
583
584
15.6k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
15.6k
        if (code < 0)
586
0
            return code;
587
15.6k
        status = s_close_filters(&s, pdev->strm);
588
15.6k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
15.6k
        stream_puts(s, "\n");
591
15.6k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
15.6k
    }
593
15.6k
    return 0;
594
15.6k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
706k
{
600
706k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
706k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
114k
        stream *s = pdev->strm;
605
114k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
114k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
114k
        if (pdev->ForOPDFRead) {
609
91.1k
            int code, status;
610
91.1k
            char BBox[256];
611
91.1k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
91.1k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
91.1k
            if (pdev->ProduceDSC)
615
91.1k
                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
91.1k
        }
660
114k
        if (!(pdev->ForOPDFRead)) {
661
23.6k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
23.6k
            if (pdev->binary_ok)
663
23.6k
                stream_puts(s, "%\307\354\217\242\n");
664
23.6k
            pdfwrite_write_args_comment(pdev, s);
665
23.6k
        }
666
114k
    }
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
706k
    if (!pdev->params.CompressPages)
677
268k
        pdev->compression = pdf_compress_none;
678
437k
    else {
679
437k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
437k
        else
682
437k
            pdev->compression = pdf_compress_Flate;
683
437k
    }
684
706k
    return 0;
685
706k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
791k
{
693
791k
    return (pdev->next_id)++;
694
791k
}
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.81M
{
706
1.81M
    stream *s = pdev->strm;
707
1.81M
    gs_offset_t pos = stell(s);
708
709
1.81M
    if (s == pdev->asides.strm)
710
361k
        pos |= ASIDES_BASE_POSITION;
711
1.81M
    return pos;
712
1.81M
}
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.1k
{
730
75.1k
    int64_t id = pdf_next_id(pdev);
731
75.1k
    gs_offset_t pos = 0;
732
733
75.1k
    if (pdev->doubleXref) {
734
75.1k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
75.1k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
75.1k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
75.1k
    return id;
740
75.1k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
716k
{
746
716k
    int64_t id = pdf_next_id(pdev);
747
716k
    gs_offset_t pos = 0;
748
749
716k
    if (pdev->doubleXref) {
750
716k
        if (pdev->strm == pdev->ObjStm.strm)
751
34.7k
            pos = pdev->ObjStm_id;
752
681k
        else
753
681k
            pos = 0;
754
716k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
716k
        if (pdev->strm == pdev->ObjStm.strm)
756
34.7k
            pos = pdev->NumObjStmObjects;
757
681k
        else
758
681k
            pos = pdf_stell(pdev);
759
716k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
716k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
716k
    return id;
766
716k
}
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
206
{
780
206
    gp_file *tfile = pdev->xref.file;
781
206
    int64_t tpos = gp_ftell(tfile);
782
206
    gs_offset_t pos = 0;
783
784
206
    if (pdev->doubleXref) {
785
206
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
206
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
206
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
206
    }
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
206
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
206
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
206
    return 0;
800
206
}
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
740k
{
806
740k
    stream *s = pdev->strm;
807
808
740k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
740k
    if (id <= 0) {
812
211k
        id = pdf_obj_ref(pdev);
813
528k
    } else {
814
528k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
528k
        gp_file *tfile = pdev->xref.file;
816
528k
        int64_t tpos = gp_ftell(tfile);
817
818
528k
        if (pdev->doubleXref) {
819
528k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
528k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
528k
            if (pdev->strm == pdev->ObjStm.strm)
823
222k
                fake_pos = pdev->ObjStm_id;
824
528k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
528k
            if (pdev->strm == pdev->ObjStm.strm)
826
222k
                pos = pdev->NumObjStmObjects;
827
528k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
528k
        } 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
528k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
528k
    }
837
740k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
242k
        switch(type) {
839
997
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
997
                break;
844
19.4k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
19.4k
                break;
847
1.86k
            case resourceColorSpace:
848
1.86k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
1.86k
                break;
850
12.2k
            case resourceExtGState:
851
12.2k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
12.2k
                break;
853
623
            case resourcePattern:
854
623
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
623
                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
24.4k
            case resourceFont:
861
                /* Ought to write the font name here */
862
24.4k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
24.4k
                break;
864
120k
            case resourceCharProc:
865
120k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
120k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
11.9k
            case resourceFontDescriptor:
871
11.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
11.9k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
1.95k
            case resourceFunction:
877
1.95k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
1.95k
                break;
879
5.67k
            case resourceEncoding:
880
5.67k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
5.67k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
15.6k
            case resourceHalftone:
886
15.6k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
15.6k
                break;
888
0
            case resourceLength:
889
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Length obj_%"PRId64")\n", id);
890
0
                break;
891
0
            case resourceSoftMaskDict:
892
                /* This should not be possible, not valid in PostScript */
893
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF SoftMask obj_%"PRId64")\n", id);
894
0
                break;
895
11
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
11
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
11
                break;
899
0
            case resourceStream:
900
                /* Possibly we should not add comments to this type */
901
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF stream obj_%"PRId64")\n", id);
902
0
                break;
903
0
            case resourceOutline:
904
                /* This should not be possible, not valid in PostScript */
905
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Outline obj_%"PRId64")\n", id);
906
0
                break;
907
0
            case resourceArticle:
908
                /* This should not be possible, not valid in PostScript */
909
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Article obj_%"PRId64")\n", id);
910
0
                break;
911
0
            case resourceDests:
912
                /* This should not be possible, not valid in PostScript */
913
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Dests obj_%"PRId64")\n", id);
914
0
                break;
915
0
            case resourceEmbeddedFiles:
916
                /* This should not be possible, not valid in PostScript */
917
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF EmbeddedFiles obj_%"PRId64")\n", id);
918
0
                break;
919
0
            case resourceLabels:
920
                /* This should not be possible, not valid in PostScript */
921
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Page Labels obj_%"PRId64")\n", id);
922
0
                break;
923
0
            case resourceThread:
924
                /* This should not be possible, not valid in PostScript */
925
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Thread obj_%"PRId64")\n", id);
926
0
                break;
927
0
            case resourceCatalog:
928
                /* This should not be possible, not valid in PostScript */
929
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Catalog obj_%"PRId64")\n", id);
930
0
                break;
931
0
            case resourceEncrypt:
932
                /* This should not be possible, not valid in PostScript */
933
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Encryption obj_%"PRId64")\n", id);
934
0
                break;
935
0
            case resourcePagesTree:
936
                /* This should not be possible, not valid in PostScript */
937
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%"PRId64")\n", id);
938
0
                break;
939
0
            case resourceMetadata:
940
                /* This should not be possible, not valid in PostScript */
941
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Metadata obj_%"PRId64")\n", id);
942
0
                break;
943
0
            case resourceICC:
944
                /* This should not be possible, not valid in PostScript */
945
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%"PRId64")\n", id);
946
0
                break;
947
0
            case resourceAnnotation:
948
                /* This should not be possible, not valid in PostScript */
949
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Annotation obj_%"PRId64")\n", id);
950
0
                break;
951
11.9k
            case resourceFontFile:
952
11.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
11.9k
                break;
954
15.7k
            default:
955
15.7k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
15.7k
                break;
957
242k
        }
958
242k
    }
959
740k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
487k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
740k
    return id;
962
740k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
31.8k
{
966
31.8k
    return pdf_open_obj(pdev, 0L, type);
967
31.8k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
740k
{
973
740k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
487k
        stream_puts(pdev->strm, "endobj\n");
975
740k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
242k
        switch(type) {
977
19.4k
            case resourcePage:
978
19.4k
                break;
979
223k
            default:
980
223k
            stream_puts(pdev->strm, "%%EndResource\n");
981
223k
            break;
982
242k
        }
983
242k
    }
984
740k
    return 0;
985
740k
}
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
173k
{
1034
173k
    gs_memory_t *mem = pdev->v_memory;
1035
173k
    stream_arcfour_state *ss;
1036
173k
    gs_md5_byte_t key[16];
1037
173k
    int code, keylength;
1038
1039
173k
    if (!pdev->KeyLength)
1040
173k
        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
51.2k
{
1065
51.2k
    stream *s;
1066
51.2k
    int code;
1067
1068
51.2k
    if (pdev->contents_id != 0)
1069
7
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
51.2k
    pdev->compression_at_page_start = pdev->compression;
1071
51.2k
    if (pdev->ResourcesBeforeUsage) {
1072
19.4k
        pdf_resource_t *pres;
1073
1074
19.4k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
19.4k
                    true, pdev->params.CompressPages);
1076
19.4k
        if (code < 0)
1077
0
            return code;
1078
19.4k
        pdev->contents_id = pres->object->id;
1079
19.4k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
19.4k
        pdev->contents_pos = -1; /* inapplicable */
1081
19.4k
        s = pdev->strm;
1082
31.8k
    } else {
1083
31.8k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
31.8k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
31.8k
        s = pdev->strm;
1086
31.8k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
31.8k
        if (pdev->compression == pdf_compress_Flate) {
1088
31.8k
            if (pdev->binary_ok)
1089
31.8k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
31.8k
        }
1093
31.8k
        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
31.8k
        stream_puts(s, ">>\nstream\n");
1100
31.8k
        pdev->contents_pos = pdf_stell(pdev);
1101
31.8k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
31.8k
        if (code < 0)
1103
0
            return code;
1104
31.8k
        pdev->strm = s;
1105
31.8k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
31.8k
            const stream_template *templat;
1107
31.8k
            stream *es;
1108
31.8k
            byte *buf;
1109
31.8k
            Flate_filter_state *st;
1110
1111
31.8k
            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
31.8k
            templat = &Flate_filter_template;
1131
31.8k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
31.8k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
31.8k
                                       "PDF compression buffer");
1134
31.8k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
31.8k
                                 templat->stype, "PDF compression state");
1136
31.8k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
31.8k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
31.8k
                       s_mode_write);
1140
31.8k
            st->memory = pdev->pdf_memory;
1141
31.8k
            st->templat = templat;
1142
31.8k
            es->state = (stream_state *) st;
1143
31.8k
            es->procs.process = templat->process;
1144
31.8k
            es->strm = s;
1145
31.8k
            (*templat->set_defaults) ((stream_state *) st);
1146
31.8k
            code = (*templat->init) ((stream_state *) st);
1147
31.8k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
31.8k
            pdev->strm = s = es;
1152
31.8k
        }
1153
31.8k
        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
31.8k
    }
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
51.2k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
51.2k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
51.2k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
31.8k
        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
31.8k
    }
1218
51.2k
    pdev->AR4_save_bug = false;
1219
51.2k
    return PDF_IN_STREAM;
1220
51.2k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
303k
{
1225
303k
    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
303k
    if (!pdev->clipped_text_pending) {
1233
302k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
302k
        if (code < 0)
1235
0
            return 0;
1236
302k
    }
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
303k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
303k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
303k
    pdev->procsets |= Text;
1248
303k
    code = pdf_from_stream_to_text(pdev);
1249
303k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
303k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
303k
{
1255
303k
    int code = pdf_from_string_to_text(pdev);
1256
1257
303k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
303k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
303k
{
1263
303k
    int code;
1264
1265
303k
    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
303k
    if (pdev->clipped_text_pending)
1273
196
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
196
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
302k
    else {
1276
302k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
302k
        if (code < 0)
1278
0
            return code;
1279
302k
        pdf_reset_text(pdev); /* because of Q */
1280
302k
    }
1281
303k
    return PDF_IN_STREAM;
1282
303k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
51.2k
{
1287
51.2k
    stream *s = pdev->strm;
1288
51.2k
    gs_offset_t length;
1289
51.2k
    int code;
1290
51.2k
    stream *target;
1291
51.2k
     char str[21];
1292
1293
51.2k
    if (pdev->ResourcesBeforeUsage) {
1294
19.4k
        int code = pdf_exit_substream(pdev);
1295
1296
19.4k
        if (code < 0)
1297
0
            return code;
1298
31.8k
    } else {
1299
31.8k
        if (pdev->vgstack_depth) {
1300
226
            code = pdf_restore_viewer_state(pdev, s);
1301
226
            if (code < 0)
1302
14
                return code;
1303
226
        }
1304
31.7k
        target = pdev->strm;
1305
1306
31.7k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
31.7k
            target = target->strm;
1308
31.7k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
31.7k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
31.7k
        s_close_filters(&pdev->strm, target);
1313
1314
31.7k
        s = pdev->strm;
1315
31.7k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
31.7k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
31.7k
        stream_puts(s, "endstream\n");
1319
31.7k
        pdf_end_obj(pdev, resourceStream);
1320
1321
31.7k
        if (pdev->WriteObjStms) {
1322
31.7k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
31.7k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
31.7k
            stream_puts(pdev->strm, str);
1325
31.7k
            pdf_end_separate(pdev, resourceLength);
1326
31.7k
        } else {
1327
11
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
11
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
11
            stream_puts(s, str);
1330
11
            pdf_end_obj(pdev, resourceLength);
1331
11
        }
1332
31.7k
    }
1333
51.2k
    return PDF_IN_NONE;
1334
51.2k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
22.0M
{
1340
22.0M
    int (*proc) (gx_device_pdf *);
1341
1342
23.0M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
1.01M
        int code = (*proc) (pdev);
1344
1345
1.01M
        if (code < 0)
1346
21
            return code;
1347
1.01M
        pdev->context = (pdf_context_t) code;
1348
1.01M
    }
1349
22.0M
    pdev->context = context;
1350
22.0M
    return 0;
1351
22.0M
}
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
51.2k
{
1357
51.2k
    if (pdev->context == PDF_IN_NONE)
1358
7
        return 0;
1359
51.2k
    if (last) {     /* Exit from the clipping path gsave. */
1360
51.2k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
51.2k
        if (code < 0)
1363
0
            return code;
1364
51.2k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
51.2k
        pdf_close_text_contents(pdev);
1366
51.2k
    }
1367
51.2k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
51.2k
}
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
255k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
255k
    pres->where_used = 0;
1386
255k
    if (pres->object) {
1387
255k
        pres->object->written = true;
1388
255k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
145k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
145k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
145k
            if (code < 0)
1393
0
                return code;
1394
145k
        }
1395
255k
        cos_release(pres->object, "pdf_cancel_resource");
1396
255k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
255k
        pres->object = 0;
1398
255k
    }
1399
255k
    return 0;
1400
255k
}
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
254k
{   /* fixme : optimize. */
1406
254k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
254k
    pdf_resource_t *pres;
1408
254k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
254k
    int i;
1410
1411
    /* since we're about to free the resource, we can just set
1412
       any of these references to null
1413
    */
1414
3.05M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
2.79M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
2.79M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
2.79M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
6
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
6
        }
1424
2.79M
    }
1425
1426
258k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
258k
        if (pres == pres1) {
1428
254k
            *pprev = pres->prev;
1429
254k
            break;
1430
254k
        }
1431
1432
254k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
254k
        pprev = pchain + i;
1434
258k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
258k
            if (pres == pres1) {
1436
254k
                *pprev = pres->next;
1437
254k
                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
254k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
254k
                return;
1444
254k
            }
1445
254k
    }
1446
254k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
75.5k
{
1451
75.5k
    return 1;
1452
75.5k
}
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
177k
{
1467
177k
    pdf_resource_t *pres1 = *ppres;
1468
177k
    int code;
1469
1470
177k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
177k
    if (code < 0)
1472
0
        return code;
1473
177k
    if (code != 0) {
1474
120k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
120k
        if (code < 0)
1476
0
            return code;
1477
120k
        pdf_forget_resource(pdev, pres1, rtype);
1478
120k
        return 0;
1479
120k
    } else {
1480
57.0k
        if (pres1->object->id < 0)
1481
57.0k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
57.0k
        if (write) {
1483
26.5k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
26.5k
            if (code < 0)
1485
0
                return code;
1486
26.5k
            pres1->object->written = 1;
1487
26.5k
        }
1488
57.0k
        return 1;
1489
57.0k
    }
1490
177k
}
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.75M
{
1497
1.75M
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
1.75M
    pdf_resource_t **pprev = pchain;
1499
1.75M
    pdf_resource_t *pres;
1500
1501
4.84M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
4.67M
        if (pres->rid == rid) {
1503
1.58M
            if (pprev != pchain) {
1504
576k
                *pprev = pres->next;
1505
576k
                pres->next = *pchain;
1506
576k
                *pchain = pres;
1507
576k
            }
1508
1.58M
            return pres;
1509
1.58M
        }
1510
173k
    return 0;
1511
1.75M
}
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
59.7k
{
1517
59.7k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
59.7k
    pdf_resource_t *pres;
1519
59.7k
    int i;
1520
1521
900k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
6.49M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
5.65M
            if (pres->object && pres->object->id == id)
1524
13.7k
                return pres;
1525
5.65M
        }
1526
854k
    }
1527
45.9k
    return 0;
1528
59.7k
}
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
209k
{
1535
209k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
209k
    pdf_resource_t *pres;
1537
209k
    cos_object_t *pco0 = (*ppres)->object;
1538
209k
    int i;
1539
1540
1.61M
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
6.22M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
4.81M
            if (*ppres != pres) {
1543
4.60M
                int code;
1544
4.60M
                cos_object_t *pco1 = pres->object;
1545
1546
4.60M
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
68.0k
                    continue;      /* don't compare different types */
1548
4.53M
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
4.53M
                if (code < 0)
1550
0
                    return code;
1551
4.53M
                if (code > 0) {
1552
122k
                    code = eq(pdev, *ppres, pres);
1553
122k
                    if (code < 0)
1554
0
                        return code;
1555
122k
                    if (code > 0) {
1556
122k
                        *ppres = pres;
1557
122k
                        return 1;
1558
122k
                    }
1559
122k
                }
1560
4.53M
            }
1561
4.81M
        }
1562
1.52M
    }
1563
87.6k
    return 0;
1564
209k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
16.2k
{
1569
16.2k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
16.2k
    pdf_resource_t *pres;
1571
16.2k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
16.2k
    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
194k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
178k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
178k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
178k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
178k
    }
1588
1589
19.7k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
19.7k
        if (pres == pres1) {
1591
16.2k
            *pprev = pres->prev;
1592
16.2k
            break;
1593
16.2k
        }
1594
1595
16.2k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
16.2k
        pprev = pchain + i;
1597
16.6k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
16.6k
            if (pres == pres1) {
1599
16.2k
                *pprev = pres->next;
1600
#if 0
1601
                if (pres->object) {
1602
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1603
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1604
                    pres->object = 0;
1605
                }
1606
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1607
#endif
1608
16.2k
                return;
1609
16.2k
            }
1610
16.2k
    }
1611
16.2k
}
1612
1613
/* Drop resources by a condition. */
1614
void
1615
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1616
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1617
0
{
1618
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1619
0
    pdf_resource_t **pprev;
1620
0
    pdf_resource_t *pres;
1621
0
    int i;
1622
1623
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1624
0
        pprev = pchain + i;
1625
0
        for (; (pres = *pprev) != 0; ) {
1626
0
            if (cond(pdev, pres)) {
1627
0
                *pprev = pres->next;
1628
0
                pres->next = pres; /* A temporary mark - see below */
1629
0
            } else
1630
0
                pprev = &pres->next;
1631
0
        }
1632
0
    }
1633
0
    pprev = &pdev->last_resource;
1634
0
    for (; (pres = *pprev) != 0; )
1635
0
        if (pres->next == pres) {
1636
0
            *pprev = pres->prev;
1637
0
            if (pres->object) {
1638
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1639
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1640
0
                pres->object = 0;
1641
0
            }
1642
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1643
0
        } else
1644
0
            pprev = &pres->prev;
1645
0
}
1646
1647
/* Print resource statistics. */
1648
void
1649
pdf_print_resource_statistics(gx_device_pdf * pdev)
1650
0
{
1651
1652
0
    int rtype;
1653
1654
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1655
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1656
0
        pdf_resource_t *pres;
1657
0
        const char *name = pdf_resource_type_names[rtype];
1658
0
        int i, n = 0;
1659
1660
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1661
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1662
0
        }
1663
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1664
0
                (name ? name : ""), n);
1665
0
    }
1666
0
}
1667
1668
int FlushObjStm(gx_device_pdf *pdev)
1669
23.8k
{
1670
23.8k
    int code = 0, i, len = 0, id = 0, end;
1671
23.8k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
23.8k
    pdf_resource_t *pres;
1673
23.8k
    int options = DATA_STREAM_BINARY;
1674
1675
23.8k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
23.8k
    pdev->WriteObjStms = false;
1679
1680
23.8k
    sflush(pdev->strm);
1681
23.8k
    sflush(pdev->ObjStm.strm);
1682
23.8k
    end = stell(pdev->ObjStm.strm);
1683
1684
23.8k
    if (pdev->CompressStreams)
1685
23.8k
        options |= DATA_STREAM_COMPRESS;
1686
1687
23.8k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
23.8k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
23.8k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
23.8k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
23.8k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
23.8k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
23.8k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
23.8k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
276k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
252k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
252k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
252k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
252k
        strcat(offsets, offset);
1713
252k
    }
1714
1715
23.8k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
23.8k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
23.8k
    stream_puts(pdev->strm, offsets);
1723
1724
23.8k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
23.8k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
23.8k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
23.8k
    code = pdf_close_aside(pdev);
1732
23.8k
    if (code < 0)
1733
0
        return code;
1734
23.8k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
23.8k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
23.8k
    pdev->WriteObjStms = true;
1740
23.8k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
23.8k
    if (pdev->ObjStmOffsets != NULL) {
1742
23.8k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
23.8k
        pdev->ObjStmOffsets = NULL;
1744
23.8k
    }
1745
23.8k
    pdev->NumObjStmObjects = 0;
1746
23.8k
    pdev->ObjStm_id = 0;
1747
1748
23.8k
    pdev->WriteObjStms = true;
1749
23.8k
    return code;
1750
23.8k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
23.8k
{
1754
23.8k
    int code;
1755
1756
23.8k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
23.8k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
23.8k
    if (code < 0)
1760
0
        return code;
1761
1762
23.8k
    pdev->NumObjStmObjects = 0;
1763
23.8k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
23.8k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
23.8k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
23.8k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
23.8k
    return code;
1772
23.8k
}
1773
1774
/* Begin an object logically separate from the contents. */
1775
int64_t
1776
pdf_open_separate_noObjStm(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1777
137k
{
1778
137k
    int code;
1779
1780
137k
    code = pdfwrite_pdf_open_document(pdev);
1781
137k
    if (code < 0)
1782
0
        return code;
1783
137k
    pdev->asides.save_strm = pdev->strm;
1784
137k
    pdev->strm = pdev->asides.strm;
1785
137k
    code = pdf_open_obj(pdev, id, type);
1786
137k
    return code;
1787
137k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
532k
{
1791
532k
    if (type == resourceStream)
1792
0
        return true;
1793
532k
    if (type == resourceCharProc)
1794
928
        return true;
1795
531k
    if (type == resourcePattern)
1796
21.7k
        return true;
1797
510k
    if (type == resourceXObject)
1798
5.35k
        return true;
1799
504k
    return false;
1800
510k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
471k
{
1805
471k
    int code;
1806
1807
471k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
219k
        code = pdfwrite_pdf_open_document(pdev);
1809
219k
        if (code < 0)
1810
0
            return code;
1811
219k
        pdev->asides.save_strm = pdev->strm;
1812
219k
        pdev->strm = pdev->asides.strm;
1813
219k
        code = pdf_open_obj(pdev, id, type);
1814
252k
    } else {
1815
252k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
233
            code = FlushObjStm(pdev);
1817
233
            if (code < 0)
1818
0
                return code;
1819
233
        }
1820
252k
        if (!pdev->ObjStm.strm) {
1821
23.8k
            code = NewObjStm(pdev);
1822
23.8k
            if (code < 0)
1823
0
                return code;
1824
23.8k
        }
1825
252k
        pdev->ObjStm.save_strm = pdev->strm;
1826
252k
        pdev->strm = pdev->ObjStm.strm;
1827
252k
        code = pdf_open_obj(pdev, id, type);
1828
252k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
252k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
252k
    }
1831
471k
    return code;
1832
471k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
177k
{
1836
177k
    return pdf_open_separate(pdev, 0L, type);
1837
177k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
426k
{
1842
426k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
426k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
426k
}
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
700k
{
1852
700k
    pdf_resource_t *pres;
1853
700k
    cos_object_t *object;
1854
1855
700k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
700k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
700k
                           "pdf_alloc_aside(resource)");
1859
700k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
700k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
700k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
700k
    memset(pres, 0, pst->ssize);
1865
700k
    pres->object = object;
1866
700k
    if (id < 0) {
1867
465k
        object->id = -1L;
1868
465k
        pres->rname[0] = 0;
1869
465k
    } else
1870
235k
        pdf_reserve_object_id(pdev, pres, id);
1871
700k
    pres->next = *plist;
1872
700k
    pres->rid = 0;
1873
700k
    *plist = pres;
1874
700k
    pres->prev = pdev->last_resource;
1875
700k
    pdev->last_resource = pres;
1876
700k
    pres->named = false;
1877
700k
    pres->global = false;
1878
700k
    pres->where_used = pdev->used_mask;
1879
700k
    *ppres = pres;
1880
700k
    return 0;
1881
700k
}
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
118k
{
1887
118k
    int64_t id = pdf_begin_separate(pdev, type);
1888
118k
    int code = 0;
1889
1890
118k
    if (id < 0)
1891
0
        return (int)id;
1892
118k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
118k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
118k
    return code;
1897
118k
}
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
118k
{
1904
118k
    int code;
1905
1906
118k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
118k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
118k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
118k
    if (code >= 0)
1913
118k
        (*ppres)->rid = rid;
1914
118k
    return code;
1915
118k
}
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
306k
{
1952
306k
    int code;
1953
1954
306k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
306k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
306k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
306k
    if (code >= 0)
1961
306k
        (*ppres)->rid = rid;
1962
306k
    return code;
1963
306k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
3.00M
{
1969
3.00M
    return pres->object->id;
1970
3.00M
}
1971
1972
/* End an aside or other separate object. */
1973
int
1974
pdf_end_separate_noObjStm(gx_device_pdf * pdev, pdf_resource_type_t type)
1975
137k
{
1976
137k
    int code = pdf_end_obj(pdev, type);
1977
1978
137k
    pdev->strm = pdev->asides.save_strm;
1979
137k
    pdev->asides.save_strm = 0;
1980
137k
    return code;
1981
137k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
471k
{
1985
471k
    int code = pdf_end_obj(pdev, type);
1986
1987
471k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
219k
        pdev->strm = pdev->asides.save_strm;
1989
219k
        pdev->asides.save_strm = 0;
1990
252k
    } else {
1991
252k
        pdev->strm = pdev->ObjStm.save_strm;
1992
252k
        pdev->ObjStm.save_strm = 0;
1993
252k
        pdev->NumObjStmObjects++;
1994
252k
    }
1995
471k
    return code;
1996
471k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
638
{
2000
638
    return pdf_end_separate(pdev, type);
2001
638
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
638
{
2007
638
    return pdf_end_aside(pdev, type);
2008
638
}
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
528k
{
2018
528k
    int j, code = 0;
2019
2020
8.97M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
8.45M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
8.72M
        for (; pres != 0; pres = pres->next)
2024
277k
            if ((!pres->named || pdev->ForOPDFRead)
2025
277k
                && pres->object && !pres->object->written) {
2026
47.5k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
47.5k
            }
2028
8.45M
    }
2029
528k
    return code;
2030
528k
}
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
15.6k
{
2040
15.6k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
15.6k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
15.6k
    if (pres == NULL)
2044
0
        return;
2045
15.6k
    pres1 = pres->next;
2046
19.4k
    for (;;) {
2047
19.4k
        if (pres1 == NULL)
2048
15.6k
            break;
2049
3.78k
        pres2 = pres1->next;
2050
3.78k
        pres1->next = pres;
2051
3.78k
        pres = pres1;
2052
3.78k
        pres1 = pres2;
2053
3.78k
    }
2054
15.6k
    pres0->next = NULL;
2055
15.6k
    pdev->resources[rtype].chains[0] = pres;
2056
15.6k
}
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
275k
{
2065
275k
    int j;
2066
2067
4.68M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
4.40M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
4.40M
        pdf_resource_t *pres;
2070
2071
4.56M
        while ((pres = *prev) != 0) {
2072
159k
            if (pres->named) { /* named, don't free */
2073
6
                prev = &pres->next;
2074
159k
            } else {
2075
159k
                if (pres->object) {
2076
159k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
159k
                    pres->object = 0;
2078
159k
                }
2079
159k
                *prev = pres->next;
2080
159k
            }
2081
159k
        }
2082
4.40M
    }
2083
275k
    return 0;
2084
275k
}
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
51.2k
{
2093
51.2k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
461k
    for (i = 0; i <= resourceFont; ++i) {
2098
410k
        stream *s = 0;
2099
410k
        int j;
2100
2101
410k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
51.2k
            continue;
2103
359k
        page->resource_ids[i] = 0;
2104
6.10M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
5.74M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
6.02M
            for (; pres != 0; pres = pres->next) {
2108
282k
                if (pres->where_used & pdev->used_mask) {
2109
172k
                    int64_t id = pdf_resource_id(pres);
2110
2111
172k
                    if (id == -1L)
2112
14.3k
                        continue;
2113
157k
                    if (s == 0) {
2114
41.1k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
41.1k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
41.1k
                        s = pdev->strm;
2117
41.1k
                        stream_puts(s, "<<");
2118
41.1k
                    }
2119
157k
                    pprints1(s, "/%s\n", pres->rname);
2120
157k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
157k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
157k
                    if (clear_usage)
2123
157k
                        pres->where_used -= pdev->used_mask;
2124
157k
                }
2125
282k
            }
2126
5.74M
        }
2127
359k
        if (s) {
2128
41.1k
            stream_puts(s, ">>\n");
2129
41.1k
            pdf_end_separate(pdev, i);
2130
41.1k
        }
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
359k
        if (i != resourceFont && i != resourceProperties)
2137
256k
            pdf_write_resource_objects(pdev, i);
2138
359k
    }
2139
51.2k
    page->procsets = pdev->procsets;
2140
51.2k
    return 0;
2141
51.2k
}
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
303k
{
2147
303k
    gs_offset_t r, left = count;
2148
303k
    byte buf[sbuf_size];
2149
2150
5.35M
    while (left > 0) {
2151
5.04M
        uint copy = min(left, sbuf_size);
2152
2153
5.04M
        r = gp_fread(buf, 1, copy, file);
2154
5.04M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
5.04M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
5.04M
        stream_write(s, buf, copy);
2160
5.04M
        left -= copy;
2161
5.04M
    }
2162
303k
    return 0;
2163
303k
}
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
158k
{
2170
158k
    int64_t r, left = count;
2171
2172
2.75M
    while (left > 0) {
2173
2.59M
        byte buf[sbuf_size];
2174
2.59M
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
2.59M
        int64_t end_pos = gp_ftell(file);
2176
2177
2.59M
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
2.59M
        r = gp_fread(buf, 1, copy, file);
2181
2.59M
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
2.59M
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
2.59M
        stream_write(s, buf, copy);
2188
2.59M
        sflush(s);
2189
2.59M
        left -= copy;
2190
2.59M
    }
2191
158k
    return 0;
2192
158k
}
2193
2194
/* ------ Pages ------ */
2195
2196
/* Get or assign the ID for a page. */
2197
/* Returns 0 if the page number is out of range. */
2198
int64_t
2199
pdf_page_id(gx_device_pdf * pdev, int page_num)
2200
234k
{
2201
234k
    cos_dict_t *Page;
2202
2203
234k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
234k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
23
        uint new_num_pages;
2207
23
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
23
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
23
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
23
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
23
                             "pdf_page_id(resize pages)");
2216
2217
23
        if (new_pages == 0)
2218
0
            return 0;
2219
23
        memset(&new_pages[pdev->num_pages], 0,
2220
23
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
23
        pdev->pages = new_pages;
2222
23
        pdev->num_pages = new_num_pages;
2223
23
    }
2224
234k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
51.2k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
51.2k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
51.2k
        Page->id = pdf_obj_forward_ref(pdev);
2230
51.2k
    }
2231
234k
    return Page->id;
2232
234k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
6.67M
{
2238
6.67M
    return &pdev->pages[pdev->next_page];
2239
6.67M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
16.0k
{
2245
16.0k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
16.0k
    return pdev->pages[pdev->next_page].Page;
2248
16.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
21.4M
{
2267
21.4M
    if (!is_in_page(pdev)) {
2268
58.6k
        int code;
2269
2270
58.6k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
58.6k
        code = pdfwrite_pdf_open_document(pdev);
2273
58.6k
        if (code < 0)
2274
0
            return code;
2275
58.6k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
21.4M
    return pdf_open_contents(pdev, context);
2278
21.4M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
202k
{
2284
202k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
202k
    if (pdev->sbstack_depth <= bottom) {
2289
177k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
177k
        if (code < 0)
2292
0
            return code;
2293
177k
    }
2294
202k
    if (pdev->context > PDF_IN_STREAM) {
2295
4
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2296
2297
4
        if (code < 0)
2298
0
            return code;
2299
4
    }
2300
202k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
116k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
116k
        if (code < 0)
2304
0
            return code;
2305
116k
        code = pdf_remember_clip_path(pdev, NULL);
2306
116k
        if (code < 0)
2307
0
            return code;
2308
116k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
116k
    }
2310
202k
    return 0;
2311
202k
}
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
39.3k
{
2323
39.3k
    int major = (int)(gs_revision / 1000);
2324
39.3k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
39.3k
    int patch = gs_revision % 10;
2326
2327
39.3k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
39.3k
}
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
168k
{
2335
168k
    stream *s = pdev->strm;
2336
2337
168k
    if (before)
2338
166k
        stream_puts(s, before);
2339
168k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
168k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
168k
    if (after)
2342
168k
        stream_puts(s, after);
2343
168k
}
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.41M
{
2354
4.41M
    uint i;
2355
2356
23.7M
    for (i = 0; i < size; ++i) {
2357
19.3M
        uint c = nstr[i];
2358
19.3M
        char hex[4];
2359
2360
19.3M
        switch (c) {
2361
19.3M
            default:
2362
19.3M
                if (c >= 0x21 && c <= 0x7e) {
2363
19.3M
                    stream_putc(s, (byte)c);
2364
19.3M
                    break;
2365
19.3M
                }
2366
                /* falls through */
2367
6.51k
            case '#':
2368
6.54k
            case '%':
2369
6.56k
            case '(': case ')':
2370
6.56k
            case '<': case '>':
2371
6.58k
            case '[': case ']':
2372
6.60k
            case '{': case '}':
2373
6.62k
            case '/':
2374
6.62k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
6.62k
                stream_puts(s, hex);
2376
6.62k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
19.3M
        }
2380
19.3M
    }
2381
4.41M
    return 0;
2382
4.41M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
4.41M
{
2386
4.41M
    return &pdf_put_name_chars_1_2;
2387
4.41M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
4.40M
{
2391
4.40M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
4.40M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
4.40M
{
2396
4.40M
    stream_putc(pdev->strm, '/');
2397
4.40M
    return pdf_put_name_chars(pdev, nstr, size);
2398
4.40M
}
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
229k
{
2452
229k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2453
229k
        stream_write(pdev->strm, str, size);
2454
229k
        return 0;
2455
229k
    } else
2456
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2457
229k
}
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
47.5k
{
2462
47.5k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
47.5k
        int i, oct, width = 0;
2464
47.5k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
47.5k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
47.5k
            stream_write(pdev->strm, "\n", 1);
2468
47.5k
        stream_write(pdev->strm, "<", 1);
2469
47.5k
        width++;
2470
1.55M
        for (i = 1; i < size - 1; i++) {
2471
1.51M
            if (str[i] == '\\') {
2472
161k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2473
160k
                    oct = (str[i+1] - 0x30) * 64;
2474
160k
                    oct += (str[i+2] - 0x30) *8;
2475
160k
                    oct += str[i+3] - 0x30;
2476
160k
                    i+=3;
2477
160k
                } else {
2478
892
                    switch (str[++i]) {
2479
5
                        case 'b' :
2480
5
                            oct = 8;
2481
5
                            break;
2482
735
                        case 't' :
2483
735
                            oct = 9;
2484
735
                            break;
2485
39
                        case 'n' :
2486
39
                            oct = 10;
2487
39
                            break;
2488
32
                        case 'f' :
2489
32
                            oct = 12;
2490
32
                            break;
2491
12
                        case 'r' :
2492
12
                            oct = 13;
2493
12
                            break;
2494
69
                        default:
2495
69
                            oct = str[i];
2496
69
                            break;
2497
892
                    }
2498
892
                }
2499
161k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2500
1.22k
                    stream_write(pdev->strm, "\n", 1);
2501
1.22k
                    width = 0;
2502
1.22k
                }
2503
161k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2504
161k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2505
161k
                width += 2;
2506
1.35M
            } else {
2507
1.35M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
259
                    stream_write(pdev->strm, "\n", 1);
2509
259
                    width = 0;
2510
259
                }
2511
1.35M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
1.35M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
1.35M
                width += 2;
2514
1.35M
            }
2515
1.51M
        }
2516
47.5k
        stream_write(pdev->strm, ">", 1);
2517
47.5k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
47.5k
            stream_write(pdev->strm, "\n", 1);
2519
47.5k
        return 0;
2520
47.5k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
47.5k
}
2523
2524
/* Write an encoded hexadecimal string with possible encryption. */
2525
static int
2526
pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2527
0
{
2528
0
    emprintf(pdev->memory,
2529
0
             "Unimplemented function : pdf_put_encoded_hex_string\n");
2530
0
    stream_write(pdev->strm, str, size);
2531
0
    return_error(gs_error_unregistered);
2532
0
}
2533
/*  Scan an item in a serialized array or dictionary.
2534
    This is a very simplified Postscript lexical scanner.
2535
    It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
2536
    We only need to select strings and encrypt them.
2537
    Other items are passed identically.
2538
    Note we don't reconstruct the nesting of arrays|dictionaries.
2539
*/
2540
static int
2541
pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
2542
0
{
2543
0
    const byte *q = p;
2544
0
    int n = l;
2545
2546
0
    if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2547
0
        return (l > 0 ? 1 : 0);
2548
0
    for (q++, n--; n; q++, n--) {
2549
0
        if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2550
0
            return q - p;
2551
0
        if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
2552
0
            return q - p;
2553
        /* Note : immediate names are not allowed in PDF. */
2554
0
    }
2555
0
    return l;
2556
0
}
2557
2558
/* Write a serialized array or dictionary with possible encryption. */
2559
static int
2560
pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2561
160k
{
2562
160k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
160k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
500
            stream_putc(pdev->strm, (byte)'\n');
2565
500
            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
500
            } else {
2606
500
                stream_write(pdev->strm, vstr, size);
2607
500
            }
2608
159k
        } else {
2609
159k
            stream_write(pdev->strm, vstr, size);
2610
159k
        }
2611
160k
    } else {
2612
0
        const byte *p = vstr;
2613
0
        int l = size, n;
2614
2615
0
        for (;l > 0 ;) {
2616
0
            if (*p == '(')
2617
0
                n = pdf_encrypt_encoded_string(pdev, p, l, object_id);
2618
0
            else {
2619
0
                n = pdf_scan_item(pdev, p, l, object_id);
2620
0
                stream_write(pdev->strm, p, n);
2621
0
            }
2622
0
            l -= n;
2623
0
            p += n;
2624
0
        }
2625
0
    }
2626
160k
    return 0;
2627
160k
}
2628
2629
/*
2630
 * Write a string in its shortest form ( () or <> ).  Note that
2631
 * this form is different depending on whether binary data are allowed.
2632
 * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
2633
 */
2634
int
2635
pdf_put_string(const gx_device_pdf * pdev, const byte * str, uint size)
2636
12.0M
{
2637
12.0M
    psdf_write_string(pdev->strm, str, size,
2638
12.0M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
12.0M
    return 0;
2640
12.0M
}
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.49M
{
2646
6.49M
    if (size > 0 && vstr[0] == '/')
2647
3.72M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
2.77M
    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.77M
    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.77M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
133k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
2.63M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
27.0k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
2.61M
    else if (size > 1 && vstr[0] == '(') {
2657
277k
        if (pdev->ForOPDFRead)
2658
47.5k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2659
229k
        else
2660
229k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2661
277k
    }
2662
2.33M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
2.33M
    stream_write(pdev->strm, vstr, size);
2665
2.33M
    return 0;
2666
6.49M
}
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
529k
{
2674
529k
    const char *filter_name = 0;
2675
529k
    bool binary_ok = true;
2676
529k
    stream *fs = s;
2677
529k
    cos_dict_t *decode_parms = 0;
2678
529k
    int code;
2679
2680
1.94M
    for (; fs != 0; fs = fs->strm) {
2681
1.41M
        const stream_state *st = fs->state;
2682
1.41M
        const stream_template *templat = st->templat;
2683
2684
1.41M
#define TEMPLATE_IS(atemp)\
2685
7.97M
  (templat->process == (atemp).process)
2686
1.41M
        if (TEMPLATE_IS(s_A85E_template))
2687
222k
            binary_ok = false;
2688
1.19M
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
118k
            cos_param_list_writer_t writer;
2690
118k
            stream_CF_state cfs;
2691
2692
118k
            decode_parms =
2693
118k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
118k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
118k
            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
118k
            cfs = *(const stream_CF_state *)st;
2702
118k
            if (cfs.EndOfBlock)
2703
876
                cfs.Rows = 0;
2704
118k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
118k
            filter_name = pfn->CCITTFaxDecode;
2706
1.07M
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
12.6k
            filter_name = pfn->DCTDecode;
2708
1.06M
        else if (TEMPLATE_IS(s_zlibE_template))
2709
207k
            filter_name = pfn->FlateDecode;
2710
856k
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
856k
        else if (TEMPLATE_IS(s_LZWE_template))
2713
92.0k
            filter_name = pfn->LZWDecode;
2714
764k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
22.2k
            const stream_PNGP_state *const ss =
2717
22.2k
                (const stream_PNGP_state *)st;
2718
2719
22.2k
            decode_parms =
2720
22.2k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
22.2k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
22.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
22.2k
                                         ss->Predictor));
2725
22.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
22.2k
                                         ss->Columns));
2727
22.2k
            if (ss->Colors != 1)
2728
22.2k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
22.2k
                                             ss->Colors));
2730
22.2k
            if (ss->BitsPerComponent != 8)
2731
22.2k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
22.2k
                                             "/BitsPerComponent",
2733
22.2k
                                             ss->BitsPerComponent));
2734
742k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
1.41M
#undef TEMPLATE_IS
2737
1.41M
    }
2738
529k
    if (filter_name) {
2739
430k
        if (binary_ok) {
2740
231k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
231k
            if (decode_parms)
2742
231k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
231k
                                                COS_OBJECT(decode_parms)));
2744
231k
        } else {
2745
199k
            cos_array_t *pca =
2746
199k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
199k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
199k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
199k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
199k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
199k
                                            COS_OBJECT(pca)));
2754
199k
            if (decode_parms) {
2755
117k
                pca = cos_array_alloc(pdev,
2756
117k
                                      "pdf_put_image_filters(DecodeParms)");
2757
117k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
117k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
117k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
117k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
117k
                                                COS_OBJECT(pca)));
2763
117k
            }
2764
199k
        }
2765
430k
    } else if (!binary_ok)
2766
98.5k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
529k
    return 0;
2768
529k
}
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
261k
{
2774
261k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
183k
                    &s_LZWE_template : &s_zlibE_template);
2776
261k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
261k
                                     "pdf_write_function");
2778
2779
261k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
261k
    if (templat->set_defaults)
2782
261k
        templat->set_defaults(st);
2783
261k
    return psdf_encode_binary(pbw, templat, st);
2784
261k
}
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
275k
{
2817
275k
    stream *s = pdev->strm;
2818
275k
    int options = orig_options;
2819
275k
#define USE_ASCII85 1
2820
508k
#define USE_FLATE 2
2821
275k
#define USE_BROTLI 4
2822
275k
    static const char *const fnames[6] = {
2823
275k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
275k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
275k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
275k
    };
2827
275k
    static const char *const fnames1_2[6] = {
2828
275k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
275k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
275k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
275k
    };
2832
275k
    int filters = 0;
2833
275k
    int code;
2834
2835
275k
    if (options & DATA_STREAM_COMPRESS) {
2836
232k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
232k
        } else {
2840
232k
            filters |= USE_FLATE;
2841
232k
            options |= DATA_STREAM_BINARY;
2842
232k
        }
2843
232k
    }
2844
275k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
60.2k
        filters |= USE_ASCII85;
2846
275k
    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
275k
    if (options & DATA_STREAM_ENCRYPT) {
2860
23.6k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
23.6k
        if (code < 0)
2862
0
            return code;
2863
23.6k
        pdev->strm = s;
2864
23.6k
        pdw->encrypted = true;
2865
23.6k
    } else
2866
251k
        pdw->encrypted = false;
2867
275k
    if (options & DATA_STREAM_BINARY) {
2868
232k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
232k
        if (code < 0)
2870
0
            return code;
2871
232k
    } else {
2872
43.1k
        code = 0;
2873
43.1k
        pdw->binary.target = pdev->strm;
2874
43.1k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
43.1k
        pdw->binary.strm = pdev->strm;
2876
43.1k
    }
2877
275k
    pdw->start = stell(s);
2878
275k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
275k
    else
2881
275k
        if (filters & USE_FLATE)
2882
232k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
275k
    return code;
2884
275k
#undef USE_ASCII85
2885
275k
#undef USE_FLATE
2886
275k
#undef USE_BROTLI
2887
275k
}
2888
2889
int
2890
pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2891
                      int options, gs_id object_id)
2892
39.1k
{   int code;
2893
    /* object_id is an unused rudiment from the old code,
2894
       when the encription was applied when creating the stream.
2895
       The new code encrypts than copying stream from the temporary file. */
2896
39.1k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
39.1k
    pdw->binary.target = pdev->strm;
2898
39.1k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
39.1k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
39.1k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
39.1k
                options);
2902
39.1k
    if (object_id != 0)
2903
257
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
39.1k
    pdw->binary.strm = pdev->strm;
2905
39.1k
    return code;
2906
39.1k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
7.35k
{   int code;
2912
2913
7.35k
    code = pdf_close_aside(pdw->pdev);
2914
7.35k
    if (code < 0)
2915
0
        return code;
2916
7.35k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
7.35k
    if (code < 0)
2918
0
        return code;
2919
7.35k
    return 0;
2920
7.35k
}
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
13.6k
{
2929
13.6k
    if (pranges == NULL)
2930
13.6k
        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
40.4k
{
2967
40.4k
    gs_function_info_t info;
2968
40.4k
    cos_param_list_writer_t rlist;
2969
40.4k
    pdf_resource_t *pres;
2970
40.4k
    cos_object_t *pcfn;
2971
40.4k
    cos_dict_t *pcd;
2972
40.4k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
40.4k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
40.4k
    *ppres = pres;
2979
40.4k
    pcfn = pres->object;
2980
40.4k
    gs_function_get_info(pfn, &info);
2981
40.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
40.4k
    if (info.DataSource != 0) {
2994
29.9k
        psdf_binary_writer writer;
2995
29.9k
        stream *save = pdev->strm;
2996
29.9k
        cos_stream_t *pcos;
2997
29.9k
        stream *s;
2998
2999
29.9k
        cos_become(pcfn, cos_type_stream);
3000
29.9k
        pcos = (cos_stream_t *)pcfn;
3001
29.9k
        pcd = cos_stream_dict(pcos);
3002
29.9k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
29.9k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
29.9k
        pdev->strm = s;
3006
29.9k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
29.9k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
29.9k
            )
3009
29.3k
            code = pdf_flate_binary(pdev, &writer);
3010
29.9k
        if (code >= 0) {
3011
29.9k
            static const pdf_filter_names_t fnames = {
3012
29.9k
                PDF_FILTER_NAMES
3013
29.9k
            };
3014
3015
29.9k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
29.9k
        }
3017
29.9k
        if (code >= 0) {
3018
29.9k
            byte buf[100];    /* arbitrary */
3019
29.9k
            ulong pos;
3020
29.9k
            uint count;
3021
29.9k
            const byte *ptr;
3022
3023
460k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
430k
                count = min(sizeof(buf), info.data_size - pos);
3025
430k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
430k
                                        &ptr);
3027
430k
                stream_write(writer.strm, ptr, count);
3028
430k
            }
3029
29.9k
            code = psdf_end_binary(&writer);
3030
29.9k
            s_close_filters(&s, s->strm);
3031
29.9k
        }
3032
29.9k
        pdev->strm = save;
3033
29.9k
        if (code < 0)
3034
0
            return code;
3035
29.9k
    } else {
3036
10.4k
        cos_become(pcfn, cos_type_dict);
3037
10.4k
        pcd = (cos_dict_t *)pcfn;
3038
10.4k
    }
3039
40.4k
    if (info.Functions != 0) {
3040
1.93k
        cos_array_t *functions =
3041
1.93k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.93k
        cos_value_t v;
3043
3044
1.93k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.93k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.93k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.93k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.93k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.93k
    }
3054
40.4k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
40.4k
    if (code < 0)
3056
0
        return code;
3057
40.4k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
40.4k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
33.7k
{
3062
33.7k
    return true;
3063
33.7k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
40.4k
{
3067
40.4k
    pdf_resource_t *pres;
3068
40.4k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
40.4k
    if (code < 0)
3071
0
        return code;
3072
40.4k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
40.4k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
40.4k
    if (code < 0)
3077
0
        return code;
3078
40.4k
    pres->where_used |= pdev->used_mask;
3079
40.4k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
40.4k
    return 0;
3081
40.4k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.93k
{
3085
1.93k
    int i, code = 0;
3086
1.93k
    cos_value_t v;
3087
3088
8.76k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
6.83k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
6.83k
            (code = cos_array_add(pca, &v)) < 0
3091
6.83k
            ) {
3092
0
            break;
3093
0
        }
3094
6.83k
    }
3095
1.93k
    return code;
3096
1.93k
}
3097
3098
/* Write a Function object. */
3099
int
3100
pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, int64_t *pid)
3101
19.9k
{
3102
19.9k
    cos_value_t value;
3103
19.9k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
19.9k
    if (code < 0)
3106
0
        return code;
3107
19.9k
    *pid = value.contents.object->id;
3108
19.9k
    return 0;
3109
19.9k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
6.65k
{
3114
6.65k
    char key[] = "/Functions";
3115
6.65k
    cos_value_t *v, v2;
3116
3117
6.65k
    if (cos_type(pco) == cos_type_dict) {
3118
2.61k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
2.61k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
613
            if (cos_type(v->contents.object) == cos_type_array){
3121
613
                int code=0;
3122
3.26k
                while (code == 0) {
3123
2.65k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
2.65k
                }
3125
613
            }
3126
613
        }
3127
2.61k
    }
3128
6.65k
    if (cos_type(pco) == cos_type_array) {
3129
0
        int64_t index;
3130
0
        cos_array_t *pca = (cos_array_t *)pco;
3131
0
        const cos_array_element_t *element = cos_array_element_first(pca);
3132
0
        cos_value_t *v;
3133
3134
0
        while (element) {
3135
0
            element = cos_array_element_next(element, &index, (const cos_value_t **)&v);
3136
0
            if (v->value_type == COS_VALUE_OBJECT) {
3137
0
                if (pdf_find_resource_by_resource_id(pdev, resourceFunction, v->contents.object->id)){
3138
0
                    v->value_type = COS_VALUE_CONST;
3139
                    /* Need to remove the element from the array here */
3140
0
                }
3141
0
            }
3142
0
        }
3143
0
    }
3144
6.65k
    return 0;
3145
6.65k
}
3146
3147
/* Write a FontBBox dictionary element. */
3148
int
3149
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3150
31.8k
{
3151
31.8k
    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
31.8k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
31.8k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
31.8k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
31.8k
             pbox->p.x, pbox->p.y, x, y);
3163
31.8k
    return 0;
3164
31.8k
}
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
2.81k
{
3170
2.81k
    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
2.81k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
2.81k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
2.81k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
2.81k
             pbox->p.x, pbox->p.y, x, y);
3182
2.81k
    return 0;
3183
2.81k
}