Coverage Report

Created: 2026-08-08 08:00

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
14.2k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
14.2k
#  define Flate_filter_name "FlateDecode"
61
14.2k
#  define Flate_filter_template s_zlibE_template
62
14.2k
#  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.82M
  BEGIN if ((code = (expr)) < 0) return code; END
78
79
/* GC descriptors */
80
extern_st(st_pdf_color_space);
81
extern_st(st_pdf_font_resource);
82
extern_st(st_pdf_char_proc);
83
extern_st(st_pdf_font_descriptor);
84
public_st_pdf_resource();
85
private_st_pdf_x_object();
86
private_st_pdf_pattern();
87
88
/* ---------------- Utilities ---------------- */
89
90
#ifdef PS2WRITE_USES_ROMFS
91
/*
92
 * Strip whitespace and comments from a line of PostScript code as possible.
93
 * Return a pointer to any string that remains, or NULL if none.
94
 * Note that this may store into the string.
95
 */
96
/* This function copied from geninit.c . */
97
static char *
98
doit(char *line, bool intact)
99
{
100
    char *str = line;
101
    char *from;
102
    char *to;
103
    int in_string = 0;
104
105
    if (intact)
106
        return str;
107
    while (*str == ' ' || *str == '\t')   /* strip leading whitespace */
108
        ++str;
109
    if (*str == 0)    /* all whitespace */
110
        return NULL;
111
    if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
112
        return str;
113
    if (str[0] == '%')    /* comment line */
114
        return NULL;
115
    /*
116
     * Copy the string over itself removing:
117
     *  - All comments not within string literals;
118
     *  - Whitespace adjacent to '[' ']' '{' '}';
119
     *  - Whitespace before '/' '(' '<';
120
     *  - Whitespace after ')' '>'.
121
     */
122
    for (to = from = str; (*to = *from) != 0; ++from, ++to) {
123
        switch (*from) {
124
            case '%':
125
                if (!in_string)
126
                    break;
127
                continue;
128
            case ' ':
129
            case '\t':
130
                if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
131
                    --to;
132
                continue;
133
            case '(':
134
            case '<':
135
            case '/':
136
            case '[':
137
            case ']':
138
            case '{':
139
            case '}':
140
                if (to > str && !in_string && strchr(" \t", to[-1]))
141
                    *--to = *from;
142
                if (*from == '(')
143
                    ++in_string;
144
                continue;
145
            case ')':
146
                --in_string;
147
                continue;
148
            case '\\':
149
                if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
150
                    *++to = *++from;
151
                continue;
152
            default:
153
                continue;
154
        }
155
        break;
156
    }
157
    /* Strip trailing whitespace. */
158
    while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
159
        --to;
160
    *to = 0;
161
    return str;
162
}
163
164
static int
165
copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
166
{
167
    stream *f;
168
    char buf[1024], *p, *q  = buf;
169
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
170
    bool skipping = false;
171
172
    f = sfopen(fname, "r", s->memory);
173
    if (f == NULL)
174
        return_error(gs_error_undefinedfilename);
175
    n = sfread(buf, 1, m, f);
176
    buf[n] = 0;
177
    do {
178
        if (*q == '\r' || *q == '\n') {
179
            q++;
180
            continue;
181
        }
182
        p = strchr(q, '\r');
183
        if (p == NULL)
184
            p = strchr(q, '\n');
185
        if (p == NULL) {
186
            if (n < m)
187
                p = buf + n;
188
            else {
189
                strcpy(buf, q);
190
                l = strlen(buf);
191
                m = sizeof(buf) - 1 - l;
192
                if (!m) {
193
                    sfclose(f);
194
                    emprintf1(s->memory,
195
                              "The procset %s contains a too long line.",
196
                              fname);
197
                    return_error(gs_error_ioerror);
198
                }
199
                n = sfread(buf + l, 1, m, f);
200
                n += l;
201
                m += l;
202
                buf[n] = 0;
203
                q = buf;
204
                continue;
205
            }
206
        }
207
        *p = 0;
208
        if (q[0] == '%')
209
            l = 0;
210
        else {
211
            q = doit(q, false);
212
            if (q == NULL)
213
                l = 0;
214
            else
215
                l = strlen(q);
216
        }
217
        if (l) {
218
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
219
                skipping = true;
220
            if (!skipping) {
221
                outl += l + 1;
222
                if (outl > 100) {
223
                    q[l] = '\r';
224
                    outl = 0;
225
                } else
226
                    q[l] = ' ';
227
                stream_write(s, q, l + 1);
228
            }
229
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
230
                skipping = false;
231
        }
232
        q = p + 1;
233
    } while (n == m || q < buf + n);
234
    if (outl)
235
        stream_write(s, "\r", 1);
236
    sfclose(f);
237
    return 0;
238
}
239
240
static int
241
copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
242
{
243
    stream *f;
244
    char buf[1024], *p, *q  = buf;
245
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
246
    bool skipping = false;
247
248
    f = sfopen(fname, "r", s->memory);
249
    if (f == NULL)
250
        return_error(gs_error_undefinedfilename);
251
    n = sfread(buf, 1, m, f);
252
    buf[n] = 0;
253
    do {
254
        if (*q == '\r' || *q == '\n') {
255
            q++;
256
            continue;
257
        }
258
        p = strchr(q, '\r');
259
        if (p == NULL)
260
            p = strchr(q, '\n');
261
        if (p == NULL) {
262
            if (n < m)
263
                p = buf + n;
264
            else {
265
                strcpy(buf, q);
266
                l = strlen(buf);
267
                m = sizeof(buf) - 1 - l;
268
                if (!m) {
269
                    sfclose(f);
270
                    emprintf1(s->memory,
271
                              "The procset %s contains a too long line.",
272
                              fname);
273
                    return_error(gs_error_ioerror);
274
                }
275
                n = sfread(buf + l, 1, m, f);
276
                n += l;
277
                m += l;
278
                buf[n] = 0;
279
                q = buf;
280
                continue;
281
            }
282
        }
283
        *p = 0;
284
        if (q[0] == '%')
285
            l = 0;
286
        else {
287
            q = doit(q, false);
288
            if (q == NULL)
289
                l = 0;
290
            else
291
                l = strlen(q);
292
        }
293
        if (l) {
294
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
295
                skipping = true;
296
            if (!skipping) {
297
                outl += l + 1;
298
                if (outl > 100) {
299
                    q[l] = '\n';
300
                    outl = 0;
301
                } else
302
                    q[l] = '\n';
303
                stream_write(s, q, l + 1);
304
            }
305
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
306
                skipping = false;
307
        }
308
        q = p + 1;
309
    } while (n == m || q < buf + n);
310
    if (outl)
311
        stream_write(s, "\r", 1);
312
    sfclose(f);
313
    return 0;
314
}
315
#endif
316
317
static int write_opdfread(stream *s)
318
23.0k
{
319
23.0k
    int index = 0;
320
321
93.5M
    do {
322
93.5M
        if (opdfread_ps[index] == 0x00)
323
23.0k
            break;
324
93.5M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
93.5M
        index++;
326
93.5M
    } while (1);
327
23.0k
    return 0;
328
23.0k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
23.0k
{
332
23.0k
    int index = 0;
333
334
899k
    do {
335
899k
        if (gs_mro_e_ps[index] == 0x00)
336
23.0k
            break;
337
876k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
876k
        index++;
339
876k
    } while (1);
340
341
23.0k
    if (HaveTrueTypes) {
342
23.0k
        char Buffer[256];
343
23.0k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
23.0k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
23.0k
        stream_write(s, Buffer, strlen(Buffer));
347
96.9M
        while (entry->Glyph) {
348
96.8M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
96.8M
            stream_write(s, Buffer, strlen(Buffer));
350
96.8M
            entry++;
351
96.8M
        };
352
23.0k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
23.0k
        stream_write(s, Buffer, strlen(Buffer));
354
355
23.0k
        index = 0;
356
830k
        do {
357
830k
            if (gs_mgl_e_ps[index] == 0x00)
358
23.0k
                break;
359
807k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
807k
            index++;
361
807k
        } while (1);
362
23.0k
    }
363
23.0k
    return 0;
364
23.0k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
23.0k
{
369
23.0k
    int code;
370
371
23.0k
    code = write_opdfread(s);
372
23.0k
    if (code < 0)
373
0
        return code;
374
375
23.0k
    code = write_tt_encodings(s, HaveTrueTypes);
376
23.0k
    return code;
377
378
23.0k
}
379
380
static int
381
encode(stream **s, const stream_template *t, gs_memory_t *mem)
382
0
{
383
0
    stream_state *st = s_alloc_state(mem, t->stype, "pdfwrite_pdf_open_document.encode");
384
385
0
    if (st == 0)
386
0
        return_error(gs_error_VMerror);
387
0
    if (t->set_defaults)
388
0
        t->set_defaults(st);
389
0
    if (s_add_filter(s, t, st, mem) == 0) {
390
0
        gs_free_object(mem, st, "pdfwrite_pdf_open_document.encode");
391
0
        return_error(gs_error_VMerror);
392
0
    }
393
0
    return 0;
394
0
}
395
396
/* ------ Document ------ */
397
398
/* Write out the arguments used to invoke GS as a comment in the PDF/PS
399
 * file. We don't write out paths, passwords, or any unrecognised string
400
 * parameters (all sanitised by the arg code) for privacy/security
401
 * reasons. This routine is only called by the PDF linearisation code.
402
 */
403
int
404
pdfwrite_fwrite_args_comment(gx_device_pdf *pdev, gp_file *f)
405
0
{
406
0
    const char * const *argv = NULL;
407
0
    const char *arg;
408
0
    int towrite, length, i, j, argc;
409
410
0
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
411
412
0
    gp_fwrite("%%Invocation:", 13, 1, f);
413
0
    length = 12;
414
0
    for (i=0;i < argc; i++) {
415
0
        arg = argv[i];
416
417
0
        if ((strlen(arg) + length) > 255) {
418
0
            gp_fwrite("\n%%+ ", 5, 1, f);
419
0
            length = 5;
420
0
        } else {
421
0
            gp_fwrite(" ", 1, 1, f);
422
0
            length++;
423
0
        }
424
425
0
        if (strlen(arg) > 250)
426
0
            towrite = 250;
427
0
        else
428
0
            towrite = strlen(arg);
429
430
0
        length += towrite;
431
432
0
        for (j=0;j < towrite;j++) {
433
0
            if (arg[j] == 0x0A) {
434
0
                gp_fwrite("<0A>", 4, 1, f);
435
0
            } else {
436
0
                if (arg[j] == 0x0D) {
437
0
                    gp_fwrite("<0D>", 4, 1, f);
438
0
                } else {
439
0
                    gp_fwrite(&arg[j], 1, 1, f);
440
0
                }
441
0
            }
442
0
        }
443
0
    }
444
0
    gp_fwrite("\n", 1, 1, f);
445
0
    return 0;
446
0
}
447
448
/* Exactly the same as pdfwrite_fwrite_args_comment() above, but uses a stream
449
 * instead of a gp_file *, because of course we aren't consistent.... This
450
 * routine is called by the regular PDF or PS file output code.
451
 */
452
int
453
pdfwrite_write_args_comment(gx_device_pdf *pdev, stream *s)
454
32.4k
{
455
32.4k
    const char * const *argv = NULL;
456
32.4k
    const char *arg;
457
32.4k
    int towrite, length, i, j, argc;
458
459
32.4k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
32.4k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
32.4k
    length = 12;
463
616k
    for (i=0;i < argc; i++) {
464
583k
        arg = argv[i];
465
466
583k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
583k
        } else {
470
583k
            stream_write(s, (byte *)" ", 1);
471
583k
            length++;
472
583k
        }
473
474
583k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
583k
        else
477
583k
            towrite = strlen(arg);
478
479
583k
        length += towrite;
480
481
7.05M
        for (j=0;j < towrite;j++) {
482
6.47M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
6.47M
            } else {
485
6.47M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
6.47M
                } else {
488
6.47M
                    stream_write(s, (byte *)&arg[j], 1);
489
6.47M
                }
490
6.47M
            }
491
6.47M
        }
492
583k
    }
493
32.4k
    stream_write(s, (byte *)"\n", 1);
494
32.4k
    return 0;
495
32.4k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
23.0k
{
499
23.0k
    stream *s = pdev->strm;
500
501
23.0k
    if (pdev->ForOPDFRead) {
502
23.0k
        char cre_date_time[41];
503
23.0k
        int code, status, cre_date_time_len;
504
23.0k
        char BBox[256];
505
506
23.0k
        if (pdev->Eps2Write)
507
11.4k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
11.6k
        else
509
11.6k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
23.0k
        pdfwrite_write_args_comment(pdev, s);
511
        /* We need to calculate the document BoundingBox which is a 'high water'
512
         * mark derived from the BoundingBox of all the individual pages.
513
         */
514
23.0k
        {
515
23.0k
            int pagecount = 1, j;
516
23.0k
            double urx=0, ury=0;
517
518
392k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
369k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
398k
                for (; pres != 0; pres = pres->next)
522
29.6k
                    if ((!pres->named || pdev->ForOPDFRead)
523
29.6k
                        && !pres->object->written) {
524
29.6k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
29.6k
                        if (ceil(page->MediaBox.x) > urx)
526
23.3k
                            urx = ceil(page->MediaBox.x);
527
29.6k
                        if (ceil(page->MediaBox.y) > ury)
528
23.1k
                            ury = ceil(page->MediaBox.y);
529
29.6k
                        pagecount++;
530
29.6k
                    }
531
369k
            }
532
23.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
17.1k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
5.94k
            else
535
5.94k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
536
23.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
23.0k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
17.1k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
5.94k
            else
540
5.94k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
541
23.0k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
23.0k
        }
543
23.0k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
23.0k
        cre_date_time[cre_date_time_len] = 0;
545
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
23.0k
                pdev->dname);
547
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
23.0k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
23.0k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
23.0k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
23.0k
        if (pdev->params.CompressPages) {
558
            /*  When CompressEntireFile is true and ASCII85EncodePages is false,
559
                the ASCII85Encode filter is applied, rather one may expect the opposite.
560
                Keeping it so due to no demand for this mode.
561
                A right implementation should compute the length of the compressed procset,
562
                write out an invocation of SubFileDecode filter, and write the length to
563
                there assuming the output file is positionable. */
564
0
            stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
565
0
            code = encode(&s, &s_A85E_template, pdev->pdf_memory);
566
0
            if (code < 0)
567
0
                return code;
568
0
            code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
569
0
            if (code < 0)
570
0
                return code;
571
0
        }
572
23.0k
        stream_puts(s, "10 dict dup begin\n");
573
23.0k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
23.0k
        if (pdev->Eps2Write) {
575
11.4k
            stream_puts(s, "/SetPageSize false def\n");
576
11.4k
            stream_puts(s, "/EPS2Write true def\n");
577
11.6k
        } else {
578
11.6k
            if (pdev->SetPageSize)
579
11.6k
                stream_puts(s, "/SetPageSize true def\n");
580
11.6k
            stream_puts(s, "/EPS2Write false def\n");
581
11.6k
        }
582
23.0k
        stream_puts(s, "end\n");
583
584
23.0k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
23.0k
        if (code < 0)
586
0
            return code;
587
23.0k
        status = s_close_filters(&s, pdev->strm);
588
23.0k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
23.0k
        stream_puts(s, "\n");
591
23.0k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
23.0k
    }
593
23.0k
    return 0;
594
23.0k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
382k
{
600
382k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
382k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
115k
        stream *s = pdev->strm;
605
115k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
115k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
115k
        if (pdev->ForOPDFRead) {
609
106k
            int code, status;
610
106k
            char BBox[256];
611
106k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
106k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
106k
            if (pdev->ProduceDSC)
615
106k
                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
106k
        }
660
115k
        if (!(pdev->ForOPDFRead)) {
661
9.35k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
9.35k
            if (pdev->binary_ok)
663
9.35k
                stream_puts(s, "%\307\354\217\242\n");
664
9.35k
            pdfwrite_write_args_comment(pdev, s);
665
9.35k
        }
666
115k
    }
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
382k
    if (!pdev->params.CompressPages)
677
247k
        pdev->compression = pdf_compress_none;
678
134k
    else {
679
134k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
134k
        else
682
134k
            pdev->compression = pdf_compress_Flate;
683
134k
    }
684
382k
    return 0;
685
382k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
467k
{
693
467k
    return (pdev->next_id)++;
694
467k
}
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
958k
{
706
958k
    stream *s = pdev->strm;
707
958k
    gs_offset_t pos = stell(s);
708
709
958k
    if (s == pdev->asides.strm)
710
234k
        pos |= ASIDES_BASE_POSITION;
711
958k
    return pos;
712
958k
}
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
53.2k
{
730
53.2k
    int64_t id = pdf_next_id(pdev);
731
53.2k
    gs_offset_t pos = 0;
732
733
53.2k
    if (pdev->doubleXref) {
734
53.2k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
53.2k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
53.2k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
53.2k
    return id;
740
53.2k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
414k
{
746
414k
    int64_t id = pdf_next_id(pdev);
747
414k
    gs_offset_t pos = 0;
748
749
414k
    if (pdev->doubleXref) {
750
414k
        if (pdev->strm == pdev->ObjStm.strm)
751
14.0k
            pos = pdev->ObjStm_id;
752
400k
        else
753
400k
            pos = 0;
754
414k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
414k
        if (pdev->strm == pdev->ObjStm.strm)
756
14.0k
            pos = pdev->NumObjStmObjects;
757
400k
        else
758
400k
            pos = pdf_stell(pdev);
759
414k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
414k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
414k
    return id;
766
414k
}
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
48
{
780
48
    gp_file *tfile = pdev->xref.file;
781
48
    int64_t tpos = gp_ftell(tfile);
782
48
    gs_offset_t pos = 0;
783
784
48
    if (pdev->doubleXref) {
785
48
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
48
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
48
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
48
    }
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
48
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
48
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
48
    return 0;
800
48
}
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
391k
{
806
391k
    stream *s = pdev->strm;
807
808
391k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
391k
    if (id <= 0) {
812
127k
        id = pdf_obj_ref(pdev);
813
264k
    } else {
814
264k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
264k
        gp_file *tfile = pdev->xref.file;
816
264k
        int64_t tpos = gp_ftell(tfile);
817
818
264k
        if (pdev->doubleXref) {
819
264k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
264k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
264k
            if (pdev->strm == pdev->ObjStm.strm)
823
71.6k
                fake_pos = pdev->ObjStm_id;
824
264k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
264k
            if (pdev->strm == pdev->ObjStm.strm)
826
71.6k
                pos = pdev->NumObjStmObjects;
827
264k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
264k
        } else {
829
0
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
830
0
                  SEEK_SET) != 0)
831
0
              return_error(gs_error_ioerror);
832
0
            gp_fwrite(&pos, sizeof(pos), 1, tfile);
833
0
        }
834
264k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
264k
    }
837
391k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
208k
        switch(type) {
839
851
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
851
                break;
844
29.6k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
29.6k
                break;
847
1.89k
            case resourceColorSpace:
848
1.89k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
1.89k
                break;
850
9.64k
            case resourceExtGState:
851
9.64k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
9.64k
                break;
853
851
            case resourcePattern:
854
851
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
851
                break;
856
0
            case resourceShading:
857
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Shading obj_%"PRId64")\n", id);
858
0
                break;
859
0
            case resourceCIDFont:
860
23.5k
            case resourceFont:
861
                /* Ought to write the font name here */
862
23.5k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
23.5k
                break;
864
80.9k
            case resourceCharProc:
865
80.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
80.9k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
10.2k
            case resourceFontDescriptor:
871
10.2k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
10.2k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.04k
            case resourceFunction:
877
2.04k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.04k
                break;
879
4.96k
            case resourceEncoding:
880
4.96k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
4.96k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
10.9k
            case resourceHalftone:
886
10.9k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
10.9k
                break;
888
0
            case resourceLength:
889
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Length obj_%"PRId64")\n", id);
890
0
                break;
891
0
            case resourceSoftMaskDict:
892
                /* This should not be possible, not valid in PostScript */
893
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF SoftMask obj_%"PRId64")\n", id);
894
0
                break;
895
13
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
13
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
13
                break;
899
0
            case resourceStream:
900
                /* Possibly we should not add comments to this type */
901
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF stream obj_%"PRId64")\n", id);
902
0
                break;
903
0
            case resourceOutline:
904
                /* This should not be possible, not valid in PostScript */
905
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Outline obj_%"PRId64")\n", id);
906
0
                break;
907
0
            case resourceArticle:
908
                /* This should not be possible, not valid in PostScript */
909
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Article obj_%"PRId64")\n", id);
910
0
                break;
911
0
            case resourceDests:
912
                /* This should not be possible, not valid in PostScript */
913
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Dests obj_%"PRId64")\n", id);
914
0
                break;
915
0
            case resourceEmbeddedFiles:
916
                /* This should not be possible, not valid in PostScript */
917
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF EmbeddedFiles obj_%"PRId64")\n", id);
918
0
                break;
919
0
            case resourceLabels:
920
                /* This should not be possible, not valid in PostScript */
921
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Page Labels obj_%"PRId64")\n", id);
922
0
                break;
923
0
            case resourceThread:
924
                /* This should not be possible, not valid in PostScript */
925
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Thread obj_%"PRId64")\n", id);
926
0
                break;
927
0
            case resourceCatalog:
928
                /* This should not be possible, not valid in PostScript */
929
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Catalog obj_%"PRId64")\n", id);
930
0
                break;
931
0
            case resourceEncrypt:
932
                /* This should not be possible, not valid in PostScript */
933
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Encryption obj_%"PRId64")\n", id);
934
0
                break;
935
0
            case resourcePagesTree:
936
                /* This should not be possible, not valid in PostScript */
937
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%"PRId64")\n", id);
938
0
                break;
939
0
            case resourceMetadata:
940
                /* This should not be possible, not valid in PostScript */
941
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Metadata obj_%"PRId64")\n", id);
942
0
                break;
943
0
            case resourceICC:
944
                /* This should not be possible, not valid in PostScript */
945
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%"PRId64")\n", id);
946
0
                break;
947
0
            case resourceAnnotation:
948
                /* This should not be possible, not valid in PostScript */
949
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Annotation obj_%"PRId64")\n", id);
950
0
                break;
951
10.1k
            case resourceFontFile:
952
10.1k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
10.1k
                break;
954
23.0k
            default:
955
23.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
23.0k
                break;
957
208k
        }
958
208k
    }
959
391k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
308k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
391k
    return id;
962
391k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
14.2k
{
966
14.2k
    return pdf_open_obj(pdev, 0L, type);
967
14.2k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
391k
{
973
391k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
308k
        stream_puts(pdev->strm, "endobj\n");
975
391k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
208k
        switch(type) {
977
29.6k
            case resourcePage:
978
29.6k
                break;
979
179k
            default:
980
179k
            stream_puts(pdev->strm, "%%EndResource\n");
981
179k
            break;
982
208k
        }
983
208k
    }
984
391k
    return 0;
985
391k
}
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
100k
{
1034
100k
    gs_memory_t *mem = pdev->v_memory;
1035
100k
    stream_arcfour_state *ss;
1036
100k
    gs_md5_byte_t key[16];
1037
100k
    int code, keylength;
1038
1039
100k
    if (!pdev->KeyLength)
1040
100k
        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
43.8k
{
1065
43.8k
    stream *s;
1066
43.8k
    int code;
1067
1068
43.8k
    if (pdev->contents_id != 0)
1069
9
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
43.8k
    pdev->compression_at_page_start = pdev->compression;
1071
43.8k
    if (pdev->ResourcesBeforeUsage) {
1072
29.6k
        pdf_resource_t *pres;
1073
1074
29.6k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
29.6k
                    true, pdev->params.CompressPages);
1076
29.6k
        if (code < 0)
1077
0
            return code;
1078
29.6k
        pdev->contents_id = pres->object->id;
1079
29.6k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
29.6k
        pdev->contents_pos = -1; /* inapplicable */
1081
29.6k
        s = pdev->strm;
1082
29.6k
    } else {
1083
14.2k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
14.2k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
14.2k
        s = pdev->strm;
1086
14.2k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
14.2k
        if (pdev->compression == pdf_compress_Flate) {
1088
14.2k
            if (pdev->binary_ok)
1089
14.2k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
14.2k
        }
1093
14.2k
        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
14.2k
        stream_puts(s, ">>\nstream\n");
1100
14.2k
        pdev->contents_pos = pdf_stell(pdev);
1101
14.2k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
14.2k
        if (code < 0)
1103
0
            return code;
1104
14.2k
        pdev->strm = s;
1105
14.2k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
14.2k
            const stream_template *templat;
1107
14.2k
            stream *es;
1108
14.2k
            byte *buf;
1109
14.2k
            Flate_filter_state *st;
1110
1111
14.2k
            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
14.2k
            templat = &Flate_filter_template;
1131
14.2k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
14.2k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
14.2k
                                       "PDF compression buffer");
1134
14.2k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
14.2k
                                 templat->stype, "PDF compression state");
1136
14.2k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
14.2k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
14.2k
                       s_mode_write);
1140
14.2k
            st->memory = pdev->pdf_memory;
1141
14.2k
            st->templat = templat;
1142
14.2k
            es->state = (stream_state *) st;
1143
14.2k
            es->procs.process = templat->process;
1144
14.2k
            es->strm = s;
1145
14.2k
            (*templat->set_defaults) ((stream_state *) st);
1146
14.2k
            code = (*templat->init) ((stream_state *) st);
1147
14.2k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
14.2k
            pdev->strm = s = es;
1152
14.2k
        }
1153
14.2k
        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
14.2k
    }
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
43.8k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
43.8k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
43.8k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
14.2k
        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
14.2k
    }
1218
43.8k
    pdev->AR4_save_bug = false;
1219
43.8k
    return PDF_IN_STREAM;
1220
43.8k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
127k
{
1225
127k
    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
127k
    if (!pdev->clipped_text_pending) {
1233
127k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
127k
        if (code < 0)
1235
0
            return 0;
1236
127k
    }
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
127k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
127k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
127k
    pdev->procsets |= Text;
1248
127k
    code = pdf_from_stream_to_text(pdev);
1249
127k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
127k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
127k
{
1255
127k
    int code = pdf_from_string_to_text(pdev);
1256
1257
127k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
127k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
127k
{
1263
127k
    int code;
1264
1265
127k
    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
127k
    if (pdev->clipped_text_pending)
1273
54
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
54
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
127k
    else {
1276
127k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
127k
        if (code < 0)
1278
0
            return code;
1279
127k
        pdf_reset_text(pdev); /* because of Q */
1280
127k
    }
1281
127k
    return PDF_IN_STREAM;
1282
127k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
43.8k
{
1287
43.8k
    stream *s = pdev->strm;
1288
43.8k
    gs_offset_t length;
1289
43.8k
    int code;
1290
43.8k
    stream *target;
1291
43.8k
     char str[21];
1292
1293
43.8k
    if (pdev->ResourcesBeforeUsage) {
1294
29.6k
        int code = pdf_exit_substream(pdev);
1295
1296
29.6k
        if (code < 0)
1297
0
            return code;
1298
29.6k
    } else {
1299
14.2k
        if (pdev->vgstack_depth) {
1300
52
            code = pdf_restore_viewer_state(pdev, s);
1301
52
            if (code < 0)
1302
2
                return code;
1303
52
        }
1304
14.2k
        target = pdev->strm;
1305
1306
14.2k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
14.2k
            target = target->strm;
1308
14.2k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
14.2k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
14.2k
        s_close_filters(&pdev->strm, target);
1313
1314
14.2k
        s = pdev->strm;
1315
14.2k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
14.2k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
14.2k
        stream_puts(s, "endstream\n");
1319
14.2k
        pdf_end_obj(pdev, resourceStream);
1320
1321
14.2k
        if (pdev->WriteObjStms) {
1322
14.2k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
14.2k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
14.2k
            stream_puts(pdev->strm, str);
1325
14.2k
            pdf_end_separate(pdev, resourceLength);
1326
14.2k
        } else {
1327
0
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
0
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
0
            stream_puts(s, str);
1330
0
            pdf_end_obj(pdev, resourceLength);
1331
0
        }
1332
14.2k
    }
1333
43.8k
    return PDF_IN_NONE;
1334
43.8k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
12.8M
{
1340
12.8M
    int (*proc) (gx_device_pdf *);
1341
1342
13.2M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
471k
        int code = (*proc) (pdev);
1344
1345
471k
        if (code < 0)
1346
11
            return code;
1347
471k
        pdev->context = (pdf_context_t) code;
1348
471k
    }
1349
12.8M
    pdev->context = context;
1350
12.8M
    return 0;
1351
12.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
43.8k
{
1357
43.8k
    if (pdev->context == PDF_IN_NONE)
1358
2
        return 0;
1359
43.8k
    if (last) {     /* Exit from the clipping path gsave. */
1360
43.8k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
43.8k
        if (code < 0)
1363
0
            return code;
1364
43.8k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
43.8k
        pdf_close_text_contents(pdev);
1366
43.8k
    }
1367
43.8k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
43.8k
}
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
123k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
123k
    pres->where_used = 0;
1386
123k
    if (pres->object) {
1387
123k
        pres->object->written = true;
1388
123k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
74.1k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
74.1k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
74.1k
            if (code < 0)
1393
0
                return code;
1394
74.1k
        }
1395
123k
        cos_release(pres->object, "pdf_cancel_resource");
1396
123k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
123k
        pres->object = 0;
1398
123k
    }
1399
123k
    return 0;
1400
123k
}
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
122k
{   /* fixme : optimize. */
1406
122k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
122k
    pdf_resource_t *pres;
1408
122k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
122k
    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.46M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
1.34M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
1.34M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
1.34M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
2
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
2
        }
1424
1.34M
    }
1425
1426
125k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
125k
        if (pres == pres1) {
1428
122k
            *pprev = pres->prev;
1429
122k
            break;
1430
122k
        }
1431
1432
122k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
122k
        pprev = pchain + i;
1434
124k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
124k
            if (pres == pres1) {
1436
122k
                *pprev = pres->next;
1437
122k
                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
122k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
122k
                return;
1444
122k
            }
1445
122k
    }
1446
122k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
33.9k
{
1451
33.9k
    return 1;
1452
33.9k
}
1453
1454
/* Substitute a resource with a same one. */
1455
/* NB we cannot substitute resources which have already had an
1456
   id assigned to them, because they already have an entry in the
1457
   xref table. If we want to substiute a resource then it should
1458
   have been allocated with an initial id of -1.
1459
   (see pdf_alloc_resource)
1460
*/
1461
int
1462
pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
1463
        pdf_resource_type_t rtype,
1464
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
1465
        bool write)
1466
85.7k
{
1467
85.7k
    pdf_resource_t *pres1 = *ppres;
1468
85.7k
    int code;
1469
1470
85.7k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
85.7k
    if (code < 0)
1472
0
        return code;
1473
85.7k
    if (code != 0) {
1474
54.5k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
54.5k
        if (code < 0)
1476
0
            return code;
1477
54.5k
        pdf_forget_resource(pdev, pres1, rtype);
1478
54.5k
        return 0;
1479
54.5k
    } else {
1480
31.1k
        if (pres1->object->id < 0)
1481
31.1k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
31.1k
        if (write) {
1483
14.5k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
14.5k
            if (code < 0)
1485
0
                return code;
1486
14.5k
            pres1->object->written = 1;
1487
14.5k
        }
1488
31.1k
        return 1;
1489
31.1k
    }
1490
85.7k
}
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
880k
{
1497
880k
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
880k
    pdf_resource_t **pprev = pchain;
1499
880k
    pdf_resource_t *pres;
1500
1501
2.05M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
1.94M
        if (pres->rid == rid) {
1503
771k
            if (pprev != pchain) {
1504
270k
                *pprev = pres->next;
1505
270k
                pres->next = *pchain;
1506
270k
                *pchain = pres;
1507
270k
            }
1508
771k
            return pres;
1509
771k
        }
1510
109k
    return 0;
1511
880k
}
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
15.2k
{
1517
15.2k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
15.2k
    pdf_resource_t *pres;
1519
15.2k
    int i;
1520
1521
257k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
2.08M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
1.84M
            if (pres->object && pres->object->id == id)
1524
270
                return pres;
1525
1.84M
        }
1526
242k
    }
1527
15.0k
    return 0;
1528
15.2k
}
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
100k
{
1535
100k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
100k
    pdf_resource_t *pres;
1537
100k
    cos_object_t *pco0 = (*ppres)->object;
1538
100k
    int i;
1539
1540
829k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
2.80M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
2.07M
            if (*ppres != pres) {
1543
1.97M
                int code;
1544
1.97M
                cos_object_t *pco1 = pres->object;
1545
1546
1.97M
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
49.3k
                    continue;      /* don't compare different types */
1548
1.92M
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
1.92M
                if (code < 0)
1550
0
                    return code;
1551
1.92M
                if (code > 0) {
1552
55.6k
                    code = eq(pdev, *ppres, pres);
1553
55.6k
                    if (code < 0)
1554
0
                        return code;
1555
55.6k
                    if (code > 0) {
1556
55.6k
                        *ppres = pres;
1557
55.6k
                        return 1;
1558
55.6k
                    }
1559
55.6k
                }
1560
1.92M
            }
1561
2.07M
        }
1562
783k
    }
1563
45.2k
    return 0;
1564
100k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
2.43k
{
1569
2.43k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
2.43k
    pdf_resource_t *pres;
1571
2.43k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
2.43k
    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
29.2k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
26.8k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
26.8k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
26.8k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
26.8k
    }
1588
1589
3.00k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
3.00k
        if (pres == pres1) {
1591
2.43k
            *pprev = pres->prev;
1592
2.43k
            break;
1593
2.43k
        }
1594
1595
2.43k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
2.43k
        pprev = pchain + i;
1597
2.54k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
2.54k
            if (pres == pres1) {
1599
2.43k
                *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
2.43k
                return;
1609
2.43k
            }
1610
2.43k
    }
1611
2.43k
}
1612
1613
/* Drop resources by a condition. */
1614
void
1615
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1616
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1617
0
{
1618
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1619
0
    pdf_resource_t **pprev;
1620
0
    pdf_resource_t *pres;
1621
0
    int i;
1622
1623
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1624
0
        pprev = pchain + i;
1625
0
        for (; (pres = *pprev) != 0; ) {
1626
0
            if (cond(pdev, pres)) {
1627
0
                *pprev = pres->next;
1628
0
                pres->next = pres; /* A temporary mark - see below */
1629
0
            } else
1630
0
                pprev = &pres->next;
1631
0
        }
1632
0
    }
1633
0
    pprev = &pdev->last_resource;
1634
0
    for (; (pres = *pprev) != 0; )
1635
0
        if (pres->next == pres) {
1636
0
            *pprev = pres->prev;
1637
0
            if (pres->object) {
1638
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1639
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1640
0
                pres->object = 0;
1641
0
            }
1642
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1643
0
        } else
1644
0
            pprev = &pres->prev;
1645
0
}
1646
1647
/* Print resource statistics. */
1648
void
1649
pdf_print_resource_statistics(gx_device_pdf * pdev)
1650
0
{
1651
1652
0
    int rtype;
1653
1654
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1655
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1656
0
        pdf_resource_t *pres;
1657
0
        const char *name = pdf_resource_type_names[rtype];
1658
0
        int i, n = 0;
1659
1660
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1661
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1662
0
        }
1663
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1664
0
                (name ? name : ""), n);
1665
0
    }
1666
0
}
1667
1668
int FlushObjStm(gx_device_pdf *pdev)
1669
9.42k
{
1670
9.42k
    int code = 0, i, len = 0, id = 0, end;
1671
9.42k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
9.42k
    pdf_resource_t *pres;
1673
9.42k
    int options = DATA_STREAM_BINARY;
1674
1675
9.42k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
9.42k
    pdev->WriteObjStms = false;
1679
1680
9.42k
    sflush(pdev->strm);
1681
9.42k
    sflush(pdev->ObjStm.strm);
1682
9.42k
    end = stell(pdev->ObjStm.strm);
1683
1684
9.42k
    if (pdev->CompressStreams)
1685
9.42k
        options |= DATA_STREAM_COMPRESS;
1686
1687
9.42k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
9.42k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
9.42k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
9.42k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
9.42k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
9.42k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
9.42k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
9.42k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
93.0k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
83.5k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
83.5k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
83.5k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
83.5k
        strcat(offsets, offset);
1713
83.5k
    }
1714
1715
9.42k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
9.42k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
9.42k
    stream_puts(pdev->strm, offsets);
1723
1724
9.42k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
9.42k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
9.42k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
9.42k
    code = pdf_close_aside(pdev);
1732
9.42k
    if (code < 0)
1733
0
        return code;
1734
9.42k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
9.42k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
9.42k
    pdev->WriteObjStms = true;
1740
9.42k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
9.42k
    if (pdev->ObjStmOffsets != NULL) {
1742
9.42k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
9.42k
        pdev->ObjStmOffsets = NULL;
1744
9.42k
    }
1745
9.42k
    pdev->NumObjStmObjects = 0;
1746
9.42k
    pdev->ObjStm_id = 0;
1747
1748
9.42k
    pdev->WriteObjStms = true;
1749
9.42k
    return code;
1750
9.42k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
9.42k
{
1754
9.42k
    int code;
1755
1756
9.42k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
9.42k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
9.42k
    if (code < 0)
1760
0
        return code;
1761
1762
9.42k
    pdev->NumObjStmObjects = 0;
1763
9.42k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
9.42k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
9.42k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
9.42k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
9.42k
    return code;
1772
9.42k
}
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
63.9k
{
1778
63.9k
    int code;
1779
1780
63.9k
    code = pdfwrite_pdf_open_document(pdev);
1781
63.9k
    if (code < 0)
1782
0
        return code;
1783
63.9k
    pdev->asides.save_strm = pdev->strm;
1784
63.9k
    pdev->strm = pdev->asides.strm;
1785
63.9k
    code = pdf_open_obj(pdev, id, type);
1786
63.9k
    return code;
1787
63.9k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
179k
{
1791
179k
    if (type == resourceStream)
1792
0
        return true;
1793
179k
    if (type == resourceCharProc)
1794
50
        return true;
1795
179k
    if (type == resourcePattern)
1796
9.73k
        return true;
1797
169k
    if (type == resourceXObject)
1798
2.50k
        return true;
1799
167k
    return false;
1800
169k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
250k
{
1805
250k
    int code;
1806
1807
250k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
167k
        code = pdfwrite_pdf_open_document(pdev);
1809
167k
        if (code < 0)
1810
0
            return code;
1811
167k
        pdev->asides.save_strm = pdev->strm;
1812
167k
        pdev->strm = pdev->asides.strm;
1813
167k
        code = pdf_open_obj(pdev, id, type);
1814
167k
    } else {
1815
83.5k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
61
            code = FlushObjStm(pdev);
1817
61
            if (code < 0)
1818
0
                return code;
1819
61
        }
1820
83.5k
        if (!pdev->ObjStm.strm) {
1821
9.42k
            code = NewObjStm(pdev);
1822
9.42k
            if (code < 0)
1823
0
                return code;
1824
9.42k
        }
1825
83.5k
        pdev->ObjStm.save_strm = pdev->strm;
1826
83.5k
        pdev->strm = pdev->ObjStm.strm;
1827
83.5k
        code = pdf_open_obj(pdev, id, type);
1828
83.5k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
83.5k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
83.5k
    }
1831
250k
    return code;
1832
250k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
112k
{
1836
112k
    return pdf_open_separate(pdev, 0L, type);
1837
112k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
244k
{
1842
244k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
244k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
244k
}
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
376k
{
1852
376k
    pdf_resource_t *pres;
1853
376k
    cos_object_t *object;
1854
1855
376k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
376k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
376k
                           "pdf_alloc_aside(resource)");
1859
376k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
376k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
376k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
376k
    memset(pres, 0, pst->ssize);
1865
376k
    pres->object = object;
1866
376k
    if (id < 0) {
1867
231k
        object->id = -1L;
1868
231k
        pres->rname[0] = 0;
1869
231k
    } else
1870
144k
        pdf_reserve_object_id(pdev, pres, id);
1871
376k
    pres->next = *plist;
1872
376k
    pres->rid = 0;
1873
376k
    *plist = pres;
1874
376k
    pres->prev = pdev->last_resource;
1875
376k
    pdev->last_resource = pres;
1876
376k
    pres->named = false;
1877
376k
    pres->global = false;
1878
376k
    pres->where_used = pdev->used_mask;
1879
376k
    *ppres = pres;
1880
376k
    return 0;
1881
376k
}
1882
int64_t
1883
pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1884
                const gs_memory_struct_type_t * pst, pdf_resource_t ** ppres,
1885
                pdf_resource_type_t type)
1886
77.0k
{
1887
77.0k
    int64_t id = pdf_begin_separate(pdev, type);
1888
77.0k
    int code = 0;
1889
1890
77.0k
    if (id < 0)
1891
0
        return (int)id;
1892
77.0k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
77.0k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
77.0k
    return code;
1897
77.0k
}
1898
1899
/* Begin a resource of a given type. */
1900
int
1901
pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1902
                        gs_id rid, pdf_resource_t ** ppres)
1903
77.0k
{
1904
77.0k
    int code;
1905
1906
77.0k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
77.0k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
77.0k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
77.0k
    if (code >= 0)
1913
77.0k
        (*ppres)->rid = rid;
1914
77.0k
    return code;
1915
77.0k
}
1916
int
1917
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1918
                   pdf_resource_t ** ppres)
1919
76.7k
{
1920
76.7k
    int code;
1921
1922
76.7k
    if (rtype >= NUM_RESOURCE_TYPES)
1923
0
        rtype = resourceOther;
1924
1925
76.7k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1926
1927
76.7k
    if (code >= 0 && pdf_resource_type_names[rtype] != 0) {
1928
0
        stream *s = pdev->strm;
1929
1930
0
        pprints1(s, "<</Type%s", pdf_resource_type_names[rtype]);
1931
0
        pprinti64d1(s, "/Name/R%"PRId64, (*ppres)->object->id);
1932
0
    }
1933
76.7k
    return code;
1934
76.7k
}
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
145k
{
1952
145k
    int code;
1953
1954
145k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
145k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
145k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
145k
    if (code >= 0)
1961
145k
        (*ppres)->rid = rid;
1962
145k
    return code;
1963
145k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
1.50M
{
1969
1.50M
    return pres->object->id;
1970
1.50M
}
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
63.9k
{
1976
63.9k
    int code = pdf_end_obj(pdev, type);
1977
1978
63.9k
    pdev->strm = pdev->asides.save_strm;
1979
63.9k
    pdev->asides.save_strm = 0;
1980
63.9k
    return code;
1981
63.9k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
250k
{
1985
250k
    int code = pdf_end_obj(pdev, type);
1986
1987
250k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
167k
        pdev->strm = pdev->asides.save_strm;
1989
167k
        pdev->asides.save_strm = 0;
1990
167k
    } else {
1991
83.5k
        pdev->strm = pdev->ObjStm.save_strm;
1992
83.5k
        pdev->ObjStm.save_strm = 0;
1993
83.5k
        pdev->NumObjStmObjects++;
1994
83.5k
    }
1995
250k
    return code;
1996
250k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
337
{
2000
337
    return pdf_end_separate(pdev, type);
2001
337
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
337
{
2007
337
    return pdf_end_aside(pdev, type);
2008
337
}
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
434k
{
2018
434k
    int j, code = 0;
2019
2020
7.38M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
6.95M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
7.13M
        for (; pres != 0; pres = pres->next)
2024
180k
            if ((!pres->named || pdev->ForOPDFRead)
2025
180k
                && pres->object && !pres->object->written) {
2026
29.0k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
29.0k
            }
2028
6.95M
    }
2029
434k
    return code;
2030
434k
}
2031
2032
/*
2033
 * Reverse resource chains.
2034
 * ps2write uses it with page resources.
2035
 * Assuming only the 0th chain contauns something.
2036
 */
2037
void
2038
pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2039
23.0k
{
2040
23.0k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
23.0k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
23.0k
    if (pres == NULL)
2044
0
        return;
2045
23.0k
    pres1 = pres->next;
2046
29.6k
    for (;;) {
2047
29.6k
        if (pres1 == NULL)
2048
23.0k
            break;
2049
6.55k
        pres2 = pres1->next;
2050
6.55k
        pres1->next = pres;
2051
6.55k
        pres = pres1;
2052
6.55k
        pres1 = pres2;
2053
6.55k
    }
2054
23.0k
    pres0->next = NULL;
2055
23.0k
    pdev->resources[rtype].chains[0] = pres;
2056
23.0k
}
2057
2058
/*
2059
 * Free unnamed Cos objects for resources local to a content stream,
2060
 * since they can't be used again.
2061
 */
2062
int
2063
pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2064
227k
{
2065
227k
    int j;
2066
2067
3.85M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
3.63M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
3.63M
        pdf_resource_t *pres;
2070
2071
3.71M
        while ((pres = *prev) != 0) {
2072
85.8k
            if (pres->named) { /* named, don't free */
2073
6
                prev = &pres->next;
2074
85.7k
            } else {
2075
85.7k
                if (pres->object) {
2076
85.7k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
85.7k
                    pres->object = 0;
2078
85.7k
                }
2079
85.7k
                *prev = pres->next;
2080
85.7k
            }
2081
85.8k
        }
2082
3.63M
    }
2083
227k
    return 0;
2084
227k
}
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
43.8k
{
2093
43.8k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
394k
    for (i = 0; i <= resourceFont; ++i) {
2098
350k
        stream *s = 0;
2099
350k
        int j;
2100
2101
350k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
43.8k
            continue;
2103
306k
        page->resource_ids[i] = 0;
2104
5.21M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
4.91M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
5.07M
            for (; pres != 0; pres = pres->next) {
2108
160k
                if (pres->where_used & pdev->used_mask) {
2109
85.9k
                    int64_t id = pdf_resource_id(pres);
2110
2111
85.9k
                    if (id == -1L)
2112
7.27k
                        continue;
2113
78.6k
                    if (s == 0) {
2114
24.3k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
24.3k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
24.3k
                        s = pdev->strm;
2117
24.3k
                        stream_puts(s, "<<");
2118
24.3k
                    }
2119
78.6k
                    pprints1(s, "/%s\n", pres->rname);
2120
78.6k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
78.6k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
78.6k
                    if (clear_usage)
2123
78.6k
                        pres->where_used -= pdev->used_mask;
2124
78.6k
                }
2125
160k
            }
2126
4.91M
        }
2127
306k
        if (s) {
2128
24.3k
            stream_puts(s, ">>\n");
2129
24.3k
            pdf_end_separate(pdev, i);
2130
24.3k
        }
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
306k
        if (i != resourceFont && i != resourceProperties)
2137
219k
            pdf_write_resource_objects(pdev, i);
2138
306k
    }
2139
43.8k
    page->procsets = pdev->procsets;
2140
43.8k
    return 0;
2141
43.8k
}
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
190k
{
2147
190k
    gs_offset_t r, left = count;
2148
190k
    byte buf[sbuf_size];
2149
2150
2.09M
    while (left > 0) {
2151
1.90M
        uint copy = min(left, sbuf_size);
2152
2153
1.90M
        r = gp_fread(buf, 1, copy, file);
2154
1.90M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
1.90M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
1.90M
        stream_write(s, buf, copy);
2160
1.90M
        left -= copy;
2161
1.90M
    }
2162
190k
    return 0;
2163
190k
}
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
123k
{
2170
123k
    int64_t r, left = count;
2171
2172
752k
    while (left > 0) {
2173
629k
        byte buf[sbuf_size];
2174
629k
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
629k
        int64_t end_pos = gp_ftell(file);
2176
2177
629k
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
629k
        r = gp_fread(buf, 1, copy, file);
2181
629k
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
629k
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
629k
        stream_write(s, buf, copy);
2188
629k
        sflush(s);
2189
629k
        left -= copy;
2190
629k
    }
2191
123k
    return 0;
2192
123k
}
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
146k
{
2201
146k
    cos_dict_t *Page;
2202
2203
146k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
146k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
72
        uint new_num_pages;
2207
72
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
72
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
72
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
72
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
72
                             "pdf_page_id(resize pages)");
2216
2217
72
        if (new_pages == 0)
2218
0
            return 0;
2219
72
        memset(&new_pages[pdev->num_pages], 0,
2220
72
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
72
        pdev->pages = new_pages;
2222
72
        pdev->num_pages = new_num_pages;
2223
72
    }
2224
146k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
43.8k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
43.8k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
43.8k
        Page->id = pdf_obj_forward_ref(pdev);
2230
43.8k
    }
2231
146k
    return Page->id;
2232
146k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
4.71M
{
2238
4.71M
    return &pdev->pages[pdev->next_page];
2239
4.71M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
6.40k
{
2245
6.40k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
6.40k
    return pdev->pages[pdev->next_page].Page;
2248
6.40k
}
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
12.4M
{
2267
12.4M
    if (!is_in_page(pdev)) {
2268
43.4k
        int code;
2269
2270
43.4k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
43.4k
        code = pdfwrite_pdf_open_document(pdev);
2273
43.4k
        if (code < 0)
2274
0
            return code;
2275
43.4k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
12.4M
    return pdf_open_contents(pdev, context);
2278
12.4M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
97.0k
{
2284
97.0k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
97.0k
    if (pdev->sbstack_depth <= bottom) {
2289
90.9k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
90.9k
        if (code < 0)
2292
0
            return code;
2293
90.9k
    }
2294
97.0k
    if (pdev->context > PDF_IN_STREAM) {
2295
0
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2296
2297
0
        if (code < 0)
2298
0
            return code;
2299
0
    }
2300
97.0k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
65.3k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
65.3k
        if (code < 0)
2304
0
            return code;
2305
65.3k
        code = pdf_remember_clip_path(pdev, NULL);
2306
65.3k
        if (code < 0)
2307
0
            return code;
2308
65.3k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
65.3k
    }
2310
97.0k
    return 0;
2311
97.0k
}
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
32.4k
{
2323
32.4k
    int major = (int)(gs_revision / 1000);
2324
32.4k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
32.4k
    int patch = gs_revision % 10;
2326
2327
32.4k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
32.4k
}
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
91.9k
{
2335
91.9k
    stream *s = pdev->strm;
2336
2337
91.9k
    if (before)
2338
89.8k
        stream_puts(s, before);
2339
91.9k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
91.9k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
91.9k
    if (after)
2342
91.9k
        stream_puts(s, after);
2343
91.9k
}
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
2.42M
{
2354
2.42M
    uint i;
2355
2356
12.5M
    for (i = 0; i < size; ++i) {
2357
10.0M
        uint c = nstr[i];
2358
10.0M
        char hex[4];
2359
2360
10.0M
        switch (c) {
2361
10.0M
            default:
2362
10.0M
                if (c >= 0x21 && c <= 0x7e) {
2363
10.0M
                    stream_putc(s, (byte)c);
2364
10.0M
                    break;
2365
10.0M
                }
2366
                /* falls through */
2367
2.39k
            case '#':
2368
2.40k
            case '%':
2369
2.42k
            case '(': case ')':
2370
2.42k
            case '<': case '>':
2371
2.43k
            case '[': case ']':
2372
2.45k
            case '{': case '}':
2373
2.46k
            case '/':
2374
2.46k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
2.46k
                stream_puts(s, hex);
2376
2.46k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
10.0M
        }
2380
10.0M
    }
2381
2.42M
    return 0;
2382
2.42M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
2.42M
{
2386
2.42M
    return &pdf_put_name_chars_1_2;
2387
2.42M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
2.41M
{
2391
2.41M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
2.41M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
2.41M
{
2396
2.41M
    stream_putc(pdev->strm, '/');
2397
2.41M
    return pdf_put_name_chars(pdev, nstr, size);
2398
2.41M
}
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
71.4k
{
2452
71.4k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2453
71.4k
        stream_write(pdev->strm, str, size);
2454
71.4k
        return 0;
2455
71.4k
    } else
2456
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2457
71.4k
}
2458
/* Write an encoded string with possible encryption. */
2459
static int
2460
pdf_put_encoded_string_as_hex(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2461
69.5k
{
2462
69.5k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
69.5k
        int i, oct, width = 0;
2464
69.5k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
69.5k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
69.5k
            stream_write(pdev->strm, "\n", 1);
2468
69.5k
        stream_write(pdev->strm, "<", 1);
2469
69.5k
        width++;
2470
2.17M
        for (i = 1; i < size - 1; i++) {
2471
2.10M
            if (str[i] == '\\') {
2472
136k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2473
135k
                    oct = (str[i+1] - 0x30) * 64;
2474
135k
                    oct += (str[i+2] - 0x30) *8;
2475
135k
                    oct += str[i+3] - 0x30;
2476
135k
                    i+=3;
2477
135k
                } else {
2478
775
                    switch (str[++i]) {
2479
8
                        case 'b' :
2480
8
                            oct = 8;
2481
8
                            break;
2482
630
                        case 't' :
2483
630
                            oct = 9;
2484
630
                            break;
2485
40
                        case 'n' :
2486
40
                            oct = 10;
2487
40
                            break;
2488
35
                        case 'f' :
2489
35
                            oct = 12;
2490
35
                            break;
2491
8
                        case 'r' :
2492
8
                            oct = 13;
2493
8
                            break;
2494
54
                        default:
2495
54
                            oct = str[i];
2496
54
                            break;
2497
775
                    }
2498
775
                }
2499
136k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2500
1.03k
                    stream_write(pdev->strm, "\n", 1);
2501
1.03k
                    width = 0;
2502
1.03k
                }
2503
136k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2504
136k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2505
136k
                width += 2;
2506
1.96M
            } else {
2507
1.96M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
225
                    stream_write(pdev->strm, "\n", 1);
2509
225
                    width = 0;
2510
225
                }
2511
1.96M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
1.96M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
1.96M
                width += 2;
2514
1.96M
            }
2515
2.10M
        }
2516
69.5k
        stream_write(pdev->strm, ">", 1);
2517
69.5k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
69.5k
            stream_write(pdev->strm, "\n", 1);
2519
69.5k
        return 0;
2520
69.5k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
69.5k
}
2523
2524
/* Write an encoded hexadecimal string with possible encryption. */
2525
static int
2526
pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2527
0
{
2528
0
    emprintf(pdev->memory,
2529
0
             "Unimplemented function : pdf_put_encoded_hex_string\n");
2530
0
    stream_write(pdev->strm, str, size);
2531
0
    return_error(gs_error_unregistered);
2532
0
}
2533
/*  Scan an item in a serialized array or dictionary.
2534
    This is a very simplified Postscript lexical scanner.
2535
    It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
2536
    We only need to select strings and encrypt them.
2537
    Other items are passed identically.
2538
    Note we don't reconstruct the nesting of arrays|dictionaries.
2539
*/
2540
static int
2541
pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
2542
0
{
2543
0
    const byte *q = p;
2544
0
    int n = l;
2545
2546
0
    if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2547
0
        return (l > 0 ? 1 : 0);
2548
0
    for (q++, n--; n; q++, n--) {
2549
0
        if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2550
0
            return q - p;
2551
0
        if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
2552
0
            return q - p;
2553
        /* Note : immediate names are not allowed in PDF. */
2554
0
    }
2555
0
    return l;
2556
0
}
2557
2558
/* Write a serialized array or dictionary with possible encryption. */
2559
static int
2560
pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2561
29.3k
{
2562
29.3k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
29.3k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
666
            stream_putc(pdev->strm, (byte)'\n');
2565
666
            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
666
            } else {
2606
666
                stream_write(pdev->strm, vstr, size);
2607
666
            }
2608
28.6k
        } else {
2609
28.6k
            stream_write(pdev->strm, vstr, size);
2610
28.6k
        }
2611
29.3k
    } 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
29.3k
    return 0;
2627
29.3k
}
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
7.35M
{
2637
7.35M
    psdf_write_string(pdev->strm, str, size,
2638
7.35M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
7.35M
    return 0;
2640
7.35M
}
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
3.58M
{
2646
3.58M
    if (size > 0 && vstr[0] == '/')
2647
2.03M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
1.55M
    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
1.55M
    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
1.55M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
25.2k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
1.52M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
4.13k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
1.52M
    else if (size > 1 && vstr[0] == '(') {
2657
141k
        if (pdev->ForOPDFRead)
2658
69.5k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2659
71.4k
        else
2660
71.4k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2661
141k
    }
2662
1.38M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
1.38M
    stream_write(pdev->strm, vstr, size);
2665
1.38M
    return 0;
2666
3.58M
}
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
315k
{
2674
315k
    const char *filter_name = 0;
2675
315k
    bool binary_ok = true;
2676
315k
    stream *fs = s;
2677
315k
    cos_dict_t *decode_parms = 0;
2678
315k
    int code;
2679
2680
1.19M
    for (; fs != 0; fs = fs->strm) {
2681
879k
        const stream_state *st = fs->state;
2682
879k
        const stream_template *templat = st->templat;
2683
2684
879k
#define TEMPLATE_IS(atemp)\
2685
4.85M
  (templat->process == (atemp).process)
2686
879k
        if (TEMPLATE_IS(s_A85E_template))
2687
166k
            binary_ok = false;
2688
713k
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
87.2k
            cos_param_list_writer_t writer;
2690
87.2k
            stream_CF_state cfs;
2691
2692
87.2k
            decode_parms =
2693
87.2k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
87.2k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
87.2k
            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.2k
            cfs = *(const stream_CF_state *)st;
2702
87.2k
            if (cfs.EndOfBlock)
2703
10.5k
                cfs.Rows = 0;
2704
87.2k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
87.2k
            filter_name = pfn->CCITTFaxDecode;
2706
625k
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
8.81k
            filter_name = pfn->DCTDecode;
2708
617k
        else if (TEMPLATE_IS(s_zlibE_template))
2709
75.9k
            filter_name = pfn->FlateDecode;
2710
541k
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
541k
        else if (TEMPLATE_IS(s_LZWE_template))
2713
69.6k
            filter_name = pfn->LZWDecode;
2714
471k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
10.4k
            const stream_PNGP_state *const ss =
2717
10.4k
                (const stream_PNGP_state *)st;
2718
2719
10.4k
            decode_parms =
2720
10.4k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
10.4k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
10.4k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
10.4k
                                         ss->Predictor));
2725
10.4k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
10.4k
                                         ss->Columns));
2727
10.4k
            if (ss->Colors != 1)
2728
10.4k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
10.4k
                                             ss->Colors));
2730
10.4k
            if (ss->BitsPerComponent != 8)
2731
10.4k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
10.4k
                                             "/BitsPerComponent",
2733
10.4k
                                             ss->BitsPerComponent));
2734
461k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
879k
#undef TEMPLATE_IS
2737
879k
    }
2738
315k
    if (filter_name) {
2739
241k
        if (binary_ok) {
2740
105k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
105k
            if (decode_parms)
2742
105k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
105k
                                                COS_OBJECT(decode_parms)));
2744
135k
        } else {
2745
135k
            cos_array_t *pca =
2746
135k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
135k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
135k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
135k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
135k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
135k
                                            COS_OBJECT(pca)));
2754
135k
            if (decode_parms) {
2755
83.2k
                pca = cos_array_alloc(pdev,
2756
83.2k
                                      "pdf_put_image_filters(DecodeParms)");
2757
83.2k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
83.2k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
83.2k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
83.2k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
83.2k
                                                COS_OBJECT(pca)));
2763
83.2k
            }
2764
135k
        }
2765
241k
    } else if (!binary_ok)
2766
73.4k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
315k
    return 0;
2768
315k
}
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
126k
{
2774
126k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
64.3k
                    &s_LZWE_template : &s_zlibE_template);
2776
126k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
126k
                                     "pdf_write_function");
2778
2779
126k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
126k
    if (templat->set_defaults)
2782
126k
        templat->set_defaults(st);
2783
126k
    return psdf_encode_binary(pbw, templat, st);
2784
126k
}
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
153k
{
2817
153k
    stream *s = pdev->strm;
2818
153k
    int options = orig_options;
2819
153k
#define USE_ASCII85 1
2820
267k
#define USE_FLATE 2
2821
153k
#define USE_BROTLI 4
2822
153k
    static const char *const fnames[6] = {
2823
153k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
153k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
153k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
153k
    };
2827
153k
    static const char *const fnames1_2[6] = {
2828
153k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
153k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
153k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
153k
    };
2832
153k
    int filters = 0;
2833
153k
    int code;
2834
2835
153k
    if (options & DATA_STREAM_COMPRESS) {
2836
114k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
114k
        } else {
2840
114k
            filters |= USE_FLATE;
2841
114k
            options |= DATA_STREAM_BINARY;
2842
114k
        }
2843
114k
    }
2844
153k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
38.0k
        filters |= USE_ASCII85;
2846
153k
    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
153k
    if (options & DATA_STREAM_ENCRYPT) {
2860
9.35k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
9.35k
        if (code < 0)
2862
0
            return code;
2863
9.35k
        pdev->strm = s;
2864
9.35k
        pdw->encrypted = true;
2865
9.35k
    } else
2866
143k
        pdw->encrypted = false;
2867
153k
    if (options & DATA_STREAM_BINARY) {
2868
114k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
114k
        if (code < 0)
2870
0
            return code;
2871
114k
    } else {
2872
38.9k
        code = 0;
2873
38.9k
        pdw->binary.target = pdev->strm;
2874
38.9k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
38.9k
        pdw->binary.strm = pdev->strm;
2876
38.9k
    }
2877
153k
    pdw->start = stell(s);
2878
153k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
153k
    else
2881
153k
        if (filters & USE_FLATE)
2882
114k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
153k
    return code;
2884
153k
#undef USE_ASCII85
2885
153k
#undef USE_FLATE
2886
153k
#undef USE_BROTLI
2887
153k
}
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
20.3k
{   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
20.3k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
20.3k
    pdw->binary.target = pdev->strm;
2898
20.3k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
20.3k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
20.3k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
20.3k
                options);
2902
20.3k
    if (object_id != 0)
2903
48
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
20.3k
    pdw->binary.strm = pdev->strm;
2905
20.3k
    return code;
2906
20.3k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
2.79k
{   int code;
2912
2913
2.79k
    code = pdf_close_aside(pdw->pdev);
2914
2.79k
    if (code < 0)
2915
0
        return code;
2916
2.79k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
2.79k
    if (code < 0)
2918
0
        return code;
2919
2.79k
    return 0;
2920
2.79k
}
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
6.38k
{
2929
6.38k
    if (pranges == NULL)
2930
6.38k
        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
19.2k
{
2967
19.2k
    gs_function_info_t info;
2968
19.2k
    cos_param_list_writer_t rlist;
2969
19.2k
    pdf_resource_t *pres;
2970
19.2k
    cos_object_t *pcfn;
2971
19.2k
    cos_dict_t *pcd;
2972
19.2k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
19.2k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
19.2k
    *ppres = pres;
2979
19.2k
    pcfn = pres->object;
2980
19.2k
    gs_function_get_info(pfn, &info);
2981
19.2k
    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
19.2k
    if (info.DataSource != 0) {
2994
12.7k
        psdf_binary_writer writer;
2995
12.7k
        stream *save = pdev->strm;
2996
12.7k
        cos_stream_t *pcos;
2997
12.7k
        stream *s;
2998
2999
12.7k
        cos_become(pcfn, cos_type_stream);
3000
12.7k
        pcos = (cos_stream_t *)pcfn;
3001
12.7k
        pcd = cos_stream_dict(pcos);
3002
12.7k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
12.7k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
12.7k
        pdev->strm = s;
3006
12.7k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
12.7k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
12.7k
            )
3009
12.1k
            code = pdf_flate_binary(pdev, &writer);
3010
12.7k
        if (code >= 0) {
3011
12.7k
            static const pdf_filter_names_t fnames = {
3012
12.7k
                PDF_FILTER_NAMES
3013
12.7k
            };
3014
3015
12.7k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
12.7k
        }
3017
12.7k
        if (code >= 0) {
3018
12.7k
            byte buf[100];    /* arbitrary */
3019
12.7k
            ulong pos;
3020
12.7k
            uint count;
3021
12.7k
            const byte *ptr;
3022
3023
204k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
192k
                count = min(sizeof(buf), info.data_size - pos);
3025
192k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
192k
                                        &ptr);
3027
192k
                stream_write(writer.strm, ptr, count);
3028
192k
            }
3029
12.7k
            code = psdf_end_binary(&writer);
3030
12.7k
            s_close_filters(&s, s->strm);
3031
12.7k
        }
3032
12.7k
        pdev->strm = save;
3033
12.7k
        if (code < 0)
3034
0
            return code;
3035
12.7k
    } else {
3036
6.49k
        cos_become(pcfn, cos_type_dict);
3037
6.49k
        pcd = (cos_dict_t *)pcfn;
3038
6.49k
    }
3039
19.2k
    if (info.Functions != 0) {
3040
1.34k
        cos_array_t *functions =
3041
1.34k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.34k
        cos_value_t v;
3043
3044
1.34k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.34k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.34k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.34k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.34k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.34k
    }
3054
19.2k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
19.2k
    if (code < 0)
3056
0
        return code;
3057
19.2k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
19.2k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
14.7k
{
3062
14.7k
    return true;
3063
14.7k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
19.2k
{
3067
19.2k
    pdf_resource_t *pres;
3068
19.2k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
19.2k
    if (code < 0)
3071
0
        return code;
3072
19.2k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
19.2k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
19.2k
    if (code < 0)
3077
0
        return code;
3078
19.2k
    pres->where_used |= pdev->used_mask;
3079
19.2k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
19.2k
    return 0;
3081
19.2k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.34k
{
3085
1.34k
    int i, code = 0;
3086
1.34k
    cos_value_t v;
3087
3088
6.41k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
5.06k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
5.06k
            (code = cos_array_add(pca, &v)) < 0
3091
5.06k
            ) {
3092
0
            break;
3093
0
        }
3094
5.06k
    }
3095
1.34k
    return code;
3096
1.34k
}
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
7.79k
{
3102
7.79k
    cos_value_t value;
3103
7.79k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
7.79k
    if (code < 0)
3106
0
        return code;
3107
7.79k
    *pid = value.contents.object->id;
3108
7.79k
    return 0;
3109
7.79k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
4.55k
{
3114
4.55k
    char key[] = "/Functions";
3115
4.55k
    cos_value_t *v, v2;
3116
3117
4.55k
    if (cos_type(pco) == cos_type_dict) {
3118
1.73k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
1.73k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
404
            if (cos_type(v->contents.object) == cos_type_array){
3121
404
                int code=0;
3122
2.29k
                while (code == 0) {
3123
1.88k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
1.88k
                }
3125
404
            }
3126
404
        }
3127
1.73k
    }
3128
4.55k
    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
4.55k
    return 0;
3145
4.55k
}
3146
3147
/* Write a FontBBox dictionary element. */
3148
int
3149
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3150
17.5k
{
3151
17.5k
    stream *s = pdev->strm;
3152
    /*
3153
     * AR 4 doesn't like fonts with empty FontBBox, which
3154
     * happens when the font contains only space characters.
3155
     * Small bbox causes AR 4 to display a hairline. So we use
3156
     * the full BBox.
3157
     */
3158
17.5k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
17.5k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
17.5k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
17.5k
             pbox->p.x, pbox->p.y, x, y);
3163
17.5k
    return 0;
3164
17.5k
}
3165
3166
/* Write a FontBBox dictionary element using floats for the values. */
3167
int
3168
pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
3169
2.67k
{
3170
2.67k
    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.67k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
2.67k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
2.67k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
2.67k
             pbox->p.x, pbox->p.y, x, y);
3182
2.67k
    return 0;
3183
2.67k
}