Coverage Report

Created: 2026-04-01 07:17

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
25.4k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
25.4k
#  define Flate_filter_name "FlateDecode"
61
25.4k
#  define Flate_filter_template s_zlibE_template
62
25.4k
#  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.92M
  BEGIN if ((code = (expr)) < 0) return code; END
78
79
/* GC descriptors */
80
extern_st(st_pdf_color_space);
81
extern_st(st_pdf_font_resource);
82
extern_st(st_pdf_char_proc);
83
extern_st(st_pdf_font_descriptor);
84
public_st_pdf_resource();
85
private_st_pdf_x_object();
86
private_st_pdf_pattern();
87
88
/* ---------------- Utilities ---------------- */
89
90
#ifdef PS2WRITE_USES_ROMFS
91
/*
92
 * Strip whitespace and comments from a line of PostScript code as possible.
93
 * Return a pointer to any string that remains, or NULL if none.
94
 * Note that this may store into the string.
95
 */
96
/* This function copied from geninit.c . */
97
static char *
98
doit(char *line, bool intact)
99
{
100
    char *str = line;
101
    char *from;
102
    char *to;
103
    int in_string = 0;
104
105
    if (intact)
106
        return str;
107
    while (*str == ' ' || *str == '\t')   /* strip leading whitespace */
108
        ++str;
109
    if (*str == 0)    /* all whitespace */
110
        return NULL;
111
    if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
112
        return str;
113
    if (str[0] == '%')    /* comment line */
114
        return NULL;
115
    /*
116
     * Copy the string over itself removing:
117
     *  - All comments not within string literals;
118
     *  - Whitespace adjacent to '[' ']' '{' '}';
119
     *  - Whitespace before '/' '(' '<';
120
     *  - Whitespace after ')' '>'.
121
     */
122
    for (to = from = str; (*to = *from) != 0; ++from, ++to) {
123
        switch (*from) {
124
            case '%':
125
                if (!in_string)
126
                    break;
127
                continue;
128
            case ' ':
129
            case '\t':
130
                if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
131
                    --to;
132
                continue;
133
            case '(':
134
            case '<':
135
            case '/':
136
            case '[':
137
            case ']':
138
            case '{':
139
            case '}':
140
                if (to > str && !in_string && strchr(" \t", to[-1]))
141
                    *--to = *from;
142
                if (*from == '(')
143
                    ++in_string;
144
                continue;
145
            case ')':
146
                --in_string;
147
                continue;
148
            case '\\':
149
                if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
150
                    *++to = *++from;
151
                continue;
152
            default:
153
                continue;
154
        }
155
        break;
156
    }
157
    /* Strip trailing whitespace. */
158
    while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
159
        --to;
160
    *to = 0;
161
    return str;
162
}
163
164
static int
165
copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
166
{
167
    stream *f;
168
    char buf[1024], *p, *q  = buf;
169
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
170
    bool skipping = false;
171
172
    f = sfopen(fname, "r", s->memory);
173
    if (f == NULL)
174
        return_error(gs_error_undefinedfilename);
175
    n = sfread(buf, 1, m, f);
176
    buf[n] = 0;
177
    do {
178
        if (*q == '\r' || *q == '\n') {
179
            q++;
180
            continue;
181
        }
182
        p = strchr(q, '\r');
183
        if (p == NULL)
184
            p = strchr(q, '\n');
185
        if (p == NULL) {
186
            if (n < m)
187
                p = buf + n;
188
            else {
189
                strcpy(buf, q);
190
                l = strlen(buf);
191
                m = sizeof(buf) - 1 - l;
192
                if (!m) {
193
                    sfclose(f);
194
                    emprintf1(s->memory,
195
                              "The procset %s contains a too long line.",
196
                              fname);
197
                    return_error(gs_error_ioerror);
198
                }
199
                n = sfread(buf + l, 1, m, f);
200
                n += l;
201
                m += l;
202
                buf[n] = 0;
203
                q = buf;
204
                continue;
205
            }
206
        }
207
        *p = 0;
208
        if (q[0] == '%')
209
            l = 0;
210
        else {
211
            q = doit(q, false);
212
            if (q == NULL)
213
                l = 0;
214
            else
215
                l = strlen(q);
216
        }
217
        if (l) {
218
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
219
                skipping = true;
220
            if (!skipping) {
221
                outl += l + 1;
222
                if (outl > 100) {
223
                    q[l] = '\r';
224
                    outl = 0;
225
                } else
226
                    q[l] = ' ';
227
                stream_write(s, q, l + 1);
228
            }
229
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
230
                skipping = false;
231
        }
232
        q = p + 1;
233
    } while (n == m || q < buf + n);
234
    if (outl)
235
        stream_write(s, "\r", 1);
236
    sfclose(f);
237
    return 0;
238
}
239
240
static int
241
copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
242
{
243
    stream *f;
244
    char buf[1024], *p, *q  = buf;
245
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
246
    bool skipping = false;
247
248
    f = sfopen(fname, "r", s->memory);
249
    if (f == NULL)
250
        return_error(gs_error_undefinedfilename);
251
    n = sfread(buf, 1, m, f);
252
    buf[n] = 0;
253
    do {
254
        if (*q == '\r' || *q == '\n') {
255
            q++;
256
            continue;
257
        }
258
        p = strchr(q, '\r');
259
        if (p == NULL)
260
            p = strchr(q, '\n');
261
        if (p == NULL) {
262
            if (n < m)
263
                p = buf + n;
264
            else {
265
                strcpy(buf, q);
266
                l = strlen(buf);
267
                m = sizeof(buf) - 1 - l;
268
                if (!m) {
269
                    sfclose(f);
270
                    emprintf1(s->memory,
271
                              "The procset %s contains a too long line.",
272
                              fname);
273
                    return_error(gs_error_ioerror);
274
                }
275
                n = sfread(buf + l, 1, m, f);
276
                n += l;
277
                m += l;
278
                buf[n] = 0;
279
                q = buf;
280
                continue;
281
            }
282
        }
283
        *p = 0;
284
        if (q[0] == '%')
285
            l = 0;
286
        else {
287
            q = doit(q, false);
288
            if (q == NULL)
289
                l = 0;
290
            else
291
                l = strlen(q);
292
        }
293
        if (l) {
294
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
295
                skipping = true;
296
            if (!skipping) {
297
                outl += l + 1;
298
                if (outl > 100) {
299
                    q[l] = '\n';
300
                    outl = 0;
301
                } else
302
                    q[l] = '\n';
303
                stream_write(s, q, l + 1);
304
            }
305
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
306
                skipping = false;
307
        }
308
        q = p + 1;
309
    } while (n == m || q < buf + n);
310
    if (outl)
311
        stream_write(s, "\r", 1);
312
    sfclose(f);
313
    return 0;
314
}
315
#endif
316
317
static int write_opdfread(stream *s)
318
24.0k
{
319
24.0k
    int index = 0;
320
321
97.6M
    do {
322
97.6M
        if (opdfread_ps[index] == 0x00)
323
24.0k
            break;
324
97.6M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
97.6M
        index++;
326
97.6M
    } while (1);
327
24.0k
    return 0;
328
24.0k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
24.0k
{
332
24.0k
    int index = 0;
333
334
939k
    do {
335
939k
        if (gs_mro_e_ps[index] == 0x00)
336
24.0k
            break;
337
915k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
915k
        index++;
339
915k
    } while (1);
340
341
24.0k
    if (HaveTrueTypes) {
342
24.0k
        char Buffer[256];
343
24.0k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
24.0k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
24.0k
        stream_write(s, Buffer, strlen(Buffer));
347
101M
        while (entry->Glyph) {
348
101M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
101M
            stream_write(s, Buffer, strlen(Buffer));
350
101M
            entry++;
351
101M
        };
352
24.0k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
24.0k
        stream_write(s, Buffer, strlen(Buffer));
354
355
24.0k
        index = 0;
356
866k
        do {
357
866k
            if (gs_mgl_e_ps[index] == 0x00)
358
24.0k
                break;
359
842k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
842k
            index++;
361
842k
        } while (1);
362
24.0k
    }
363
24.0k
    return 0;
364
24.0k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
24.0k
{
369
24.0k
    int code;
370
371
24.0k
    code = write_opdfread(s);
372
24.0k
    if (code < 0)
373
0
        return code;
374
375
24.0k
    code = write_tt_encodings(s, HaveTrueTypes);
376
24.0k
    return code;
377
378
24.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
43.5k
{
455
43.5k
    const char * const *argv = NULL;
456
43.5k
    const char *arg;
457
43.5k
    int towrite, length, i, j, argc;
458
459
43.5k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
43.5k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
43.5k
    length = 12;
463
827k
    for (i=0;i < argc; i++) {
464
783k
        arg = argv[i];
465
466
783k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
783k
        } else {
470
783k
            stream_write(s, (byte *)" ", 1);
471
783k
            length++;
472
783k
        }
473
474
783k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
783k
        else
477
783k
            towrite = strlen(arg);
478
479
783k
        length += towrite;
480
481
9.46M
        for (j=0;j < towrite;j++) {
482
8.68M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
8.68M
            } else {
485
8.68M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
8.68M
                } else {
488
8.68M
                    stream_write(s, (byte *)&arg[j], 1);
489
8.68M
                }
490
8.68M
            }
491
8.68M
        }
492
783k
    }
493
43.5k
    stream_write(s, (byte *)"\n", 1);
494
43.5k
    return 0;
495
43.5k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
24.0k
{
499
24.0k
    stream *s = pdev->strm;
500
501
24.0k
    if (pdev->ForOPDFRead) {
502
24.0k
        char cre_date_time[41];
503
24.0k
        int code, status, cre_date_time_len;
504
24.0k
        char BBox[256];
505
506
24.0k
        if (pdev->Eps2Write)
507
12.0k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
12.0k
        else
509
12.0k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
24.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
24.0k
        {
515
24.0k
            int pagecount = 1, j;
516
24.0k
            double urx=0, ury=0;
517
518
409k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
385k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
416k
                for (; pres != 0; pres = pres->next)
522
30.7k
                    if ((!pres->named || pdev->ForOPDFRead)
523
30.7k
                        && !pres->object->written) {
524
30.7k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
30.7k
                        if (ceil(page->MediaBox.x) > urx)
526
24.3k
                            urx = ceil(page->MediaBox.x);
527
30.7k
                        if (ceil(page->MediaBox.y) > ury)
528
24.1k
                            ury = ceil(page->MediaBox.y);
529
30.7k
                        pagecount++;
530
30.7k
                    }
531
385k
            }
532
24.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
17.8k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
6.18k
            else
535
6.18k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
536
24.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
24.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
17.8k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
6.18k
            else
540
6.18k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
541
24.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
24.0k
        }
543
24.0k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
24.0k
        cre_date_time[cre_date_time_len] = 0;
545
24.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
24.0k
                pdev->dname);
547
24.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
24.0k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
24.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
24.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
24.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
24.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
24.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
24.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
24.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
24.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
24.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
24.0k
        stream_puts(s, "10 dict dup begin\n");
573
24.0k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
24.0k
        if (pdev->Eps2Write) {
575
12.0k
            stream_puts(s, "/SetPageSize false def\n");
576
12.0k
            stream_puts(s, "/EPS2Write true def\n");
577
12.0k
        } else {
578
12.0k
            if (pdev->SetPageSize)
579
12.0k
                stream_puts(s, "/SetPageSize true def\n");
580
12.0k
            stream_puts(s, "/EPS2Write false def\n");
581
12.0k
        }
582
24.0k
        stream_puts(s, "end\n");
583
584
24.0k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
24.0k
        if (code < 0)
586
0
            return code;
587
24.0k
        status = s_close_filters(&s, pdev->strm);
588
24.0k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
24.0k
        stream_puts(s, "\n");
591
24.0k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
24.0k
    }
593
24.0k
    return 0;
594
24.0k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
541k
{
600
541k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
541k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
129k
        stream *s = pdev->strm;
605
129k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
129k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
129k
        if (pdev->ForOPDFRead) {
609
109k
            int code, status;
610
109k
            char BBox[256];
611
109k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
109k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
109k
            if (pdev->ProduceDSC)
615
109k
                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
109k
        }
660
129k
        if (!(pdev->ForOPDFRead)) {
661
19.4k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
19.4k
            if (pdev->binary_ok)
663
19.4k
                stream_puts(s, "%\307\354\217\242\n");
664
19.4k
            pdfwrite_write_args_comment(pdev, s);
665
19.4k
        }
666
129k
    }
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
541k
    if (!pdev->params.CompressPages)
677
252k
        pdev->compression = pdf_compress_none;
678
288k
    else {
679
288k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
288k
        else
682
288k
            pdev->compression = pdf_compress_Flate;
683
288k
    }
684
541k
    return 0;
685
541k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
661k
{
693
661k
    return (pdev->next_id)++;
694
661k
}
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.48M
{
706
1.48M
    stream *s = pdev->strm;
707
1.48M
    gs_offset_t pos = stell(s);
708
709
1.48M
    if (s == pdev->asides.strm)
710
281k
        pos |= ASIDES_BASE_POSITION;
711
1.48M
    return pos;
712
1.48M
}
713
714
/* Allocate an ID for a future object.
715
 * pdf_obj_ref below allocates an object and assigns it a position assuming
716
 * it will be written at the current location in the PDF file. But we want
717
 * some way to notice when writing the PDF file if some kinds of objects have
718
 * never been written out (eg pages allocated for /Dest targets). Setting the
719
 * position to 0 is good, because we always write the header, which is 15
720
 * bytes. However, pdf_obj_ref is so wisely used its no longer possible to
721
 * tell whether writing the object out has been deferred (in which case the
722
 * pos is updated by pdf_open_obj) or not. Adding this function to allow us
723
 * to create an object whose writing is definitely deferred, in which case
724
 * we know it will be updated later. This allows setting the pos to 0,
725
 * and we can detect that when writing the xref, and set the object to
726
 * 'unused'.
727
 */
728
int64_t pdf_obj_forward_ref(gx_device_pdf * pdev)
729
75.7k
{
730
75.7k
    int64_t id = pdf_next_id(pdev);
731
75.7k
    gs_offset_t pos = 0;
732
733
75.7k
    if (pdev->doubleXref) {
734
75.7k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
75.7k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
75.7k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
75.7k
    return id;
740
75.7k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
585k
{
746
585k
    int64_t id = pdf_next_id(pdev);
747
585k
    gs_offset_t pos = 0;
748
749
585k
    if (pdev->doubleXref) {
750
585k
        if (pdev->strm == pdev->ObjStm.strm)
751
25.5k
            pos = pdev->ObjStm_id;
752
560k
        else
753
560k
            pos = 0;
754
585k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
585k
        if (pdev->strm == pdev->ObjStm.strm)
756
25.5k
            pos = pdev->NumObjStmObjects;
757
560k
        else
758
560k
            pos = pdf_stell(pdev);
759
585k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
585k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
585k
    return id;
766
585k
}
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
44
{
780
44
    gp_file *tfile = pdev->xref.file;
781
44
    int64_t tpos = gp_ftell(tfile);
782
44
    gs_offset_t pos = 0;
783
784
44
    if (pdev->doubleXref) {
785
44
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
44
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
44
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
44
    }
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
44
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
44
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
44
    return 0;
800
44
}
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
582k
{
806
582k
    stream *s = pdev->strm;
807
808
582k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
582k
    if (id <= 0) {
812
150k
        id = pdf_obj_ref(pdev);
813
432k
    } else {
814
432k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
432k
        gp_file *tfile = pdev->xref.file;
816
432k
        int64_t tpos = gp_ftell(tfile);
817
818
432k
        if (pdev->doubleXref) {
819
432k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
432k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
432k
            if (pdev->strm == pdev->ObjStm.strm)
823
162k
                fake_pos = pdev->ObjStm_id;
824
432k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
432k
            if (pdev->strm == pdev->ObjStm.strm)
826
162k
                pos = pdev->NumObjStmObjects;
827
432k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
432k
        } 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
432k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
432k
    }
837
582k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
211k
        switch(type) {
839
878
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
878
                break;
844
30.4k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
30.4k
                break;
847
1.93k
            case resourceColorSpace:
848
1.93k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
1.93k
                break;
850
10.1k
            case resourceExtGState:
851
10.1k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
10.1k
                break;
853
867
            case resourcePattern:
854
867
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
867
                break;
856
0
            case resourceShading:
857
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Shading obj_%"PRId64")\n", id);
858
0
                break;
859
0
            case resourceCIDFont:
860
24.2k
            case resourceFont:
861
                /* Ought to write the font name here */
862
24.2k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
24.2k
                break;
864
79.8k
            case resourceCharProc:
865
79.8k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
79.8k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
10.3k
            case resourceFontDescriptor:
871
10.3k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
10.3k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.12k
            case resourceFunction:
877
2.12k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.12k
                break;
879
5.14k
            case resourceEncoding:
880
5.14k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
5.14k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
11.6k
            case resourceHalftone:
886
11.6k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
11.6k
                break;
888
0
            case resourceLength:
889
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Length obj_%"PRId64")\n", id);
890
0
                break;
891
0
            case resourceSoftMaskDict:
892
                /* This should not be possible, not valid in PostScript */
893
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF SoftMask obj_%"PRId64")\n", id);
894
0
                break;
895
15
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
15
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
15
                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
24.0k
            default:
955
24.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
24.0k
                break;
957
211k
        }
958
211k
    }
959
582k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
398k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
582k
    return id;
962
582k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
25.4k
{
966
25.4k
    return pdf_open_obj(pdev, 0L, type);
967
25.4k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
582k
{
973
582k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
398k
        stream_puts(pdev->strm, "endobj\n");
975
582k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
211k
        switch(type) {
977
30.4k
            case resourcePage:
978
30.4k
                break;
979
181k
            default:
980
181k
            stream_puts(pdev->strm, "%%EndResource\n");
981
181k
            break;
982
211k
        }
983
211k
    }
984
582k
    return 0;
985
582k
}
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
120k
{
1034
120k
    gs_memory_t *mem = pdev->v_memory;
1035
120k
    stream_arcfour_state *ss;
1036
120k
    gs_md5_byte_t key[16];
1037
120k
    int code, keylength;
1038
1039
120k
    if (!pdev->KeyLength)
1040
120k
        return 0;
1041
0
    keylength = pdf_object_key(pdev, object_id, key);
1042
0
    ss = gs_alloc_struct(mem, stream_arcfour_state,
1043
0
                    s_arcfour_template.stype, "psdf_encrypt");
1044
0
    if (ss == NULL)
1045
0
        return_error(gs_error_VMerror);
1046
0
    code = s_arcfour_set_key(ss, key, keylength);
1047
0
    if (code < 0)
1048
0
        return code;
1049
0
    if (s_add_filter(s, &s_arcfour_template, (stream_state *)ss, mem) == 0)
1050
0
        return_error(gs_error_VMerror);
1051
0
    return 0;
1052
    /* IMPORTANT NOTE :
1053
       We don't encrypt streams written into temporary files,
1054
       because they can be used for comparizon
1055
       (for example, for merging equal images).
1056
       Instead that the encryption is applied in pdf_copy_data,
1057
       when the stream is copied to the output file.
1058
     */
1059
0
}
1060
1061
/* Enter stream context. */
1062
static int
1063
none_to_stream(gx_device_pdf * pdev)
1064
56.1k
{
1065
56.1k
    stream *s;
1066
56.1k
    int code;
1067
1068
56.1k
    if (pdev->contents_id != 0)
1069
9
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
56.1k
    pdev->compression_at_page_start = pdev->compression;
1071
56.1k
    if (pdev->ResourcesBeforeUsage) {
1072
30.7k
        pdf_resource_t *pres;
1073
1074
30.7k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
30.7k
                    true, pdev->params.CompressPages);
1076
30.7k
        if (code < 0)
1077
0
            return code;
1078
30.7k
        pdev->contents_id = pres->object->id;
1079
30.7k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
30.7k
        pdev->contents_pos = -1; /* inapplicable */
1081
30.7k
        s = pdev->strm;
1082
30.7k
    } else {
1083
25.4k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
25.4k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
25.4k
        s = pdev->strm;
1086
25.4k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
25.4k
        if (pdev->compression == pdf_compress_Flate) {
1088
25.4k
            if (pdev->binary_ok)
1089
25.4k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
25.4k
        }
1093
25.4k
        if (pdev->compression == pdf_compress_Brotli) {
1094
0
            if (pdev->binary_ok)
1095
0
                pprints1(s, "/Filter /%s", Brotli_filter_name);
1096
0
            else
1097
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Brotli_filter_name);
1098
0
        }
1099
25.4k
        stream_puts(s, ">>\nstream\n");
1100
25.4k
        pdev->contents_pos = pdf_stell(pdev);
1101
25.4k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
25.4k
        if (code < 0)
1103
0
            return code;
1104
25.4k
        pdev->strm = s;
1105
25.4k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
25.4k
            const stream_template *templat;
1107
25.4k
            stream *es;
1108
25.4k
            byte *buf;
1109
25.4k
            Flate_filter_state *st;
1110
1111
25.4k
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1112
0
                const stream_template *templat2 = &s_A85E_template;
1113
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1114
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1115
0
                                           "PDF contents buffer");
1116
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1117
0
                                templat2->stype, "PDF contents state");
1118
0
                if (as == 0 || ast == 0 || buf == 0)
1119
0
                    return_error(gs_error_VMerror);
1120
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1121
0
                           s_mode_write);
1122
0
                ast->memory = pdev->pdf_memory;
1123
0
                ast->templat = templat2;
1124
0
                as->state = (stream_state *) ast;
1125
0
                as->procs.process = templat2->process;
1126
0
                as->strm = s;
1127
0
                (*templat2->init) ((stream_state *) ast);
1128
0
                pdev->strm = s = as;
1129
0
            }
1130
25.4k
            templat = &Flate_filter_template;
1131
25.4k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
25.4k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
25.4k
                                       "PDF compression buffer");
1134
25.4k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
25.4k
                                 templat->stype, "PDF compression state");
1136
25.4k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
25.4k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
25.4k
                       s_mode_write);
1140
25.4k
            st->memory = pdev->pdf_memory;
1141
25.4k
            st->templat = templat;
1142
25.4k
            es->state = (stream_state *) st;
1143
25.4k
            es->procs.process = templat->process;
1144
25.4k
            es->strm = s;
1145
25.4k
            (*templat->set_defaults) ((stream_state *) st);
1146
25.4k
            code = (*templat->init) ((stream_state *) st);
1147
25.4k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
25.4k
            pdev->strm = s = es;
1152
25.4k
        }
1153
25.4k
        if (pdev->compression == pdf_compress_Brotli) { /* Set up the Brotli filter. */
1154
0
            const stream_template *templat;
1155
0
            stream *es;
1156
0
            byte *buf;
1157
0
            Brotli_filter_state *st;
1158
1159
0
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1160
0
                const stream_template *templat2 = &s_A85E_template;
1161
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1162
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1163
0
                                           "PDF contents buffer");
1164
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1165
0
                                templat2->stype, "PDF contents state");
1166
0
                if (as == 0 || ast == 0 || buf == 0)
1167
0
                    return_error(gs_error_VMerror);
1168
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1169
0
                           s_mode_write);
1170
0
                ast->memory = pdev->pdf_memory;
1171
0
                ast->templat = templat2;
1172
0
                as->state = (stream_state *) ast;
1173
0
                as->procs.process = templat2->process;
1174
0
                as->strm = s;
1175
0
                (*templat2->init) ((stream_state *) ast);
1176
0
                pdev->strm = s = as;
1177
0
            }
1178
0
            templat = &Brotli_filter_template;
1179
0
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1180
0
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1181
0
                                       "PDF compression buffer");
1182
0
            st = gs_alloc_struct(pdev->pdf_memory, Brotli_filter_state,
1183
0
                                 templat->stype, "PDF compression state");
1184
0
            if (es == 0 || st == 0 || buf == 0)
1185
0
                return_error(gs_error_VMerror);
1186
0
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1187
0
                       s_mode_write);
1188
0
            st->memory = pdev->pdf_memory;
1189
0
            st->templat = templat;
1190
0
            es->state = (stream_state *) st;
1191
0
            es->procs.process = templat->process;
1192
0
            es->strm = s;
1193
0
            (*templat->set_defaults) ((stream_state *) st);
1194
0
            code = (*templat->init) ((stream_state *) st);
1195
0
            if (code < 0) {
1196
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1197
0
                return code;
1198
0
            }
1199
0
            pdev->strm = s = es;
1200
0
        }
1201
25.4k
    }
1202
    /*
1203
     * Scale the coordinate system.  Use an extra level of q/Q for the
1204
     * sake of poorly designed PDF tools that assume that the contents
1205
     * stream restores the CTM.
1206
     */
1207
56.1k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
56.1k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
56.1k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
25.4k
        if (pdev->params.DefaultRenderingIntent != ri_Default) {
1212
0
            static const char *const ri_names[] = { psdf_ri_names };
1213
1214
0
            pprints1(s, "/%s ri\n",
1215
0
                     ri_names[(int)pdev->params.DefaultRenderingIntent]);
1216
0
        }
1217
25.4k
    }
1218
56.1k
    pdev->AR4_save_bug = false;
1219
56.1k
    return PDF_IN_STREAM;
1220
56.1k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
195k
{
1225
195k
    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
195k
    if (!pdev->clipped_text_pending) {
1233
195k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
195k
        if (code < 0)
1235
0
            return 0;
1236
195k
    }
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
195k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
195k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
195k
    pdev->procsets |= Text;
1248
195k
    code = pdf_from_stream_to_text(pdev);
1249
195k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
195k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
195k
{
1255
195k
    int code = pdf_from_string_to_text(pdev);
1256
1257
195k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
195k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
195k
{
1263
195k
    int code;
1264
1265
195k
    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
195k
    if (pdev->clipped_text_pending)
1273
18
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
18
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
195k
    else {
1276
195k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
195k
        if (code < 0)
1278
0
            return code;
1279
195k
        pdf_reset_text(pdev); /* because of Q */
1280
195k
    }
1281
195k
    return PDF_IN_STREAM;
1282
195k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
56.1k
{
1287
56.1k
    stream *s = pdev->strm;
1288
56.1k
    gs_offset_t length;
1289
56.1k
    int code;
1290
56.1k
    stream *target;
1291
56.1k
     char str[21];
1292
1293
56.1k
    if (pdev->ResourcesBeforeUsage) {
1294
30.7k
        int code = pdf_exit_substream(pdev);
1295
1296
30.7k
        if (code < 0)
1297
0
            return code;
1298
30.7k
    } else {
1299
25.4k
        if (pdev->vgstack_depth) {
1300
155
            code = pdf_restore_viewer_state(pdev, s);
1301
155
            if (code < 0)
1302
12
                return code;
1303
155
        }
1304
25.4k
        target = pdev->strm;
1305
1306
25.4k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
25.4k
            target = target->strm;
1308
25.4k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
25.4k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
25.4k
        s_close_filters(&pdev->strm, target);
1313
1314
25.4k
        s = pdev->strm;
1315
25.4k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
25.4k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
25.4k
        stream_puts(s, "endstream\n");
1319
25.4k
        pdf_end_obj(pdev, resourceStream);
1320
1321
25.4k
        if (pdev->WriteObjStms) {
1322
25.4k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
25.4k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
25.4k
            stream_puts(pdev->strm, str);
1325
25.4k
            pdf_end_separate(pdev, resourceLength);
1326
25.4k
        } else {
1327
9
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
9
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
9
            stream_puts(s, str);
1330
9
            pdf_end_obj(pdev, resourceLength);
1331
9
        }
1332
25.4k
    }
1333
56.1k
    return PDF_IN_NONE;
1334
56.1k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
11.8M
{
1340
11.8M
    int (*proc) (gx_device_pdf *);
1341
1342
12.5M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
697k
        int code = (*proc) (pdev);
1344
1345
697k
        if (code < 0)
1346
21
            return code;
1347
697k
        pdev->context = (pdf_context_t) code;
1348
697k
    }
1349
11.8M
    pdev->context = context;
1350
11.8M
    return 0;
1351
11.8M
}
1352
1353
/* Close the current contents part if we are in one. */
1354
int
1355
pdf_close_contents(gx_device_pdf * pdev, bool last)
1356
56.1k
{
1357
56.1k
    if (pdev->context == PDF_IN_NONE)
1358
7
        return 0;
1359
56.1k
    if (last) {     /* Exit from the clipping path gsave. */
1360
56.1k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
56.1k
        if (code < 0)
1363
0
            return code;
1364
56.1k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
56.1k
        pdf_close_text_contents(pdev);
1366
56.1k
    }
1367
56.1k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
56.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
163k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
163k
    pres->where_used = 0;
1386
163k
    if (pres->object) {
1387
163k
        pres->object->written = true;
1388
163k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
106k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
106k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
106k
            if (code < 0)
1393
0
                return code;
1394
106k
        }
1395
163k
        cos_release(pres->object, "pdf_cancel_resource");
1396
163k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
163k
        pres->object = 0;
1398
163k
    }
1399
163k
    return 0;
1400
163k
}
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
161k
{   /* fixme : optimize. */
1406
161k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
161k
    pdf_resource_t *pres;
1408
161k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
161k
    int i;
1410
1411
    /* since we're about to free the resource, we can just set
1412
       any of these references to null
1413
    */
1414
1.93M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
1.77M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
1.77M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
1.77M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
4
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
4
        }
1424
1.77M
    }
1425
1426
163k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
163k
        if (pres == pres1) {
1428
161k
            *pprev = pres->prev;
1429
161k
            break;
1430
161k
        }
1431
1432
161k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
161k
        pprev = pchain + i;
1434
163k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
163k
            if (pres == pres1) {
1436
161k
                *pprev = pres->next;
1437
161k
                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
161k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
161k
                return;
1444
161k
            }
1445
161k
    }
1446
161k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
41.2k
{
1451
41.2k
    return 1;
1452
41.2k
}
1453
1454
/* Substitute a resource with a same one. */
1455
/* NB we cannot substitute resources which have already had an
1456
   id assigned to them, because they already have an entry in the
1457
   xref table. If we want to substiute a resource then it should
1458
   have been allocated with an initial id of -1.
1459
   (see pdf_alloc_resource)
1460
*/
1461
int
1462
pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
1463
        pdf_resource_type_t rtype,
1464
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
1465
        bool write)
1466
97.2k
{
1467
97.2k
    pdf_resource_t *pres1 = *ppres;
1468
97.2k
    int code;
1469
1470
97.2k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
97.2k
    if (code < 0)
1472
0
        return code;
1473
97.2k
    if (code != 0) {
1474
61.1k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
61.1k
        if (code < 0)
1476
0
            return code;
1477
61.1k
        pdf_forget_resource(pdev, pres1, rtype);
1478
61.1k
        return 0;
1479
61.1k
    } else {
1480
36.1k
        if (pres1->object->id < 0)
1481
36.1k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
36.1k
        if (write) {
1483
20.6k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
20.6k
            if (code < 0)
1485
0
                return code;
1486
20.6k
            pres1->object->written = 1;
1487
20.6k
        }
1488
36.1k
        return 1;
1489
36.1k
    }
1490
97.2k
}
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
878k
{
1497
878k
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
878k
    pdf_resource_t **pprev = pchain;
1499
878k
    pdf_resource_t *pres;
1500
1501
1.99M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
1.89M
        if (pres->rid == rid) {
1503
777k
            if (pprev != pchain) {
1504
274k
                *pprev = pres->next;
1505
274k
                pres->next = *pchain;
1506
274k
                *pchain = pres;
1507
274k
            }
1508
777k
            return pres;
1509
777k
        }
1510
100k
    return 0;
1511
878k
}
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
25.4k
{
1517
25.4k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
25.4k
    pdf_resource_t *pres;
1519
25.4k
    int i;
1520
1521
419k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
2.54M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
2.15M
            if (pres->object && pres->object->id == id)
1524
1.65k
                return pres;
1525
2.15M
        }
1526
395k
    }
1527
23.7k
    return 0;
1528
25.4k
}
1529
1530
/* Find same resource. */
1531
int
1532
pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
1533
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
1534
126k
{
1535
126k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
126k
    pdf_resource_t *pres;
1537
126k
    cos_object_t *pco0 = (*ppres)->object;
1538
126k
    int i;
1539
1540
1.14M
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
2.12M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
1.10M
            if (*ppres != pres) {
1543
977k
                int code;
1544
977k
                cos_object_t *pco1 = pres->object;
1545
1546
977k
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
17.7k
                    continue;      /* don't compare different types */
1548
959k
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
959k
                if (code < 0)
1550
0
                    return code;
1551
959k
                if (code > 0) {
1552
62.9k
                    code = eq(pdev, *ppres, pres);
1553
62.9k
                    if (code < 0)
1554
0
                        return code;
1555
62.9k
                    if (code > 0) {
1556
62.9k
                        *ppres = pres;
1557
62.9k
                        return 1;
1558
62.9k
                    }
1559
62.9k
                }
1560
959k
            }
1561
1.10M
        }
1562
1.08M
    }
1563
63.2k
    return 0;
1564
126k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
13.5k
{
1569
13.5k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
13.5k
    pdf_resource_t *pres;
1571
13.5k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
13.5k
    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
163k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
149k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
149k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
149k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
149k
    }
1588
1589
16.4k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
16.4k
        if (pres == pres1) {
1591
13.5k
            *pprev = pres->prev;
1592
13.5k
            break;
1593
13.5k
        }
1594
1595
13.5k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
13.5k
        pprev = pchain + i;
1597
13.9k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
13.9k
            if (pres == pres1) {
1599
13.5k
                *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
13.5k
                return;
1609
13.5k
            }
1610
13.5k
    }
1611
13.5k
}
1612
1613
/* Drop resources by a condition. */
1614
void
1615
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1616
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1617
0
{
1618
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1619
0
    pdf_resource_t **pprev;
1620
0
    pdf_resource_t *pres;
1621
0
    int i;
1622
1623
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1624
0
        pprev = pchain + i;
1625
0
        for (; (pres = *pprev) != 0; ) {
1626
0
            if (cond(pdev, pres)) {
1627
0
                *pprev = pres->next;
1628
0
                pres->next = pres; /* A temporary mark - see below */
1629
0
            } else
1630
0
                pprev = &pres->next;
1631
0
        }
1632
0
    }
1633
0
    pprev = &pdev->last_resource;
1634
0
    for (; (pres = *pprev) != 0; )
1635
0
        if (pres->next == pres) {
1636
0
            *pprev = pres->prev;
1637
0
            if (pres->object) {
1638
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1639
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1640
0
                pres->object = 0;
1641
0
            }
1642
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1643
0
        } else
1644
0
            pprev = &pres->prev;
1645
0
}
1646
1647
/* Print resource statistics. */
1648
void
1649
pdf_print_resource_statistics(gx_device_pdf * pdev)
1650
0
{
1651
1652
0
    int rtype;
1653
1654
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1655
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1656
0
        pdf_resource_t *pres;
1657
0
        const char *name = pdf_resource_type_names[rtype];
1658
0
        int i, n = 0;
1659
1660
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1661
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1662
0
        }
1663
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1664
0
                (name ? name : ""), n);
1665
0
    }
1666
0
}
1667
1668
int FlushObjStm(gx_device_pdf *pdev)
1669
19.5k
{
1670
19.5k
    int code = 0, i, len = 0, id = 0, end;
1671
19.5k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
19.5k
    pdf_resource_t *pres;
1673
19.5k
    int options = DATA_STREAM_BINARY;
1674
1675
19.5k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
19.5k
    pdev->WriteObjStms = false;
1679
1680
19.5k
    sflush(pdev->strm);
1681
19.5k
    sflush(pdev->ObjStm.strm);
1682
19.5k
    end = stell(pdev->ObjStm.strm);
1683
1684
19.5k
    if (pdev->CompressStreams)
1685
19.5k
        options |= DATA_STREAM_COMPRESS;
1686
1687
19.5k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
19.5k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
19.5k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
19.5k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
19.5k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
19.5k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
19.5k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
19.5k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
203k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
183k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
183k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
183k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
183k
        strcat(offsets, offset);
1713
183k
    }
1714
1715
19.5k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
19.5k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
19.5k
    stream_puts(pdev->strm, offsets);
1723
1724
19.5k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
19.5k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
19.5k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
19.5k
    code = pdf_close_aside(pdev);
1732
19.5k
    if (code < 0)
1733
0
        return code;
1734
19.5k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
19.5k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
19.5k
    pdev->WriteObjStms = true;
1740
19.5k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
19.5k
    if (pdev->ObjStmOffsets != NULL) {
1742
19.5k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
19.5k
        pdev->ObjStmOffsets = NULL;
1744
19.5k
    }
1745
19.5k
    pdev->NumObjStmObjects = 0;
1746
19.5k
    pdev->ObjStm_id = 0;
1747
1748
19.5k
    pdev->WriteObjStms = true;
1749
19.5k
    return code;
1750
19.5k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
19.5k
{
1754
19.5k
    int code;
1755
1756
19.5k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
19.5k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
19.5k
    if (code < 0)
1760
0
        return code;
1761
1762
19.5k
    pdev->NumObjStmObjects = 0;
1763
19.5k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
19.5k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
19.5k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
19.5k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
19.5k
    return code;
1772
19.5k
}
1773
1774
/* Begin an object logically separate from the contents. */
1775
int64_t
1776
pdf_open_separate_noObjStm(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1777
108k
{
1778
108k
    int code;
1779
1780
108k
    code = pdfwrite_pdf_open_document(pdev);
1781
108k
    if (code < 0)
1782
0
        return code;
1783
108k
    pdev->asides.save_strm = pdev->strm;
1784
108k
    pdev->strm = pdev->asides.strm;
1785
108k
    code = pdf_open_obj(pdev, id, type);
1786
108k
    return code;
1787
108k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
378k
{
1791
378k
    if (type == resourceStream)
1792
0
        return true;
1793
378k
    if (type == resourceCharProc)
1794
406
        return true;
1795
378k
    if (type == resourcePattern)
1796
6.66k
        return true;
1797
371k
    if (type == resourceXObject)
1798
3.74k
        return true;
1799
367k
    return false;
1800
371k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
352k
{
1805
352k
    int code;
1806
1807
352k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
168k
        code = pdfwrite_pdf_open_document(pdev);
1809
168k
        if (code < 0)
1810
0
            return code;
1811
168k
        pdev->asides.save_strm = pdev->strm;
1812
168k
        pdev->strm = pdev->asides.strm;
1813
168k
        code = pdf_open_obj(pdev, id, type);
1814
183k
    } else {
1815
183k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
132
            code = FlushObjStm(pdev);
1817
132
            if (code < 0)
1818
0
                return code;
1819
132
        }
1820
183k
        if (!pdev->ObjStm.strm) {
1821
19.5k
            code = NewObjStm(pdev);
1822
19.5k
            if (code < 0)
1823
0
                return code;
1824
19.5k
        }
1825
183k
        pdev->ObjStm.save_strm = pdev->strm;
1826
183k
        pdev->strm = pdev->ObjStm.strm;
1827
183k
        code = pdf_open_obj(pdev, id, type);
1828
183k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
183k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
183k
    }
1831
352k
    return code;
1832
352k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
123k
{
1836
123k
    return pdf_open_separate(pdev, 0L, type);
1837
123k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
317k
{
1842
317k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
317k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
317k
}
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
492k
{
1852
492k
    pdf_resource_t *pres;
1853
492k
    cos_object_t *object;
1854
1855
492k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
492k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
492k
                           "pdf_alloc_aside(resource)");
1859
492k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
492k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
492k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
492k
    memset(pres, 0, pst->ssize);
1865
492k
    pres->object = object;
1866
492k
    if (id < 0) {
1867
321k
        object->id = -1L;
1868
321k
        pres->rname[0] = 0;
1869
321k
    } else
1870
171k
        pdf_reserve_object_id(pdev, pres, id);
1871
492k
    pres->next = *plist;
1872
492k
    pres->rid = 0;
1873
492k
    *plist = pres;
1874
492k
    pres->prev = pdev->last_resource;
1875
492k
    pdev->last_resource = pres;
1876
492k
    pres->named = false;
1877
492k
    pres->global = false;
1878
492k
    pres->where_used = pdev->used_mask;
1879
492k
    *ppres = pres;
1880
492k
    return 0;
1881
492k
}
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
76.2k
{
1887
76.2k
    int64_t id = pdf_begin_separate(pdev, type);
1888
76.2k
    int code = 0;
1889
1890
76.2k
    if (id < 0)
1891
0
        return (int)id;
1892
76.2k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
76.2k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
76.2k
    return code;
1897
76.2k
}
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
76.2k
{
1904
76.2k
    int code;
1905
1906
76.2k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
76.2k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
76.2k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
76.2k
    if (code >= 0)
1913
76.2k
        (*ppres)->rid = rid;
1914
76.2k
    return code;
1915
76.2k
}
1916
int
1917
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1918
                   pdf_resource_t ** ppres)
1919
75.6k
{
1920
75.6k
    int code;
1921
1922
75.6k
    if (rtype >= NUM_RESOURCE_TYPES)
1923
0
        rtype = resourceOther;
1924
1925
75.6k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1926
1927
75.6k
    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
75.6k
    return code;
1934
75.6k
}
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
184k
{
1952
184k
    int code;
1953
1954
184k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
184k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
184k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
184k
    if (code >= 0)
1961
184k
        (*ppres)->rid = rid;
1962
184k
    return code;
1963
184k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
1.75M
{
1969
1.75M
    return pres->object->id;
1970
1.75M
}
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
108k
{
1976
108k
    int code = pdf_end_obj(pdev, type);
1977
1978
108k
    pdev->strm = pdev->asides.save_strm;
1979
108k
    pdev->asides.save_strm = 0;
1980
108k
    return code;
1981
108k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
352k
{
1985
352k
    int code = pdf_end_obj(pdev, type);
1986
1987
352k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
168k
        pdev->strm = pdev->asides.save_strm;
1989
168k
        pdev->asides.save_strm = 0;
1990
183k
    } else {
1991
183k
        pdev->strm = pdev->ObjStm.save_strm;
1992
183k
        pdev->ObjStm.save_strm = 0;
1993
183k
        pdev->NumObjStmObjects++;
1994
183k
    }
1995
352k
    return code;
1996
352k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
539
{
2000
539
    return pdf_end_separate(pdev, type);
2001
539
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
539
{
2007
539
    return pdf_end_aside(pdev, type);
2008
539
}
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
574k
{
2018
574k
    int j, code = 0;
2019
2020
9.75M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
9.18M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
9.36M
        for (; pres != 0; pres = pres->next)
2024
176k
            if ((!pres->named || pdev->ForOPDFRead)
2025
176k
                && pres->object && !pres->object->written) {
2026
30.4k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
30.4k
            }
2028
9.18M
    }
2029
574k
    return code;
2030
574k
}
2031
2032
/*
2033
 * Reverse resource chains.
2034
 * ps2write uses it with page resources.
2035
 * Assuming only the 0th chain contauns something.
2036
 */
2037
void
2038
pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2039
24.0k
{
2040
24.0k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
24.0k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
24.0k
    if (pres == NULL)
2044
0
        return;
2045
24.0k
    pres1 = pres->next;
2046
30.7k
    for (;;) {
2047
30.7k
        if (pres1 == NULL)
2048
24.0k
            break;
2049
6.65k
        pres2 = pres1->next;
2050
6.65k
        pres1->next = pres;
2051
6.65k
        pres = pres1;
2052
6.65k
        pres1 = pres2;
2053
6.65k
    }
2054
24.0k
    pres0->next = NULL;
2055
24.0k
    pdev->resources[rtype].chains[0] = pres;
2056
24.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
304k
{
2065
304k
    int j;
2066
2067
5.17M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
4.87M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
4.87M
        pdf_resource_t *pres;
2070
2071
5.00M
        while ((pres = *prev) != 0) {
2072
125k
            if (pres->named) { /* named, don't free */
2073
7
                prev = &pres->next;
2074
125k
            } else {
2075
125k
                if (pres->object) {
2076
125k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
125k
                    pres->object = 0;
2078
125k
                }
2079
125k
                *prev = pres->next;
2080
125k
            }
2081
125k
        }
2082
4.87M
    }
2083
304k
    return 0;
2084
304k
}
2085
2086
/*
2087
 * Store the resource sets for a content stream (page or XObject).
2088
 * Sets page->{procsets, resource_ids[]}.
2089
 */
2090
int
2091
pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage)
2092
56.1k
{
2093
56.1k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
505k
    for (i = 0; i <= resourceFont; ++i) {
2098
449k
        stream *s = 0;
2099
449k
        int j;
2100
2101
449k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
56.1k
            continue;
2103
393k
        page->resource_ids[i] = 0;
2104
6.68M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
6.29M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
6.45M
            for (; pres != 0; pres = pres->next) {
2108
165k
                if (pres->where_used & pdev->used_mask) {
2109
108k
                    int64_t id = pdf_resource_id(pres);
2110
2111
108k
                    if (id == -1L)
2112
9.04k
                        continue;
2113
99.3k
                    if (s == 0) {
2114
34.2k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
34.2k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
34.2k
                        s = pdev->strm;
2117
34.2k
                        stream_puts(s, "<<");
2118
34.2k
                    }
2119
99.3k
                    pprints1(s, "/%s\n", pres->rname);
2120
99.3k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
99.3k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
99.3k
                    if (clear_usage)
2123
99.3k
                        pres->where_used -= pdev->used_mask;
2124
99.3k
                }
2125
165k
            }
2126
6.29M
        }
2127
393k
        if (s) {
2128
34.2k
            stream_puts(s, ">>\n");
2129
34.2k
            pdf_end_separate(pdev, i);
2130
34.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
393k
        if (i != resourceFont && i != resourceProperties)
2137
280k
            pdf_write_resource_objects(pdev, i);
2138
393k
    }
2139
56.1k
    page->procsets = pdev->procsets;
2140
56.1k
    return 0;
2141
56.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
280k
{
2147
280k
    gs_offset_t r, left = count;
2148
280k
    byte buf[sbuf_size];
2149
2150
2.74M
    while (left > 0) {
2151
2.46M
        uint copy = min(left, sbuf_size);
2152
2153
2.46M
        r = gp_fread(buf, 1, copy, file);
2154
2.46M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
2.46M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
2.46M
        stream_write(s, buf, copy);
2160
2.46M
        left -= copy;
2161
2.46M
    }
2162
280k
    return 0;
2163
280k
}
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
804k
    while (left > 0) {
2173
678k
        byte buf[sbuf_size];
2174
678k
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
678k
        int64_t end_pos = gp_ftell(file);
2176
2177
678k
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
678k
        r = gp_fread(buf, 1, copy, file);
2181
678k
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
678k
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
678k
        stream_write(s, buf, copy);
2188
678k
        sflush(s);
2189
678k
        left -= copy;
2190
678k
    }
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
227k
{
2201
227k
    cos_dict_t *Page;
2202
2203
227k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
227k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
54
        uint new_num_pages;
2207
54
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
54
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
54
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
54
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
54
                             "pdf_page_id(resize pages)");
2216
2217
54
        if (new_pages == 0)
2218
0
            return 0;
2219
54
        memset(&new_pages[pdev->num_pages], 0,
2220
54
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
54
        pdev->pages = new_pages;
2222
54
        pdev->num_pages = new_num_pages;
2223
54
    }
2224
227k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
56.1k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
56.1k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
56.1k
        Page->id = pdf_obj_forward_ref(pdev);
2230
56.1k
    }
2231
227k
    return Page->id;
2232
227k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
3.53M
{
2238
3.53M
    return &pdev->pages[pdev->next_page];
2239
3.53M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
11.5k
{
2245
11.5k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
11.5k
    return pdev->pages[pdev->next_page].Page;
2248
11.5k
}
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
11.4M
{
2267
11.4M
    if (!is_in_page(pdev)) {
2268
55.2k
        int code;
2269
2270
55.2k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
55.2k
        code = pdfwrite_pdf_open_document(pdev);
2273
55.2k
        if (code < 0)
2274
0
            return code;
2275
55.2k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
11.4M
    return pdf_open_contents(pdev, context);
2278
11.4M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
129k
{
2284
129k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
129k
    if (pdev->sbstack_depth <= bottom) {
2289
113k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
113k
        if (code < 0)
2292
0
            return code;
2293
113k
    }
2294
129k
    if (pdev->context > PDF_IN_STREAM) {
2295
2
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2296
2297
2
        if (code < 0)
2298
0
            return code;
2299
2
    }
2300
129k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
83.2k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
83.2k
        if (code < 0)
2304
0
            return code;
2305
83.2k
        code = pdf_remember_clip_path(pdev, NULL);
2306
83.2k
        if (code < 0)
2307
0
            return code;
2308
83.2k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
83.2k
    }
2310
129k
    return 0;
2311
129k
}
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
43.5k
{
2323
43.5k
    int major = (int)(gs_revision / 1000);
2324
43.5k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
43.5k
    int patch = gs_revision % 10;
2326
2327
43.5k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
43.5k
}
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
145k
{
2335
145k
    stream *s = pdev->strm;
2336
2337
145k
    if (before)
2338
143k
        stream_puts(s, before);
2339
145k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
145k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
145k
    if (after)
2342
145k
        stream_puts(s, after);
2343
145k
}
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.42M
{
2354
3.42M
    uint i;
2355
2356
18.2M
    for (i = 0; i < size; ++i) {
2357
14.7M
        uint c = nstr[i];
2358
14.7M
        char hex[4];
2359
2360
14.7M
        switch (c) {
2361
14.7M
            default:
2362
14.7M
                if (c >= 0x21 && c <= 0x7e) {
2363
14.7M
                    stream_putc(s, (byte)c);
2364
14.7M
                    break;
2365
14.7M
                }
2366
                /* falls through */
2367
4.67k
            case '#':
2368
4.68k
            case '%':
2369
4.69k
            case '(': case ')':
2370
4.69k
            case '<': case '>':
2371
4.71k
            case '[': case ']':
2372
4.72k
            case '{': case '}':
2373
4.74k
            case '/':
2374
4.74k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
4.74k
                stream_puts(s, hex);
2376
4.74k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
14.7M
        }
2380
14.7M
    }
2381
3.42M
    return 0;
2382
3.42M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
3.42M
{
2386
3.42M
    return &pdf_put_name_chars_1_2;
2387
3.42M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
3.42M
{
2391
3.42M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
3.42M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
3.42M
{
2396
3.42M
    stream_putc(pdev->strm, '/');
2397
3.42M
    return pdf_put_name_chars(pdev, nstr, size);
2398
3.42M
}
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
72.6k
{
2462
72.6k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
72.6k
        int i, oct, width = 0;
2464
72.6k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
72.6k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
72.6k
            stream_write(pdev->strm, "\n", 1);
2468
72.6k
        stream_write(pdev->strm, "<", 1);
2469
72.6k
        width++;
2470
2.25M
        for (i = 1; i < size - 1; i++) {
2471
2.18M
            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
768
                    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
39
                        case 'n' :
2486
39
                            oct = 10;
2487
39
                            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
768
                    }
2498
768
                }
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
2.04M
            } else {
2507
2.04M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
223
                    stream_write(pdev->strm, "\n", 1);
2509
223
                    width = 0;
2510
223
                }
2511
2.04M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
2.04M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
2.04M
                width += 2;
2514
2.04M
            }
2515
2.18M
        }
2516
72.6k
        stream_write(pdev->strm, ">", 1);
2517
72.6k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
72.6k
            stream_write(pdev->strm, "\n", 1);
2519
72.6k
        return 0;
2520
72.6k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
72.6k
}
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
130k
{
2562
130k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
130k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
677
            stream_putc(pdev->strm, (byte)'\n');
2565
677
            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
677
            } else {
2606
677
                stream_write(pdev->strm, vstr, size);
2607
677
            }
2608
129k
        } else {
2609
129k
            stream_write(pdev->strm, vstr, size);
2610
129k
        }
2611
130k
    } 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
130k
    return 0;
2627
130k
}
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
5.90M
{
2637
5.90M
    psdf_write_string(pdev->strm, str, size,
2638
5.90M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
5.90M
    return 0;
2640
5.90M
}
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.02M
{
2646
5.02M
    if (size > 0 && vstr[0] == '/')
2647
2.89M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
2.12M
    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.12M
    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.12M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
107k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
2.01M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
22.7k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
1.99M
    else if (size > 1 && vstr[0] == '(') {
2657
259k
        if (pdev->ForOPDFRead)
2658
72.6k
            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
259k
    }
2662
1.73M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
1.73M
    stream_write(pdev->strm, vstr, size);
2665
1.73M
    return 0;
2666
5.02M
}
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
414k
{
2674
414k
    const char *filter_name = 0;
2675
414k
    bool binary_ok = true;
2676
414k
    stream *fs = s;
2677
414k
    cos_dict_t *decode_parms = 0;
2678
414k
    int code;
2679
2680
1.47M
    for (; fs != 0; fs = fs->strm) {
2681
1.05M
        const stream_state *st = fs->state;
2682
1.05M
        const stream_template *templat = st->templat;
2683
2684
1.05M
#define TEMPLATE_IS(atemp)\
2685
5.97M
  (templat->process == (atemp).process)
2686
1.05M
        if (TEMPLATE_IS(s_A85E_template))
2687
178k
            binary_ok = false;
2688
880k
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
87.8k
            cos_param_list_writer_t writer;
2690
87.8k
            stream_CF_state cfs;
2691
2692
87.8k
            decode_parms =
2693
87.8k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
87.8k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
87.8k
            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
87.8k
            cfs = *(const stream_CF_state *)st;
2702
87.8k
            if (cfs.EndOfBlock)
2703
12.4k
                cfs.Rows = 0;
2704
87.8k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
87.8k
            filter_name = pfn->CCITTFaxDecode;
2706
792k
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
6.07k
            filter_name = pfn->DCTDecode;
2708
786k
        else if (TEMPLATE_IS(s_zlibE_template))
2709
129k
            filter_name = pfn->FlateDecode;
2710
656k
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
656k
        else if (TEMPLATE_IS(s_LZWE_template))
2713
81.1k
            filter_name = pfn->LZWDecode;
2714
575k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
8.00k
            const stream_PNGP_state *const ss =
2717
8.00k
                (const stream_PNGP_state *)st;
2718
2719
8.00k
            decode_parms =
2720
8.00k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
8.00k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
8.00k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
8.00k
                                         ss->Predictor));
2725
8.00k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
8.00k
                                         ss->Columns));
2727
8.00k
            if (ss->Colors != 1)
2728
8.00k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
8.00k
                                             ss->Colors));
2730
8.00k
            if (ss->BitsPerComponent != 8)
2731
8.00k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
8.00k
                                             "/BitsPerComponent",
2733
8.00k
                                             ss->BitsPerComponent));
2734
567k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
1.05M
#undef TEMPLATE_IS
2737
1.05M
    }
2738
414k
    if (filter_name) {
2739
304k
        if (binary_ok) {
2740
158k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
158k
            if (decode_parms)
2742
158k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
158k
                                                COS_OBJECT(decode_parms)));
2744
158k
        } else {
2745
146k
            cos_array_t *pca =
2746
146k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
146k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
146k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
146k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
146k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
146k
                                            COS_OBJECT(pca)));
2754
146k
            if (decode_parms) {
2755
82.0k
                pca = cos_array_alloc(pdev,
2756
82.0k
                                      "pdf_put_image_filters(DecodeParms)");
2757
82.0k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
82.0k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
82.0k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
82.0k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
82.0k
                                                COS_OBJECT(pca)));
2763
82.0k
            }
2764
146k
        }
2765
304k
    } else if (!binary_ok)
2766
109k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
414k
    return 0;
2768
414k
}
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
193k
{
2774
193k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
120k
                    &s_LZWE_template : &s_zlibE_template);
2776
193k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
193k
                                     "pdf_write_function");
2778
2779
193k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
193k
    if (templat->set_defaults)
2782
193k
        templat->set_defaults(st);
2783
193k
    return psdf_encode_binary(pbw, templat, st);
2784
193k
}
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
231k
{
2817
231k
    stream *s = pdev->strm;
2818
231k
    int options = orig_options;
2819
231k
#define USE_ASCII85 1
2820
412k
#define USE_FLATE 2
2821
231k
#define USE_BROTLI 4
2822
231k
    static const char *const fnames[6] = {
2823
231k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
231k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
231k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
231k
    };
2827
231k
    static const char *const fnames1_2[6] = {
2828
231k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
231k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
231k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
231k
    };
2832
231k
    int filters = 0;
2833
231k
    int code;
2834
2835
231k
    if (options & DATA_STREAM_COMPRESS) {
2836
181k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
181k
        } else {
2840
181k
            filters |= USE_FLATE;
2841
181k
            options |= DATA_STREAM_BINARY;
2842
181k
        }
2843
181k
    }
2844
231k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
49.0k
        filters |= USE_ASCII85;
2846
231k
    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
231k
    if (options & DATA_STREAM_ENCRYPT) {
2860
19.4k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
19.4k
        if (code < 0)
2862
0
            return code;
2863
19.4k
        pdev->strm = s;
2864
19.4k
        pdw->encrypted = true;
2865
19.4k
    } else
2866
212k
        pdw->encrypted = false;
2867
231k
    if (options & DATA_STREAM_BINARY) {
2868
181k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
181k
        if (code < 0)
2870
0
            return code;
2871
181k
    } else {
2872
50.1k
        code = 0;
2873
50.1k
        pdw->binary.target = pdev->strm;
2874
50.1k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
50.1k
        pdw->binary.strm = pdev->strm;
2876
50.1k
    }
2877
231k
    pdw->start = stell(s);
2878
231k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
231k
    else
2881
231k
        if (filters & USE_FLATE)
2882
181k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
231k
    return code;
2884
231k
#undef USE_ASCII85
2885
231k
#undef USE_FLATE
2886
231k
#undef USE_BROTLI
2887
231k
}
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
29.5k
{   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
29.5k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
29.5k
    pdw->binary.target = pdev->strm;
2898
29.5k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
29.5k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
29.5k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
29.5k
                options);
2902
29.5k
    if (object_id != 0)
2903
159
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
29.5k
    pdw->binary.strm = pdev->strm;
2905
29.5k
    return code;
2906
29.5k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
5.27k
{   int code;
2912
2913
5.27k
    code = pdf_close_aside(pdw->pdev);
2914
5.27k
    if (code < 0)
2915
0
        return code;
2916
5.27k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
5.27k
    if (code < 0)
2918
0
        return code;
2919
5.27k
    return 0;
2920
5.27k
}
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
4.10k
{
2929
4.10k
    if (pranges == NULL)
2930
4.10k
        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
20.4k
{
2967
20.4k
    gs_function_info_t info;
2968
20.4k
    cos_param_list_writer_t rlist;
2969
20.4k
    pdf_resource_t *pres;
2970
20.4k
    cos_object_t *pcfn;
2971
20.4k
    cos_dict_t *pcd;
2972
20.4k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
20.4k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
20.4k
    *ppres = pres;
2979
20.4k
    pcfn = pres->object;
2980
20.4k
    gs_function_get_info(pfn, &info);
2981
20.4k
    if (FunctionType(pfn) == function_type_ArrayedOutput) {
2982
        /*
2983
         * Arrayed Output Functions are used internally to represent
2984
         * Shading Function entries that are arrays of Functions.
2985
         * They require special handling.
2986
         */
2987
0
        cos_array_t *pca;
2988
2989
0
        cos_become(pcfn, cos_type_array);
2990
0
        pca = (cos_array_t *)pcfn;
2991
0
        return pdf_function_array(pdev, pca, &info);
2992
0
    }
2993
20.4k
    if (info.DataSource != 0) {
2994
12.8k
        psdf_binary_writer writer;
2995
12.8k
        stream *save = pdev->strm;
2996
12.8k
        cos_stream_t *pcos;
2997
12.8k
        stream *s;
2998
2999
12.8k
        cos_become(pcfn, cos_type_stream);
3000
12.8k
        pcos = (cos_stream_t *)pcfn;
3001
12.8k
        pcd = cos_stream_dict(pcos);
3002
12.8k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
12.8k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
12.8k
        pdev->strm = s;
3006
12.8k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
12.8k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
12.8k
            )
3009
12.1k
            code = pdf_flate_binary(pdev, &writer);
3010
12.8k
        if (code >= 0) {
3011
12.8k
            static const pdf_filter_names_t fnames = {
3012
12.8k
                PDF_FILTER_NAMES
3013
12.8k
            };
3014
3015
12.8k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
12.8k
        }
3017
12.8k
        if (code >= 0) {
3018
12.8k
            byte buf[100];    /* arbitrary */
3019
12.8k
            ulong pos;
3020
12.8k
            uint count;
3021
12.8k
            const byte *ptr;
3022
3023
64.5k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
51.6k
                count = min(sizeof(buf), info.data_size - pos);
3025
51.6k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
51.6k
                                        &ptr);
3027
51.6k
                stream_write(writer.strm, ptr, count);
3028
51.6k
            }
3029
12.8k
            code = psdf_end_binary(&writer);
3030
12.8k
            s_close_filters(&s, s->strm);
3031
12.8k
        }
3032
12.8k
        pdev->strm = save;
3033
12.8k
        if (code < 0)
3034
0
            return code;
3035
12.8k
    } else {
3036
7.53k
        cos_become(pcfn, cos_type_dict);
3037
7.53k
        pcd = (cos_dict_t *)pcfn;
3038
7.53k
    }
3039
20.4k
    if (info.Functions != 0) {
3040
1.39k
        cos_array_t *functions =
3041
1.39k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.39k
        cos_value_t v;
3043
3044
1.39k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.39k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.39k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.39k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.39k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.39k
    }
3054
20.4k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
20.4k
    if (code < 0)
3056
0
        return code;
3057
20.4k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
20.4k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
14.8k
{
3062
14.8k
    return true;
3063
14.8k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
20.4k
{
3067
20.4k
    pdf_resource_t *pres;
3068
20.4k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
20.4k
    if (code < 0)
3071
0
        return code;
3072
20.4k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
20.4k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
20.4k
    if (code < 0)
3077
0
        return code;
3078
20.4k
    pres->where_used |= pdev->used_mask;
3079
20.4k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
20.4k
    return 0;
3081
20.4k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.39k
{
3085
1.39k
    int i, code = 0;
3086
1.39k
    cos_value_t v;
3087
3088
6.25k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
4.86k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
4.86k
            (code = cos_array_add(pca, &v)) < 0
3091
4.86k
            ) {
3092
0
            break;
3093
0
        }
3094
4.86k
    }
3095
1.39k
    return code;
3096
1.39k
}
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.4k
{
3102
11.4k
    cos_value_t value;
3103
11.4k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
11.4k
    if (code < 0)
3106
0
        return code;
3107
11.4k
    *pid = value.contents.object->id;
3108
11.4k
    return 0;
3109
11.4k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
5.57k
{
3114
5.57k
    char key[] = "/Functions";
3115
5.57k
    cos_value_t *v, v2;
3116
3117
5.57k
    if (cos_type(pco) == cos_type_dict) {
3118
1.89k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
1.89k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
439
            if (cos_type(v->contents.object) == cos_type_array){
3121
439
                int code=0;
3122
2.35k
                while (code == 0) {
3123
1.91k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
1.91k
                }
3125
439
            }
3126
439
        }
3127
1.89k
    }
3128
5.57k
    if (cos_type(pco) == cos_type_array) {
3129
0
        int64_t index;
3130
0
        cos_array_t *pca = (cos_array_t *)pco;
3131
0
        const cos_array_element_t *element = cos_array_element_first(pca);
3132
0
        cos_value_t *v;
3133
3134
0
        while (element) {
3135
0
            element = cos_array_element_next(element, &index, (const cos_value_t **)&v);
3136
0
            if (v->value_type == COS_VALUE_OBJECT) {
3137
0
                if (pdf_find_resource_by_resource_id(pdev, resourceFunction, v->contents.object->id)){
3138
0
                    v->value_type = COS_VALUE_CONST;
3139
                    /* Need to remove the element from the array here */
3140
0
                }
3141
0
            }
3142
0
        }
3143
0
    }
3144
5.57k
    return 0;
3145
5.57k
}
3146
3147
/* Write a FontBBox dictionary element. */
3148
int
3149
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3150
24.9k
{
3151
24.9k
    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
24.9k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
24.9k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
24.9k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
24.9k
             pbox->p.x, pbox->p.y, x, y);
3163
24.9k
    return 0;
3164
24.9k
}
3165
3166
/* Write a FontBBox dictionary element using floats for the values. */
3167
int
3168
pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
3169
2.97k
{
3170
2.97k
    stream *s = pdev->strm;
3171
    /*
3172
     * AR 4 doesn't like fonts with empty FontBBox, which
3173
     * happens when the font contains only space characters.
3174
     * Small bbox causes AR 4 to display a hairline. So we use
3175
     * the full BBox.
3176
     */
3177
2.97k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
2.97k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
2.97k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
2.97k
             pbox->p.x, pbox->p.y, x, y);
3182
2.97k
    return 0;
3183
2.97k
}