Coverage Report

Created: 2026-07-24 07:44

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
27.6k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
27.6k
#  define Flate_filter_name "FlateDecode"
61
27.6k
#  define Flate_filter_template s_zlibE_template
62
27.6k
#  define Flate_filter_state stream_zlib_state
63
0
#  define Brotli_filter_name "BrotliDecode"
64
0
#  define Brotli_filter_template s_brotliE_template
65
0
#  define Brotli_filter_state stream_brotlie_state
66
#else
67
#  define compression_filter_name "LZWDecode"
68
#  define compression_filter_template s_LZWE_template
69
#  define compression_filter_state stream_LZW_state
70
#endif
71
72
/* Import procedures for writing filter parameters. */
73
extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
74
extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
75
76
#define CHECK(expr)\
77
1.93M
  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
23.0k
{
319
23.0k
    int index = 0;
320
321
93.4M
    do {
322
93.4M
        if (opdfread_ps[index] == 0x00)
323
23.0k
            break;
324
93.4M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
93.4M
        index++;
326
93.4M
    } while (1);
327
23.0k
    return 0;
328
23.0k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
23.0k
{
332
23.0k
    int index = 0;
333
334
898k
    do {
335
898k
        if (gs_mro_e_ps[index] == 0x00)
336
23.0k
            break;
337
875k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
875k
        index++;
339
875k
    } while (1);
340
341
23.0k
    if (HaveTrueTypes) {
342
23.0k
        char Buffer[256];
343
23.0k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
23.0k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
23.0k
        stream_write(s, Buffer, strlen(Buffer));
347
96.7M
        while (entry->Glyph) {
348
96.7M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
96.7M
            stream_write(s, Buffer, strlen(Buffer));
350
96.7M
            entry++;
351
96.7M
        };
352
23.0k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
23.0k
        stream_write(s, Buffer, strlen(Buffer));
354
355
23.0k
        index = 0;
356
829k
        do {
357
829k
            if (gs_mgl_e_ps[index] == 0x00)
358
23.0k
                break;
359
806k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
806k
            index++;
361
806k
        } while (1);
362
23.0k
    }
363
23.0k
    return 0;
364
23.0k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
23.0k
{
369
23.0k
    int code;
370
371
23.0k
    code = write_opdfread(s);
372
23.0k
    if (code < 0)
373
0
        return code;
374
375
23.0k
    code = write_tt_encodings(s, HaveTrueTypes);
376
23.0k
    return code;
377
378
23.0k
}
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
42.9k
{
455
42.9k
    const char * const *argv = NULL;
456
42.9k
    const char *arg;
457
42.9k
    int towrite, length, i, j, argc;
458
459
42.9k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
42.9k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
42.9k
    length = 12;
463
816k
    for (i=0;i < argc; i++) {
464
773k
        arg = argv[i];
465
466
773k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
773k
        } else {
470
773k
            stream_write(s, (byte *)" ", 1);
471
773k
            length++;
472
773k
        }
473
474
773k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
773k
        else
477
773k
            towrite = strlen(arg);
478
479
773k
        length += towrite;
480
481
9.33M
        for (j=0;j < towrite;j++) {
482
8.56M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
8.56M
            } else {
485
8.56M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
8.56M
                } else {
488
8.56M
                    stream_write(s, (byte *)&arg[j], 1);
489
8.56M
                }
490
8.56M
            }
491
8.56M
        }
492
773k
    }
493
42.9k
    stream_write(s, (byte *)"\n", 1);
494
42.9k
    return 0;
495
42.9k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
23.0k
{
499
23.0k
    stream *s = pdev->strm;
500
501
23.0k
    if (pdev->ForOPDFRead) {
502
23.0k
        char cre_date_time[41];
503
23.0k
        int code, status, cre_date_time_len;
504
23.0k
        char BBox[256];
505
506
23.0k
        if (pdev->Eps2Write)
507
11.4k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
11.6k
        else
509
11.6k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
23.0k
        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
23.0k
        {
515
23.0k
            int pagecount = 1, j;
516
23.0k
            double urx=0, ury=0;
517
518
391k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
368k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
398k
                for (; pres != 0; pres = pres->next)
522
29.5k
                    if ((!pres->named || pdev->ForOPDFRead)
523
29.5k
                        && !pres->object->written) {
524
29.5k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
29.5k
                        if (ceil(page->MediaBox.x) > urx)
526
23.3k
                            urx = ceil(page->MediaBox.x);
527
29.5k
                        if (ceil(page->MediaBox.y) > ury)
528
23.1k
                            ury = ceil(page->MediaBox.y);
529
29.5k
                        pagecount++;
530
29.5k
                    }
531
368k
            }
532
23.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
17.0k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
5.94k
            else
535
5.94k
                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
23.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
23.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
17.0k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
5.94k
            else
540
5.94k
                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
23.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
23.0k
        }
543
23.0k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
23.0k
        cre_date_time[cre_date_time_len] = 0;
545
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
23.0k
                pdev->dname);
547
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
23.0k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
23.0k
        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
23.0k
        stream_puts(s, "10 dict dup begin\n");
573
23.0k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
23.0k
        if (pdev->Eps2Write) {
575
11.4k
            stream_puts(s, "/SetPageSize false def\n");
576
11.4k
            stream_puts(s, "/EPS2Write true def\n");
577
11.6k
        } else {
578
11.6k
            if (pdev->SetPageSize)
579
11.6k
                stream_puts(s, "/SetPageSize true def\n");
580
11.6k
            stream_puts(s, "/EPS2Write false def\n");
581
11.6k
        }
582
23.0k
        stream_puts(s, "end\n");
583
584
23.0k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
23.0k
        if (code < 0)
586
0
            return code;
587
23.0k
        status = s_close_filters(&s, pdev->strm);
588
23.0k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
23.0k
        stream_puts(s, "\n");
591
23.0k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
23.0k
    }
593
23.0k
    return 0;
594
23.0k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
568k
{
600
568k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
568k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
125k
        stream *s = pdev->strm;
605
125k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
125k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
125k
        if (pdev->ForOPDFRead) {
609
105k
            int code, status;
610
105k
            char BBox[256];
611
105k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
105k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
105k
            if (pdev->ProduceDSC)
615
105k
                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
105k
        }
660
125k
        if (!(pdev->ForOPDFRead)) {
661
19.9k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
19.9k
            if (pdev->binary_ok)
663
19.9k
                stream_puts(s, "%\307\354\217\242\n");
664
19.9k
            pdfwrite_write_args_comment(pdev, s);
665
19.9k
        }
666
125k
    }
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
568k
    if (!pdev->params.CompressPages)
677
247k
        pdev->compression = pdf_compress_none;
678
321k
    else {
679
321k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
321k
        else
682
321k
            pdev->compression = pdf_compress_Flate;
683
321k
    }
684
568k
    return 0;
685
568k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
692k
{
693
692k
    return (pdev->next_id)++;
694
692k
}
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.57M
{
706
1.57M
    stream *s = pdev->strm;
707
1.57M
    gs_offset_t pos = stell(s);
708
709
1.57M
    if (s == pdev->asides.strm)
710
292k
        pos |= ASIDES_BASE_POSITION;
711
1.57M
    return pos;
712
1.57M
}
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
77.2k
{
730
77.2k
    int64_t id = pdf_next_id(pdev);
731
77.2k
    gs_offset_t pos = 0;
732
733
77.2k
    if (pdev->doubleXref) {
734
77.2k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
77.2k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
77.2k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
77.2k
    return id;
740
77.2k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
614k
{
746
614k
    int64_t id = pdf_next_id(pdev);
747
614k
    gs_offset_t pos = 0;
748
749
614k
    if (pdev->doubleXref) {
750
614k
        if (pdev->strm == pdev->ObjStm.strm)
751
29.6k
            pos = pdev->ObjStm_id;
752
585k
        else
753
585k
            pos = 0;
754
614k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
614k
        if (pdev->strm == pdev->ObjStm.strm)
756
29.6k
            pos = pdev->NumObjStmObjects;
757
585k
        else
758
585k
            pos = pdf_stell(pdev);
759
614k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
614k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
614k
    return id;
766
614k
}
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
186
{
780
186
    gp_file *tfile = pdev->xref.file;
781
186
    int64_t tpos = gp_ftell(tfile);
782
186
    gs_offset_t pos = 0;
783
784
186
    if (pdev->doubleXref) {
785
186
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
186
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
186
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
186
    }
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
186
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
186
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
186
    return 0;
800
186
}
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
616k
{
806
616k
    stream *s = pdev->strm;
807
808
616k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
616k
    if (id <= 0) {
812
156k
        id = pdf_obj_ref(pdev);
813
459k
    } else {
814
459k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
459k
        gp_file *tfile = pdev->xref.file;
816
459k
        int64_t tpos = gp_ftell(tfile);
817
818
459k
        if (pdev->doubleXref) {
819
459k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
459k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
459k
            if (pdev->strm == pdev->ObjStm.strm)
823
176k
                fake_pos = pdev->ObjStm_id;
824
459k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
459k
            if (pdev->strm == pdev->ObjStm.strm)
826
176k
                pos = pdev->NumObjStmObjects;
827
459k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
459k
        } 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
459k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
459k
    }
837
616k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
208k
        switch(type) {
839
852
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
852
                break;
844
29.5k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
29.5k
                break;
847
1.88k
            case resourceColorSpace:
848
1.88k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
1.88k
                break;
850
9.63k
            case resourceExtGState:
851
9.63k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
9.63k
                break;
853
844
            case resourcePattern:
854
844
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
844
                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
23.5k
            case resourceFont:
861
                /* Ought to write the font name here */
862
23.5k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
23.5k
                break;
864
80.9k
            case resourceCharProc:
865
80.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
80.9k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
10.1k
            case resourceFontDescriptor:
871
10.1k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
10.1k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.04k
            case resourceFunction:
877
2.04k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.04k
                break;
879
4.96k
            case resourceEncoding:
880
4.96k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
4.96k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
10.9k
            case resourceHalftone:
886
10.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
10.9k
                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
13
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
13
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
13
                break;
899
0
            case resourceStream:
900
                /* Possibly we should not add comments to this type */
901
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF stream obj_%"PRId64")\n", id);
902
0
                break;
903
0
            case resourceOutline:
904
                /* This should not be possible, not valid in PostScript */
905
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Outline obj_%"PRId64")\n", id);
906
0
                break;
907
0
            case resourceArticle:
908
                /* This should not be possible, not valid in PostScript */
909
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Article obj_%"PRId64")\n", id);
910
0
                break;
911
0
            case resourceDests:
912
                /* This should not be possible, not valid in PostScript */
913
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Dests obj_%"PRId64")\n", id);
914
0
                break;
915
0
            case resourceEmbeddedFiles:
916
                /* This should not be possible, not valid in PostScript */
917
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF EmbeddedFiles obj_%"PRId64")\n", id);
918
0
                break;
919
0
            case resourceLabels:
920
                /* This should not be possible, not valid in PostScript */
921
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Page Labels obj_%"PRId64")\n", id);
922
0
                break;
923
0
            case resourceThread:
924
                /* This should not be possible, not valid in PostScript */
925
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Thread obj_%"PRId64")\n", id);
926
0
                break;
927
0
            case resourceCatalog:
928
                /* This should not be possible, not valid in PostScript */
929
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Catalog obj_%"PRId64")\n", id);
930
0
                break;
931
0
            case resourceEncrypt:
932
                /* This should not be possible, not valid in PostScript */
933
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Encryption obj_%"PRId64")\n", id);
934
0
                break;
935
0
            case resourcePagesTree:
936
                /* This should not be possible, not valid in PostScript */
937
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%"PRId64")\n", id);
938
0
                break;
939
0
            case resourceMetadata:
940
                /* This should not be possible, not valid in PostScript */
941
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Metadata obj_%"PRId64")\n", id);
942
0
                break;
943
0
            case resourceICC:
944
                /* This should not be possible, not valid in PostScript */
945
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%"PRId64")\n", id);
946
0
                break;
947
0
            case resourceAnnotation:
948
                /* This should not be possible, not valid in PostScript */
949
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Annotation obj_%"PRId64")\n", id);
950
0
                break;
951
10.1k
            case resourceFontFile:
952
10.1k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
10.1k
                break;
954
23.0k
            default:
955
23.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
23.0k
                break;
957
208k
        }
958
208k
    }
959
616k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
414k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
616k
    return id;
962
616k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
27.6k
{
966
27.6k
    return pdf_open_obj(pdev, 0L, type);
967
27.6k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
616k
{
973
616k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
414k
        stream_puts(pdev->strm, "endobj\n");
975
616k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
208k
        switch(type) {
977
29.5k
            case resourcePage:
978
29.5k
                break;
979
179k
            default:
980
179k
            stream_puts(pdev->strm, "%%EndResource\n");
981
179k
            break;
982
208k
        }
983
208k
    }
984
616k
    return 0;
985
616k
}
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
124k
{
1034
124k
    gs_memory_t *mem = pdev->v_memory;
1035
124k
    stream_arcfour_state *ss;
1036
124k
    gs_md5_byte_t key[16];
1037
124k
    int code, keylength;
1038
1039
124k
    if (!pdev->KeyLength)
1040
124k
        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
57.1k
{
1065
57.1k
    stream *s;
1066
57.1k
    int code;
1067
1068
57.1k
    if (pdev->contents_id != 0)
1069
9
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
57.1k
    pdev->compression_at_page_start = pdev->compression;
1071
57.1k
    if (pdev->ResourcesBeforeUsage) {
1072
29.5k
        pdf_resource_t *pres;
1073
1074
29.5k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
29.5k
                    true, pdev->params.CompressPages);
1076
29.5k
        if (code < 0)
1077
0
            return code;
1078
29.5k
        pdev->contents_id = pres->object->id;
1079
29.5k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
29.5k
        pdev->contents_pos = -1; /* inapplicable */
1081
29.5k
        s = pdev->strm;
1082
29.5k
    } else {
1083
27.6k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
27.6k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
27.6k
        s = pdev->strm;
1086
27.6k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
27.6k
        if (pdev->compression == pdf_compress_Flate) {
1088
27.6k
            if (pdev->binary_ok)
1089
27.6k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
27.6k
        }
1093
27.6k
        if (pdev->compression == pdf_compress_Brotli) {
1094
0
            if (pdev->binary_ok)
1095
0
                pprints1(s, "/Filter /%s", Brotli_filter_name);
1096
0
            else
1097
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Brotli_filter_name);
1098
0
        }
1099
27.6k
        stream_puts(s, ">>\nstream\n");
1100
27.6k
        pdev->contents_pos = pdf_stell(pdev);
1101
27.6k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
27.6k
        if (code < 0)
1103
0
            return code;
1104
27.6k
        pdev->strm = s;
1105
27.6k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
27.6k
            const stream_template *templat;
1107
27.6k
            stream *es;
1108
27.6k
            byte *buf;
1109
27.6k
            Flate_filter_state *st;
1110
1111
27.6k
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1112
0
                const stream_template *templat2 = &s_A85E_template;
1113
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1114
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1115
0
                                           "PDF contents buffer");
1116
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1117
0
                                templat2->stype, "PDF contents state");
1118
0
                if (as == 0 || ast == 0 || buf == 0)
1119
0
                    return_error(gs_error_VMerror);
1120
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1121
0
                           s_mode_write);
1122
0
                ast->memory = pdev->pdf_memory;
1123
0
                ast->templat = templat2;
1124
0
                as->state = (stream_state *) ast;
1125
0
                as->procs.process = templat2->process;
1126
0
                as->strm = s;
1127
0
                (*templat2->init) ((stream_state *) ast);
1128
0
                pdev->strm = s = as;
1129
0
            }
1130
27.6k
            templat = &Flate_filter_template;
1131
27.6k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
27.6k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
27.6k
                                       "PDF compression buffer");
1134
27.6k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
27.6k
                                 templat->stype, "PDF compression state");
1136
27.6k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
27.6k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
27.6k
                       s_mode_write);
1140
27.6k
            st->memory = pdev->pdf_memory;
1141
27.6k
            st->templat = templat;
1142
27.6k
            es->state = (stream_state *) st;
1143
27.6k
            es->procs.process = templat->process;
1144
27.6k
            es->strm = s;
1145
27.6k
            (*templat->set_defaults) ((stream_state *) st);
1146
27.6k
            code = (*templat->init) ((stream_state *) st);
1147
27.6k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
27.6k
            pdev->strm = s = es;
1152
27.6k
        }
1153
27.6k
        if (pdev->compression == pdf_compress_Brotli) { /* Set up the Brotli filter. */
1154
0
            const stream_template *templat;
1155
0
            stream *es;
1156
0
            byte *buf;
1157
0
            Brotli_filter_state *st;
1158
1159
0
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1160
0
                const stream_template *templat2 = &s_A85E_template;
1161
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1162
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1163
0
                                           "PDF contents buffer");
1164
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1165
0
                                templat2->stype, "PDF contents state");
1166
0
                if (as == 0 || ast == 0 || buf == 0)
1167
0
                    return_error(gs_error_VMerror);
1168
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1169
0
                           s_mode_write);
1170
0
                ast->memory = pdev->pdf_memory;
1171
0
                ast->templat = templat2;
1172
0
                as->state = (stream_state *) ast;
1173
0
                as->procs.process = templat2->process;
1174
0
                as->strm = s;
1175
0
                (*templat2->init) ((stream_state *) ast);
1176
0
                pdev->strm = s = as;
1177
0
            }
1178
0
            templat = &Brotli_filter_template;
1179
0
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1180
0
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1181
0
                                       "PDF compression buffer");
1182
0
            st = gs_alloc_struct(pdev->pdf_memory, Brotli_filter_state,
1183
0
                                 templat->stype, "PDF compression state");
1184
0
            if (es == 0 || st == 0 || buf == 0)
1185
0
                return_error(gs_error_VMerror);
1186
0
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1187
0
                       s_mode_write);
1188
0
            st->memory = pdev->pdf_memory;
1189
0
            st->templat = templat;
1190
0
            es->state = (stream_state *) st;
1191
0
            es->procs.process = templat->process;
1192
0
            es->strm = s;
1193
0
            (*templat->set_defaults) ((stream_state *) st);
1194
0
            code = (*templat->init) ((stream_state *) st);
1195
0
            if (code < 0) {
1196
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1197
0
                return code;
1198
0
            }
1199
0
            pdev->strm = s = es;
1200
0
        }
1201
27.6k
    }
1202
    /*
1203
     * Scale the coordinate system.  Use an extra level of q/Q for the
1204
     * sake of poorly designed PDF tools that assume that the contents
1205
     * stream restores the CTM.
1206
     */
1207
57.1k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
57.1k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
57.1k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
27.6k
        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
27.6k
    }
1218
57.1k
    pdev->AR4_save_bug = false;
1219
57.1k
    return PDF_IN_STREAM;
1220
57.1k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
198k
{
1225
198k
    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
198k
    if (!pdev->clipped_text_pending) {
1233
198k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
198k
        if (code < 0)
1235
0
            return 0;
1236
198k
    }
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
198k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
198k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
198k
    pdev->procsets |= Text;
1248
198k
    code = pdf_from_stream_to_text(pdev);
1249
198k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
198k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
198k
{
1255
198k
    int code = pdf_from_string_to_text(pdev);
1256
1257
198k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
198k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
198k
{
1263
198k
    int code;
1264
1265
198k
    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
198k
    if (pdev->clipped_text_pending)
1273
69
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
69
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
198k
    else {
1276
198k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
198k
        if (code < 0)
1278
0
            return code;
1279
198k
        pdf_reset_text(pdev); /* because of Q */
1280
198k
    }
1281
198k
    return PDF_IN_STREAM;
1282
198k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
57.1k
{
1287
57.1k
    stream *s = pdev->strm;
1288
57.1k
    gs_offset_t length;
1289
57.1k
    int code;
1290
57.1k
    stream *target;
1291
57.1k
     char str[21];
1292
1293
57.1k
    if (pdev->ResourcesBeforeUsage) {
1294
29.5k
        int code = pdf_exit_substream(pdev);
1295
1296
29.5k
        if (code < 0)
1297
0
            return code;
1298
29.5k
    } else {
1299
27.6k
        if (pdev->vgstack_depth) {
1300
158
            code = pdf_restore_viewer_state(pdev, s);
1301
158
            if (code < 0)
1302
11
                return code;
1303
158
        }
1304
27.6k
        target = pdev->strm;
1305
1306
27.6k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
27.6k
            target = target->strm;
1308
27.6k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
27.6k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
27.6k
        s_close_filters(&pdev->strm, target);
1313
1314
27.6k
        s = pdev->strm;
1315
27.6k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
27.6k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
27.6k
        stream_puts(s, "endstream\n");
1319
27.6k
        pdf_end_obj(pdev, resourceStream);
1320
1321
27.6k
        if (pdev->WriteObjStms) {
1322
27.6k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
27.6k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
27.6k
            stream_puts(pdev->strm, str);
1325
27.6k
            pdf_end_separate(pdev, resourceLength);
1326
27.6k
        } else {
1327
12
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
12
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
12
            stream_puts(s, str);
1330
12
            pdf_end_obj(pdev, resourceLength);
1331
12
        }
1332
27.6k
    }
1333
57.1k
    return PDF_IN_NONE;
1334
57.1k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
16.6M
{
1340
16.6M
    int (*proc) (gx_device_pdf *);
1341
1342
17.4M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
709k
        int code = (*proc) (pdev);
1344
1345
709k
        if (code < 0)
1346
20
            return code;
1347
709k
        pdev->context = (pdf_context_t) code;
1348
709k
    }
1349
16.6M
    pdev->context = context;
1350
16.6M
    return 0;
1351
16.6M
}
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
57.1k
{
1357
57.1k
    if (pdev->context == PDF_IN_NONE)
1358
7
        return 0;
1359
57.1k
    if (last) {     /* Exit from the clipping path gsave. */
1360
57.1k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
57.1k
        if (code < 0)
1363
0
            return code;
1364
57.1k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
57.1k
        pdf_close_text_contents(pdev);
1366
57.1k
    }
1367
57.1k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
57.1k
}
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
168k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
168k
    pres->where_used = 0;
1386
168k
    if (pres->object) {
1387
168k
        pres->object->written = true;
1388
168k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
94.1k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
94.1k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
94.1k
            if (code < 0)
1393
0
                return code;
1394
94.1k
        }
1395
168k
        cos_release(pres->object, "pdf_cancel_resource");
1396
168k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
168k
        pres->object = 0;
1398
168k
    }
1399
168k
    return 0;
1400
168k
}
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
167k
{   /* fixme : optimize. */
1406
167k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
167k
    pdf_resource_t *pres;
1408
167k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
167k
    int i;
1410
1411
    /* since we're about to free the resource, we can just set
1412
       any of these references to null
1413
    */
1414
2.00M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
1.83M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
1.83M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
1.83M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
4
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
4
        }
1424
1.83M
    }
1425
1426
170k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
170k
        if (pres == pres1) {
1428
167k
            *pprev = pres->prev;
1429
167k
            break;
1430
167k
        }
1431
1432
167k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
167k
        pprev = pchain + i;
1434
170k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
170k
            if (pres == pres1) {
1436
167k
                *pprev = pres->next;
1437
167k
                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
167k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
167k
                return;
1444
167k
            }
1445
167k
    }
1446
167k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
53.7k
{
1451
53.7k
    return 1;
1452
53.7k
}
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
127k
{
1467
127k
    pdf_resource_t *pres1 = *ppres;
1468
127k
    int code;
1469
1470
127k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
127k
    if (code < 0)
1472
0
        return code;
1473
127k
    if (code != 0) {
1474
82.2k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
82.2k
        if (code < 0)
1476
0
            return code;
1477
82.2k
        pdf_forget_resource(pdev, pres1, rtype);
1478
82.2k
        return 0;
1479
82.2k
    } else {
1480
45.4k
        if (pres1->object->id < 0)
1481
45.4k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
45.4k
        if (write) {
1483
21.0k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
21.0k
            if (code < 0)
1485
0
                return code;
1486
21.0k
            pres1->object->written = 1;
1487
21.0k
        }
1488
45.4k
        return 1;
1489
45.4k
    }
1490
127k
}
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
892k
{
1497
892k
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
892k
    pdf_resource_t **pprev = pchain;
1499
892k
    pdf_resource_t *pres;
1500
1501
2.06M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
1.95M
        if (pres->rid == rid) {
1503
778k
            if (pprev != pchain) {
1504
270k
                *pprev = pres->next;
1505
270k
                pres->next = *pchain;
1506
270k
                *pchain = pres;
1507
270k
            }
1508
778k
            return pres;
1509
778k
        }
1510
114k
    return 0;
1511
892k
}
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
32.8k
{
1517
32.8k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
32.8k
    pdf_resource_t *pres;
1519
32.8k
    int i;
1520
1521
529k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
3.06M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
2.56M
            if (pres->object && pres->object->id == id)
1524
3.74k
                return pres;
1525
2.56M
        }
1526
500k
    }
1527
29.1k
    return 0;
1528
32.8k
}
1529
1530
/* Find same resource. */
1531
int
1532
pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
1533
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
1534
157k
{
1535
157k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
157k
    pdf_resource_t *pres;
1537
157k
    cos_object_t *pco0 = (*ppres)->object;
1538
157k
    int i;
1539
1540
1.33M
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
3.57M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
2.39M
            if (*ppres != pres) {
1543
2.24M
                int code;
1544
2.24M
                cos_object_t *pco1 = pres->object;
1545
1546
2.24M
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
53.6k
                    continue;      /* don't compare different types */
1548
2.18M
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
2.18M
                if (code < 0)
1550
0
                    return code;
1551
2.18M
                if (code > 0) {
1552
84.0k
                    code = eq(pdev, *ppres, pres);
1553
84.0k
                    if (code < 0)
1554
0
                        return code;
1555
84.0k
                    if (code > 0) {
1556
84.0k
                        *ppres = pres;
1557
84.0k
                        return 1;
1558
84.0k
                    }
1559
84.0k
                }
1560
2.18M
            }
1561
2.39M
        }
1562
1.26M
    }
1563
73.4k
    return 0;
1564
157k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
12.6k
{
1569
12.6k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
12.6k
    pdf_resource_t *pres;
1571
12.6k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
12.6k
    int i;
1573
1574
    /* since we're about to free the resource, we can just set
1575
       any of these references to null
1576
    */
1577
151k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
138k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
138k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
138k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
138k
    }
1588
1589
15.5k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
15.5k
        if (pres == pres1) {
1591
12.6k
            *pprev = pres->prev;
1592
12.6k
            break;
1593
12.6k
        }
1594
1595
12.6k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
12.6k
        pprev = pchain + i;
1597
12.9k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
12.9k
            if (pres == pres1) {
1599
12.6k
                *pprev = pres->next;
1600
#if 0
1601
                if (pres->object) {
1602
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1603
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1604
                    pres->object = 0;
1605
                }
1606
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1607
#endif
1608
12.6k
                return;
1609
12.6k
            }
1610
12.6k
    }
1611
12.6k
}
1612
1613
/* Drop resources by a condition. */
1614
void
1615
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1616
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1617
0
{
1618
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1619
0
    pdf_resource_t **pprev;
1620
0
    pdf_resource_t *pres;
1621
0
    int i;
1622
1623
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1624
0
        pprev = pchain + i;
1625
0
        for (; (pres = *pprev) != 0; ) {
1626
0
            if (cond(pdev, pres)) {
1627
0
                *pprev = pres->next;
1628
0
                pres->next = pres; /* A temporary mark - see below */
1629
0
            } else
1630
0
                pprev = &pres->next;
1631
0
        }
1632
0
    }
1633
0
    pprev = &pdev->last_resource;
1634
0
    for (; (pres = *pprev) != 0; )
1635
0
        if (pres->next == pres) {
1636
0
            *pprev = pres->prev;
1637
0
            if (pres->object) {
1638
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1639
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1640
0
                pres->object = 0;
1641
0
            }
1642
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1643
0
        } else
1644
0
            pprev = &pres->prev;
1645
0
}
1646
1647
/* Print resource statistics. */
1648
void
1649
pdf_print_resource_statistics(gx_device_pdf * pdev)
1650
0
{
1651
1652
0
    int rtype;
1653
1654
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1655
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1656
0
        pdf_resource_t *pres;
1657
0
        const char *name = pdf_resource_type_names[rtype];
1658
0
        int i, n = 0;
1659
1660
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1661
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1662
0
        }
1663
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1664
0
                (name ? name : ""), n);
1665
0
    }
1666
0
}
1667
1668
int FlushObjStm(gx_device_pdf *pdev)
1669
20.0k
{
1670
20.0k
    int code = 0, i, len = 0, id = 0, end;
1671
20.0k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
20.0k
    pdf_resource_t *pres;
1673
20.0k
    int options = DATA_STREAM_BINARY;
1674
1675
20.0k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
20.0k
    pdev->WriteObjStms = false;
1679
1680
20.0k
    sflush(pdev->strm);
1681
20.0k
    sflush(pdev->ObjStm.strm);
1682
20.0k
    end = stell(pdev->ObjStm.strm);
1683
1684
20.0k
    if (pdev->CompressStreams)
1685
20.0k
        options |= DATA_STREAM_COMPRESS;
1686
1687
20.0k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
20.0k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
20.0k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
20.0k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
20.0k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
20.0k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
20.0k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
20.0k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
221k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
201k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
201k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
201k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
201k
        strcat(offsets, offset);
1713
201k
    }
1714
1715
20.0k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
20.0k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
20.0k
    stream_puts(pdev->strm, offsets);
1723
1724
20.0k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
20.0k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
20.0k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
20.0k
    code = pdf_close_aside(pdev);
1732
20.0k
    if (code < 0)
1733
0
        return code;
1734
20.0k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
20.0k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
20.0k
    pdev->WriteObjStms = true;
1740
20.0k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
20.0k
    if (pdev->ObjStmOffsets != NULL) {
1742
20.0k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
20.0k
        pdev->ObjStmOffsets = NULL;
1744
20.0k
    }
1745
20.0k
    pdev->NumObjStmObjects = 0;
1746
20.0k
    pdev->ObjStm_id = 0;
1747
1748
20.0k
    pdev->WriteObjStms = true;
1749
20.0k
    return code;
1750
20.0k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
20.0k
{
1754
20.0k
    int code;
1755
1756
20.0k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
20.0k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
20.0k
    if (code < 0)
1760
0
        return code;
1761
1762
20.0k
    pdev->NumObjStmObjects = 0;
1763
20.0k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
20.0k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
20.0k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
20.0k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
20.0k
    return code;
1772
20.0k
}
1773
1774
/* Begin an object logically separate from the contents. */
1775
int64_t
1776
pdf_open_separate_noObjStm(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1777
118k
{
1778
118k
    int code;
1779
1780
118k
    code = pdfwrite_pdf_open_document(pdev);
1781
118k
    if (code < 0)
1782
0
        return code;
1783
118k
    pdev->asides.save_strm = pdev->strm;
1784
118k
    pdev->strm = pdev->asides.strm;
1785
118k
    code = pdf_open_obj(pdev, id, type);
1786
118k
    return code;
1787
118k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
421k
{
1791
421k
    if (type == resourceStream)
1792
0
        return true;
1793
421k
    if (type == resourceCharProc)
1794
558
        return true;
1795
421k
    if (type == resourcePattern)
1796
13.1k
        return true;
1797
408k
    if (type == resourceXObject)
1798
4.75k
        return true;
1799
403k
    return false;
1800
408k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
372k
{
1805
372k
    int code;
1806
1807
372k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
170k
        code = pdfwrite_pdf_open_document(pdev);
1809
170k
        if (code < 0)
1810
0
            return code;
1811
170k
        pdev->asides.save_strm = pdev->strm;
1812
170k
        pdev->strm = pdev->asides.strm;
1813
170k
        code = pdf_open_obj(pdev, id, type);
1814
201k
    } else {
1815
201k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
137
            code = FlushObjStm(pdev);
1817
137
            if (code < 0)
1818
0
                return code;
1819
137
        }
1820
201k
        if (!pdev->ObjStm.strm) {
1821
20.0k
            code = NewObjStm(pdev);
1822
20.0k
            if (code < 0)
1823
0
                return code;
1824
20.0k
        }
1825
201k
        pdev->ObjStm.save_strm = pdev->strm;
1826
201k
        pdev->strm = pdev->ObjStm.strm;
1827
201k
        code = pdf_open_obj(pdev, id, type);
1828
201k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
201k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
201k
    }
1831
372k
    return code;
1832
372k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
127k
{
1836
127k
    return pdf_open_separate(pdev, 0L, type);
1837
127k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
342k
{
1842
342k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
342k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
342k
}
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
523k
{
1852
523k
    pdf_resource_t *pres;
1853
523k
    cos_object_t *object;
1854
1855
523k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
523k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
523k
                           "pdf_alloc_aside(resource)");
1859
523k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
523k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
523k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
523k
    memset(pres, 0, pst->ssize);
1865
523k
    pres->object = object;
1866
523k
    if (id < 0) {
1867
343k
        object->id = -1L;
1868
343k
        pres->rname[0] = 0;
1869
343k
    } else
1870
180k
        pdf_reserve_object_id(pdev, pres, id);
1871
523k
    pres->next = *plist;
1872
523k
    pres->rid = 0;
1873
523k
    *plist = pres;
1874
523k
    pres->prev = pdev->last_resource;
1875
523k
    pdev->last_resource = pres;
1876
523k
    pres->named = false;
1877
523k
    pres->global = false;
1878
523k
    pres->where_used = pdev->used_mask;
1879
523k
    *ppres = pres;
1880
523k
    return 0;
1881
523k
}
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
77.6k
{
1887
77.6k
    int64_t id = pdf_begin_separate(pdev, type);
1888
77.6k
    int code = 0;
1889
1890
77.6k
    if (id < 0)
1891
0
        return (int)id;
1892
77.6k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
77.6k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
77.6k
    return code;
1897
77.6k
}
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
77.6k
{
1904
77.6k
    int code;
1905
1906
77.6k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
77.6k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
77.6k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
77.6k
    if (code >= 0)
1913
77.6k
        (*ppres)->rid = rid;
1914
77.6k
    return code;
1915
77.6k
}
1916
int
1917
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1918
                   pdf_resource_t ** ppres)
1919
76.9k
{
1920
76.9k
    int code;
1921
1922
76.9k
    if (rtype >= NUM_RESOURCE_TYPES)
1923
0
        rtype = resourceOther;
1924
1925
76.9k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1926
1927
76.9k
    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
76.9k
    return code;
1934
76.9k
}
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
224k
{
1952
224k
    int code;
1953
1954
224k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
224k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
224k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
224k
    if (code >= 0)
1961
224k
        (*ppres)->rid = rid;
1962
224k
    return code;
1963
224k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
1.98M
{
1969
1.98M
    return pres->object->id;
1970
1.98M
}
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
118k
{
1976
118k
    int code = pdf_end_obj(pdev, type);
1977
1978
118k
    pdev->strm = pdev->asides.save_strm;
1979
118k
    pdev->asides.save_strm = 0;
1980
118k
    return code;
1981
118k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
372k
{
1985
372k
    int code = pdf_end_obj(pdev, type);
1986
1987
372k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
170k
        pdev->strm = pdev->asides.save_strm;
1989
170k
        pdev->asides.save_strm = 0;
1990
201k
    } else {
1991
201k
        pdev->strm = pdev->ObjStm.save_strm;
1992
201k
        pdev->ObjStm.save_strm = 0;
1993
201k
        pdev->NumObjStmObjects++;
1994
201k
    }
1995
372k
    return code;
1996
372k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
627
{
2000
627
    return pdf_end_separate(pdev, type);
2001
627
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
627
{
2007
627
    return pdf_end_aside(pdev, type);
2008
627
}
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
577k
{
2018
577k
    int j, code = 0;
2019
2020
9.82M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
9.24M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
9.46M
        for (; pres != 0; pres = pres->next)
2024
218k
            if ((!pres->named || pdev->ForOPDFRead)
2025
218k
                && pres->object && !pres->object->written) {
2026
41.3k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
41.3k
            }
2028
9.24M
    }
2029
577k
    return code;
2030
577k
}
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
23.0k
{
2040
23.0k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
23.0k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
23.0k
    if (pres == NULL)
2044
0
        return;
2045
23.0k
    pres1 = pres->next;
2046
29.5k
    for (;;) {
2047
29.5k
        if (pres1 == NULL)
2048
23.0k
            break;
2049
6.51k
        pres2 = pres1->next;
2050
6.51k
        pres1->next = pres;
2051
6.51k
        pres = pres1;
2052
6.51k
        pres1 = pres2;
2053
6.51k
    }
2054
23.0k
    pres0->next = NULL;
2055
23.0k
    pdev->resources[rtype].chains[0] = pres;
2056
23.0k
}
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
300k
{
2065
300k
    int j;
2066
2067
5.11M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
4.81M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
4.81M
        pdf_resource_t *pres;
2070
2071
4.94M
        while ((pres = *prev) != 0) {
2072
137k
            if (pres->named) { /* named, don't free */
2073
6
                prev = &pres->next;
2074
137k
            } else {
2075
137k
                if (pres->object) {
2076
137k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
137k
                    pres->object = 0;
2078
137k
                }
2079
137k
                *prev = pres->next;
2080
137k
            }
2081
137k
        }
2082
4.81M
    }
2083
300k
    return 0;
2084
300k
}
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
57.1k
{
2093
57.1k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
514k
    for (i = 0; i <= resourceFont; ++i) {
2098
457k
        stream *s = 0;
2099
457k
        int j;
2100
2101
457k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
57.1k
            continue;
2103
400k
        page->resource_ids[i] = 0;
2104
6.80M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
6.40M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
6.62M
            for (; pres != 0; pres = pres->next) {
2108
222k
                if (pres->where_used & pdev->used_mask) {
2109
129k
                    int64_t id = pdf_resource_id(pres);
2110
2111
129k
                    if (id == -1L)
2112
10.3k
                        continue;
2113
119k
                    if (s == 0) {
2114
37.2k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
37.2k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
37.2k
                        s = pdev->strm;
2117
37.2k
                        stream_puts(s, "<<");
2118
37.2k
                    }
2119
119k
                    pprints1(s, "/%s\n", pres->rname);
2120
119k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
119k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
119k
                    if (clear_usage)
2123
119k
                        pres->where_used -= pdev->used_mask;
2124
119k
                }
2125
222k
            }
2126
6.40M
        }
2127
400k
        if (s) {
2128
37.2k
            stream_puts(s, ">>\n");
2129
37.2k
            pdf_end_separate(pdev, i);
2130
37.2k
        }
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
400k
        if (i != resourceFont && i != resourceProperties)
2137
285k
            pdf_write_resource_objects(pdev, i);
2138
400k
    }
2139
57.1k
    page->procsets = pdev->procsets;
2140
57.1k
    return 0;
2141
57.1k
}
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
297k
{
2147
297k
    gs_offset_t r, left = count;
2148
297k
    byte buf[sbuf_size];
2149
2150
2.79M
    while (left > 0) {
2151
2.50M
        uint copy = min(left, sbuf_size);
2152
2153
2.50M
        r = gp_fread(buf, 1, copy, file);
2154
2.50M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
2.50M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
2.50M
        stream_write(s, buf, copy);
2160
2.50M
        left -= copy;
2161
2.50M
    }
2162
297k
    return 0;
2163
297k
}
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
125k
{
2170
125k
    int64_t r, left = count;
2171
2172
757k
    while (left > 0) {
2173
631k
        byte buf[sbuf_size];
2174
631k
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
631k
        int64_t end_pos = gp_ftell(file);
2176
2177
631k
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
631k
        r = gp_fread(buf, 1, copy, file);
2181
631k
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
631k
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
631k
        stream_write(s, buf, copy);
2188
631k
        sflush(s);
2189
631k
        left -= copy;
2190
631k
    }
2191
125k
    return 0;
2192
125k
}
2193
2194
/* ------ Pages ------ */
2195
2196
/* Get or assign the ID for a page. */
2197
/* Returns 0 if the page number is out of range. */
2198
int64_t
2199
pdf_page_id(gx_device_pdf * pdev, int page_num)
2200
230k
{
2201
230k
    cos_dict_t *Page;
2202
2203
230k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
230k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
75
        uint new_num_pages;
2207
75
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
75
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
75
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
75
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
75
                             "pdf_page_id(resize pages)");
2216
2217
75
        if (new_pages == 0)
2218
0
            return 0;
2219
75
        memset(&new_pages[pdev->num_pages], 0,
2220
75
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
75
        pdev->pages = new_pages;
2222
75
        pdev->num_pages = new_num_pages;
2223
75
    }
2224
230k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
57.1k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
57.1k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
57.1k
        Page->id = pdf_obj_forward_ref(pdev);
2230
57.1k
    }
2231
230k
    return Page->id;
2232
230k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
5.64M
{
2238
5.64M
    return &pdev->pages[pdev->next_page];
2239
5.64M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
13.8k
{
2245
13.8k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
13.8k
    return pdev->pages[pdev->next_page].Page;
2248
13.8k
}
2249
2250
/* Write saved page- or document-level information. */
2251
int
2252
pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr)
2253
0
{
2254
0
    if (pstr->data != 0) {
2255
0
        stream_write(pdev->strm, pstr->data, pstr->size);
2256
0
        gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
2257
0
                       "pdf_write_saved_string");
2258
0
        pstr->data = 0;
2259
0
    }
2260
0
    return 0;
2261
0
}
2262
2263
/* Open a page for writing. */
2264
int
2265
pdf_open_page(gx_device_pdf * pdev, pdf_context_t context)
2266
16.2M
{
2267
16.2M
    if (!is_in_page(pdev)) {
2268
56.2k
        int code;
2269
2270
56.2k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
56.2k
        code = pdfwrite_pdf_open_document(pdev);
2273
56.2k
        if (code < 0)
2274
0
            return code;
2275
56.2k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
16.2M
    return pdf_open_contents(pdev, context);
2278
16.2M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
149k
{
2284
149k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
149k
    if (pdev->sbstack_depth <= bottom) {
2289
132k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
132k
        if (code < 0)
2292
0
            return code;
2293
132k
    }
2294
149k
    if (pdev->context > PDF_IN_STREAM) {
2295
5
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2296
2297
5
        if (code < 0)
2298
0
            return code;
2299
5
    }
2300
149k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
97.5k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
97.5k
        if (code < 0)
2304
0
            return code;
2305
97.5k
        code = pdf_remember_clip_path(pdev, NULL);
2306
97.5k
        if (code < 0)
2307
0
            return code;
2308
97.5k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
97.5k
    }
2310
149k
    return 0;
2311
149k
}
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
42.9k
{
2323
42.9k
    int major = (int)(gs_revision / 1000);
2324
42.9k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
42.9k
    int patch = gs_revision % 10;
2326
2327
42.9k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
42.9k
}
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
142k
{
2335
142k
    stream *s = pdev->strm;
2336
2337
142k
    if (before)
2338
140k
        stream_puts(s, before);
2339
142k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
142k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
142k
    if (after)
2342
142k
        stream_puts(s, after);
2343
142k
}
2344
2345
/*
2346
 * Write a name, with escapes for unusual characters.  Since we only support
2347
 * PDF 1.2 and above, we can use an escape sequence for anything except a
2348
 * null <00>, and the machinery for selecting the put_name_chars procedure
2349
 * depending on CompatibilityLevel is no longer needed.
2350
 */
2351
static int
2352
pdf_put_name_chars_1_2(stream *s, const byte *nstr, uint size)
2353
3.59M
{
2354
3.59M
    uint i;
2355
2356
19.3M
    for (i = 0; i < size; ++i) {
2357
15.7M
        uint c = nstr[i];
2358
15.7M
        char hex[4];
2359
2360
15.7M
        switch (c) {
2361
15.7M
            default:
2362
15.7M
                if (c >= 0x21 && c <= 0x7e) {
2363
15.7M
                    stream_putc(s, (byte)c);
2364
15.7M
                    break;
2365
15.7M
                }
2366
                /* falls through */
2367
4.89k
            case '#':
2368
4.90k
            case '%':
2369
4.91k
            case '(': case ')':
2370
4.91k
            case '<': case '>':
2371
4.93k
            case '[': case ']':
2372
4.94k
            case '{': case '}':
2373
4.96k
            case '/':
2374
4.96k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
4.96k
                stream_puts(s, hex);
2376
4.96k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
15.7M
        }
2380
15.7M
    }
2381
3.59M
    return 0;
2382
3.59M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
3.59M
{
2386
3.59M
    return &pdf_put_name_chars_1_2;
2387
3.59M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
3.58M
{
2391
3.58M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
3.58M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
3.58M
{
2396
3.58M
    stream_putc(pdev->strm, '/');
2397
3.58M
    return pdf_put_name_chars(pdev, nstr, size);
2398
3.58M
}
2399
2400
/* Write an encoded string with encryption. */
2401
static int
2402
pdf_encrypt_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2403
0
{
2404
0
    stream sinp, sstr, sout;
2405
0
    stream_PSSD_state st;
2406
0
    stream_state so;
2407
0
    byte buf[100], bufo[100];
2408
0
    stream_arcfour_state sarc4;
2409
2410
0
    if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
2411
        /* The interface can't pass an error. */
2412
0
        stream_write(pdev->strm, str, size);
2413
0
        return size;
2414
0
    }
2415
0
    s_init(&sinp, NULL);
2416
0
    sread_string(&sinp, str + 1, size);
2417
0
    s_init(&sstr, NULL);
2418
0
    sstr.close_at_eod = false;
2419
0
    s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
2420
0
    s_init_filter(&sstr, (stream_state *)&st, buf, sizeof(buf), &sinp);
2421
0
    s_init(&sout, NULL);
2422
0
    s_init_state(&so, &s_PSSE_template, NULL);
2423
0
    s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
2424
0
    stream_putc(pdev->strm, '(');
2425
0
    for (;;) {
2426
0
        uint n;
2427
0
        int code = sgets(&sstr, buf, sizeof(buf), &n);
2428
2429
0
        if (n > 0) {
2430
0
            s_arcfour_process_buffer(&sarc4, buf, n);
2431
0
            stream_write(&sout, buf, n);
2432
0
        }
2433
0
        if (code == EOFC)
2434
0
            break;
2435
0
        if (code < 0 || n < sizeof(buf)) {
2436
            /* The interface can't pass an error. */
2437
0
            break;
2438
0
        }
2439
0
    }
2440
    /* Another case where we use sclose() and not sclose_filters(), because the
2441
     * buffer we supplied to s_init_filter is a heap based C object, so we
2442
     * must not free it.
2443
     */
2444
0
    sclose(&sout); /* Writes ')'. */
2445
0
    return (int)stell(&sinp) + 1;
2446
0
}
2447
2448
/* Write an encoded string with possible encryption. */
2449
static int
2450
pdf_put_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2451
187k
{
2452
187k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2453
187k
        stream_write(pdev->strm, str, size);
2454
187k
        return 0;
2455
187k
    } else
2456
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2457
187k
}
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
69.4k
{
2462
69.4k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
69.4k
        int i, oct, width = 0;
2464
69.4k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
69.4k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
69.4k
            stream_write(pdev->strm, "\n", 1);
2468
69.4k
        stream_write(pdev->strm, "<", 1);
2469
69.4k
        width++;
2470
2.16M
        for (i = 1; i < size - 1; i++) {
2471
2.09M
            if (str[i] == '\\') {
2472
135k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2473
134k
                    oct = (str[i+1] - 0x30) * 64;
2474
134k
                    oct += (str[i+2] - 0x30) *8;
2475
134k
                    oct += str[i+3] - 0x30;
2476
134k
                    i+=3;
2477
134k
                } else {
2478
769
                    switch (str[++i]) {
2479
8
                        case 'b' :
2480
8
                            oct = 8;
2481
8
                            break;
2482
624
                        case 't' :
2483
624
                            oct = 9;
2484
624
                            break;
2485
40
                        case 'n' :
2486
40
                            oct = 10;
2487
40
                            break;
2488
35
                        case 'f' :
2489
35
                            oct = 12;
2490
35
                            break;
2491
8
                        case 'r' :
2492
8
                            oct = 13;
2493
8
                            break;
2494
54
                        default:
2495
54
                            oct = str[i];
2496
54
                            break;
2497
769
                    }
2498
769
                }
2499
135k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2500
1.02k
                    stream_write(pdev->strm, "\n", 1);
2501
1.02k
                    width = 0;
2502
1.02k
                }
2503
135k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2504
135k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2505
135k
                width += 2;
2506
1.96M
            } else {
2507
1.96M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
223
                    stream_write(pdev->strm, "\n", 1);
2509
223
                    width = 0;
2510
223
                }
2511
1.96M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
1.96M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
1.96M
                width += 2;
2514
1.96M
            }
2515
2.09M
        }
2516
69.4k
        stream_write(pdev->strm, ">", 1);
2517
69.4k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
69.4k
            stream_write(pdev->strm, "\n", 1);
2519
69.4k
        return 0;
2520
69.4k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
69.4k
}
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
127k
{
2562
127k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
127k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
660
            stream_putc(pdev->strm, (byte)'\n');
2565
660
            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
660
            } else {
2606
660
                stream_write(pdev->strm, vstr, size);
2607
660
            }
2608
126k
        } else {
2609
126k
            stream_write(pdev->strm, vstr, size);
2610
126k
        }
2611
127k
    } 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
127k
    return 0;
2627
127k
}
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
8.76M
{
2637
8.76M
    psdf_write_string(pdev->strm, str, size,
2638
8.76M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
8.76M
    return 0;
2640
8.76M
}
2641
2642
/* Write a value, treating names specially. */
2643
int
2644
pdf_write_value(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2645
5.27M
{
2646
5.27M
    if (size > 0 && vstr[0] == '/')
2647
3.03M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
2.24M
    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.24M
    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.24M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
106k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
2.13M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
21.3k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
2.11M
    else if (size > 1 && vstr[0] == '(') {
2657
256k
        if (pdev->ForOPDFRead)
2658
69.4k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2659
187k
        else
2660
187k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2661
256k
    }
2662
1.85M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
1.85M
    stream_write(pdev->strm, vstr, size);
2665
1.85M
    return 0;
2666
5.27M
}
2667
2668
/* Store filters for a stream. */
2669
/* Currently this only saves parameters for CCITTFaxDecode. */
2670
int
2671
pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
2672
                const pdf_filter_names_t *pfn)
2673
427k
{
2674
427k
    const char *filter_name = 0;
2675
427k
    bool binary_ok = true;
2676
427k
    stream *fs = s;
2677
427k
    cos_dict_t *decode_parms = 0;
2678
427k
    int code;
2679
2680
1.49M
    for (; fs != 0; fs = fs->strm) {
2681
1.07M
        const stream_state *st = fs->state;
2682
1.07M
        const stream_template *templat = st->templat;
2683
2684
1.07M
#define TEMPLATE_IS(atemp)\
2685
6.08M
  (templat->process == (atemp).process)
2686
1.07M
        if (TEMPLATE_IS(s_A85E_template))
2687
166k
            binary_ok = false;
2688
904k
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
89.7k
            cos_param_list_writer_t writer;
2690
89.7k
            stream_CF_state cfs;
2691
2692
89.7k
            decode_parms =
2693
89.7k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
89.7k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
89.7k
            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
89.7k
            cfs = *(const stream_CF_state *)st;
2702
89.7k
            if (cfs.EndOfBlock)
2703
13.0k
                cfs.Rows = 0;
2704
89.7k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
89.7k
            filter_name = pfn->CCITTFaxDecode;
2706
814k
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
11.7k
            filter_name = pfn->DCTDecode;
2708
802k
        else if (TEMPLATE_IS(s_zlibE_template))
2709
142k
            filter_name = pfn->FlateDecode;
2710
660k
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
660k
        else if (TEMPLATE_IS(s_LZWE_template))
2713
69.6k
            filter_name = pfn->LZWDecode;
2714
590k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
14.2k
            const stream_PNGP_state *const ss =
2717
14.2k
                (const stream_PNGP_state *)st;
2718
2719
14.2k
            decode_parms =
2720
14.2k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
14.2k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
14.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
14.2k
                                         ss->Predictor));
2725
14.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
14.2k
                                         ss->Columns));
2727
14.2k
            if (ss->Colors != 1)
2728
14.2k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
14.2k
                                             ss->Colors));
2730
14.2k
            if (ss->BitsPerComponent != 8)
2731
14.2k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
14.2k
                                             "/BitsPerComponent",
2733
14.2k
                                             ss->BitsPerComponent));
2734
576k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
1.07M
#undef TEMPLATE_IS
2737
1.07M
    }
2738
427k
    if (filter_name) {
2739
313k
        if (binary_ok) {
2740
177k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
177k
            if (decode_parms)
2742
177k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
177k
                                                COS_OBJECT(decode_parms)));
2744
177k
        } else {
2745
135k
            cos_array_t *pca =
2746
135k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
135k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
135k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
135k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
135k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
135k
                                            COS_OBJECT(pca)));
2754
135k
            if (decode_parms) {
2755
83.2k
                pca = cos_array_alloc(pdev,
2756
83.2k
                                      "pdf_put_image_filters(DecodeParms)");
2757
83.2k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
83.2k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
83.2k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
83.2k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
83.2k
                                                COS_OBJECT(pca)));
2763
83.2k
            }
2764
135k
        }
2765
313k
    } else if (!binary_ok)
2766
114k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
427k
    return 0;
2768
427k
}
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
188k
{
2774
188k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
126k
                    &s_LZWE_template : &s_zlibE_template);
2776
188k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
188k
                                     "pdf_write_function");
2778
2779
188k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
188k
    if (templat->set_defaults)
2782
188k
        templat->set_defaults(st);
2783
188k
    return psdf_encode_binary(pbw, templat, st);
2784
188k
}
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
221k
{
2817
221k
    stream *s = pdev->strm;
2818
221k
    int options = orig_options;
2819
221k
#define USE_ASCII85 1
2820
393k
#define USE_FLATE 2
2821
221k
#define USE_BROTLI 4
2822
221k
    static const char *const fnames[6] = {
2823
221k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
221k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
221k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
221k
    };
2827
221k
    static const char *const fnames1_2[6] = {
2828
221k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
221k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
221k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
221k
    };
2832
221k
    int filters = 0;
2833
221k
    int code;
2834
2835
221k
    if (options & DATA_STREAM_COMPRESS) {
2836
171k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
171k
        } else {
2840
171k
            filters |= USE_FLATE;
2841
171k
            options |= DATA_STREAM_BINARY;
2842
171k
        }
2843
171k
    }
2844
221k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
38.0k
        filters |= USE_ASCII85;
2846
221k
    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
221k
    if (options & DATA_STREAM_ENCRYPT) {
2860
19.9k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
19.9k
        if (code < 0)
2862
0
            return code;
2863
19.9k
        pdev->strm = s;
2864
19.9k
        pdw->encrypted = true;
2865
19.9k
    } else
2866
201k
        pdw->encrypted = false;
2867
221k
    if (options & DATA_STREAM_BINARY) {
2868
171k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
171k
        if (code < 0)
2870
0
            return code;
2871
171k
    } else {
2872
49.4k
        code = 0;
2873
49.4k
        pdw->binary.target = pdev->strm;
2874
49.4k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
49.4k
        pdw->binary.strm = pdev->strm;
2876
49.4k
    }
2877
221k
    pdw->start = stell(s);
2878
221k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
221k
    else
2881
221k
        if (filters & USE_FLATE)
2882
171k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
221k
    return code;
2884
221k
#undef USE_ASCII85
2885
221k
#undef USE_FLATE
2886
221k
#undef USE_BROTLI
2887
221k
}
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
33.9k
{   int code;
2893
    /* object_id is an unused rudiment from the old code,
2894
       when the encription was applied when creating the stream.
2895
       The new code encrypts than copying stream from the temporary file. */
2896
33.9k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
33.9k
    pdw->binary.target = pdev->strm;
2898
33.9k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
33.9k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
33.9k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
33.9k
                options);
2902
33.9k
    if (object_id != 0)
2903
169
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
33.9k
    pdw->binary.strm = pdev->strm;
2905
33.9k
    return code;
2906
33.9k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
6.42k
{   int code;
2912
2913
6.42k
    code = pdf_close_aside(pdw->pdev);
2914
6.42k
    if (code < 0)
2915
0
        return code;
2916
6.42k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
6.42k
    if (code < 0)
2918
0
        return code;
2919
6.42k
    return 0;
2920
6.42k
}
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
7.98k
{
2929
7.98k
    if (pranges == NULL)
2930
7.98k
        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
26.1k
{
2967
26.1k
    gs_function_info_t info;
2968
26.1k
    cos_param_list_writer_t rlist;
2969
26.1k
    pdf_resource_t *pres;
2970
26.1k
    cos_object_t *pcfn;
2971
26.1k
    cos_dict_t *pcd;
2972
26.1k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
26.1k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
26.1k
    *ppres = pres;
2979
26.1k
    pcfn = pres->object;
2980
26.1k
    gs_function_get_info(pfn, &info);
2981
26.1k
    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
26.1k
    if (info.DataSource != 0) {
2994
17.1k
        psdf_binary_writer writer;
2995
17.1k
        stream *save = pdev->strm;
2996
17.1k
        cos_stream_t *pcos;
2997
17.1k
        stream *s;
2998
2999
17.1k
        cos_become(pcfn, cos_type_stream);
3000
17.1k
        pcos = (cos_stream_t *)pcfn;
3001
17.1k
        pcd = cos_stream_dict(pcos);
3002
17.1k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
17.1k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
17.1k
        pdev->strm = s;
3006
17.1k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
17.1k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
17.1k
            )
3009
16.4k
            code = pdf_flate_binary(pdev, &writer);
3010
17.1k
        if (code >= 0) {
3011
17.1k
            static const pdf_filter_names_t fnames = {
3012
17.1k
                PDF_FILTER_NAMES
3013
17.1k
            };
3014
3015
17.1k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
17.1k
        }
3017
17.1k
        if (code >= 0) {
3018
17.1k
            byte buf[100];    /* arbitrary */
3019
17.1k
            ulong pos;
3020
17.1k
            uint count;
3021
17.1k
            const byte *ptr;
3022
3023
222k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
205k
                count = min(sizeof(buf), info.data_size - pos);
3025
205k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
205k
                                        &ptr);
3027
205k
                stream_write(writer.strm, ptr, count);
3028
205k
            }
3029
17.1k
            code = psdf_end_binary(&writer);
3030
17.1k
            s_close_filters(&s, s->strm);
3031
17.1k
        }
3032
17.1k
        pdev->strm = save;
3033
17.1k
        if (code < 0)
3034
0
            return code;
3035
17.1k
    } else {
3036
9.00k
        cos_become(pcfn, cos_type_dict);
3037
9.00k
        pcd = (cos_dict_t *)pcfn;
3038
9.00k
    }
3039
26.1k
    if (info.Functions != 0) {
3040
1.77k
        cos_array_t *functions =
3041
1.77k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.77k
        cos_value_t v;
3043
3044
1.77k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.77k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.77k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.77k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.77k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.77k
    }
3054
26.1k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
26.1k
    if (code < 0)
3056
0
        return code;
3057
26.1k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
26.1k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
19.9k
{
3062
19.9k
    return true;
3063
19.9k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
26.1k
{
3067
26.1k
    pdf_resource_t *pres;
3068
26.1k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
26.1k
    if (code < 0)
3071
0
        return code;
3072
26.1k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
26.1k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
26.1k
    if (code < 0)
3077
0
        return code;
3078
26.1k
    pres->where_used |= pdev->used_mask;
3079
26.1k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
26.1k
    return 0;
3081
26.1k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.77k
{
3085
1.77k
    int i, code = 0;
3086
1.77k
    cos_value_t v;
3087
3088
7.98k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
6.20k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
6.20k
            (code = cos_array_add(pca, &v)) < 0
3091
6.20k
            ) {
3092
0
            break;
3093
0
        }
3094
6.20k
    }
3095
1.77k
    return code;
3096
1.77k
}
3097
3098
/* Write a Function object. */
3099
int
3100
pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, int64_t *pid)
3101
11.9k
{
3102
11.9k
    cos_value_t value;
3103
11.9k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
11.9k
    if (code < 0)
3106
0
        return code;
3107
11.9k
    *pid = value.contents.object->id;
3108
11.9k
    return 0;
3109
11.9k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
6.14k
{
3114
6.14k
    char key[] = "/Functions";
3115
6.14k
    cos_value_t *v, v2;
3116
3117
6.14k
    if (cos_type(pco) == cos_type_dict) {
3118
2.34k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
2.34k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
566
            if (cos_type(v->contents.object) == cos_type_array){
3121
566
                int code=0;
3122
3.00k
                while (code == 0) {
3123
2.44k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
2.44k
                }
3125
566
            }
3126
566
        }
3127
2.34k
    }
3128
6.14k
    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.14k
    return 0;
3145
6.14k
}
3146
3147
/* Write a FontBBox dictionary element. */
3148
int
3149
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3150
27.5k
{
3151
27.5k
    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
27.5k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
27.5k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
27.5k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
27.5k
             pbox->p.x, pbox->p.y, x, y);
3163
27.5k
    return 0;
3164
27.5k
}
3165
3166
/* Write a FontBBox dictionary element using floats for the values. */
3167
int
3168
pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
3169
3.01k
{
3170
3.01k
    stream *s = pdev->strm;
3171
    /*
3172
     * AR 4 doesn't like fonts with empty FontBBox, which
3173
     * happens when the font contains only space characters.
3174
     * Small bbox causes AR 4 to display a hairline. So we use
3175
     * the full BBox.
3176
     */
3177
3.01k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
3.01k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
3.01k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
3.01k
             pbox->p.x, pbox->p.y, x, y);
3182
3.01k
    return 0;
3183
3.01k
}