Coverage Report

Created: 2026-09-14 07:34

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-2026 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
13.8k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
13.8k
#  define Flate_filter_name "FlateDecode"
61
13.8k
#  define Flate_filter_template s_zlibE_template
62
13.8k
#  define Flate_filter_state stream_zlib_state
63
0
#  define Brotli_filter_name "BrotliDecode"
64
0
#  define Brotli_filter_template s_brotliE_template
65
0
#  define Brotli_filter_state stream_brotlie_state
66
#else
67
#  define compression_filter_name "LZWDecode"
68
#  define compression_filter_template s_LZWE_template
69
#  define compression_filter_state stream_LZW_state
70
#endif
71
72
/* Import procedures for writing filter parameters. */
73
extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
74
extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
75
76
#define CHECK(expr)\
77
2.11M
  BEGIN if ((code = (expr)) < 0) return code; END
78
79
/* GC descriptors */
80
extern_st(st_pdf_color_space);
81
extern_st(st_pdf_font_resource);
82
extern_st(st_pdf_char_proc);
83
extern_st(st_pdf_font_descriptor);
84
public_st_pdf_resource();
85
private_st_pdf_x_object();
86
private_st_pdf_pattern();
87
88
/* ---------------- Utilities ---------------- */
89
90
#ifdef PS2WRITE_USES_ROMFS
91
/*
92
 * Strip whitespace and comments from a line of PostScript code as possible.
93
 * Return a pointer to any string that remains, or NULL if none.
94
 * Note that this may store into the string.
95
 */
96
/* This function copied from geninit.c . */
97
static char *
98
doit(char *line, bool intact)
99
{
100
    char *str = line;
101
    char *from;
102
    char *to;
103
    int in_string = 0;
104
105
    if (intact)
106
        return str;
107
    while (*str == ' ' || *str == '\t')   /* strip leading whitespace */
108
        ++str;
109
    if (*str == 0)    /* all whitespace */
110
        return NULL;
111
    if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
112
        return str;
113
    if (str[0] == '%')    /* comment line */
114
        return NULL;
115
    /*
116
     * Copy the string over itself removing:
117
     *  - All comments not within string literals;
118
     *  - Whitespace adjacent to '[' ']' '{' '}';
119
     *  - Whitespace before '/' '(' '<';
120
     *  - Whitespace after ')' '>'.
121
     */
122
    for (to = from = str; (*to = *from) != 0; ++from, ++to) {
123
        switch (*from) {
124
            case '%':
125
                if (!in_string)
126
                    break;
127
                continue;
128
            case ' ':
129
            case '\t':
130
                if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
131
                    --to;
132
                continue;
133
            case '(':
134
            case '<':
135
            case '/':
136
            case '[':
137
            case ']':
138
            case '{':
139
            case '}':
140
                if (to > str && !in_string && strchr(" \t", to[-1]))
141
                    *--to = *from;
142
                if (*from == '(')
143
                    ++in_string;
144
                continue;
145
            case ')':
146
                --in_string;
147
                continue;
148
            case '\\':
149
                if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
150
                    *++to = *++from;
151
                continue;
152
            default:
153
                continue;
154
        }
155
        break;
156
    }
157
    /* Strip trailing whitespace. */
158
    while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
159
        --to;
160
    *to = 0;
161
    return str;
162
}
163
164
static int
165
copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
166
{
167
    stream *f;
168
    char buf[1024], *p, *q  = buf;
169
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
170
    bool skipping = false;
171
172
    f = sfopen(fname, "r", s->memory);
173
    if (f == NULL)
174
        return_error(gs_error_undefinedfilename);
175
    n = sfread(buf, 1, m, f);
176
    buf[n] = 0;
177
    do {
178
        if (*q == '\r' || *q == '\n') {
179
            q++;
180
            continue;
181
        }
182
        p = strchr(q, '\r');
183
        if (p == NULL)
184
            p = strchr(q, '\n');
185
        if (p == NULL) {
186
            if (n < m)
187
                p = buf + n;
188
            else {
189
                strcpy(buf, q);
190
                l = strlen(buf);
191
                m = sizeof(buf) - 1 - l;
192
                if (!m) {
193
                    sfclose(f);
194
                    emprintf1(s->memory,
195
                              "The procset %s contains a too long line.",
196
                              fname);
197
                    return_error(gs_error_ioerror);
198
                }
199
                n = sfread(buf + l, 1, m, f);
200
                n += l;
201
                m += l;
202
                buf[n] = 0;
203
                q = buf;
204
                continue;
205
            }
206
        }
207
        *p = 0;
208
        if (q[0] == '%')
209
            l = 0;
210
        else {
211
            q = doit(q, false);
212
            if (q == NULL)
213
                l = 0;
214
            else
215
                l = strlen(q);
216
        }
217
        if (l) {
218
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
219
                skipping = true;
220
            if (!skipping) {
221
                outl += l + 1;
222
                if (outl > 100) {
223
                    q[l] = '\r';
224
                    outl = 0;
225
                } else
226
                    q[l] = ' ';
227
                stream_write(s, q, l + 1);
228
            }
229
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
230
                skipping = false;
231
        }
232
        q = p + 1;
233
    } while (n == m || q < buf + n);
234
    if (outl)
235
        stream_write(s, "\r", 1);
236
    sfclose(f);
237
    return 0;
238
}
239
240
static int
241
copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
242
{
243
    stream *f;
244
    char buf[1024], *p, *q  = buf;
245
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
246
    bool skipping = false;
247
248
    f = sfopen(fname, "r", s->memory);
249
    if (f == NULL)
250
        return_error(gs_error_undefinedfilename);
251
    n = sfread(buf, 1, m, f);
252
    buf[n] = 0;
253
    do {
254
        if (*q == '\r' || *q == '\n') {
255
            q++;
256
            continue;
257
        }
258
        p = strchr(q, '\r');
259
        if (p == NULL)
260
            p = strchr(q, '\n');
261
        if (p == NULL) {
262
            if (n < m)
263
                p = buf + n;
264
            else {
265
                strcpy(buf, q);
266
                l = strlen(buf);
267
                m = sizeof(buf) - 1 - l;
268
                if (!m) {
269
                    sfclose(f);
270
                    emprintf1(s->memory,
271
                              "The procset %s contains a too long line.",
272
                              fname);
273
                    return_error(gs_error_ioerror);
274
                }
275
                n = sfread(buf + l, 1, m, f);
276
                n += l;
277
                m += l;
278
                buf[n] = 0;
279
                q = buf;
280
                continue;
281
            }
282
        }
283
        *p = 0;
284
        if (q[0] == '%')
285
            l = 0;
286
        else {
287
            q = doit(q, false);
288
            if (q == NULL)
289
                l = 0;
290
            else
291
                l = strlen(q);
292
        }
293
        if (l) {
294
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
295
                skipping = true;
296
            if (!skipping) {
297
                outl += l + 1;
298
                if (outl > 100) {
299
                    q[l] = '\n';
300
                    outl = 0;
301
                } else
302
                    q[l] = '\n';
303
                stream_write(s, q, l + 1);
304
            }
305
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
306
                skipping = false;
307
        }
308
        q = p + 1;
309
    } while (n == m || q < buf + n);
310
    if (outl)
311
        stream_write(s, "\r", 1);
312
    sfclose(f);
313
    return 0;
314
}
315
#endif
316
317
static int write_opdfread(stream *s)
318
24.6k
{
319
24.6k
    int index = 0;
320
321
99.7M
    do {
322
99.7M
        if (opdfread_ps[index] == 0x00)
323
24.6k
            break;
324
99.7M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
99.7M
        index++;
326
99.7M
    } while (1);
327
24.6k
    return 0;
328
24.6k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
24.6k
{
332
24.6k
    int index = 0;
333
334
959k
    do {
335
959k
        if (gs_mro_e_ps[index] == 0x00)
336
24.6k
            break;
337
934k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
934k
        index++;
339
934k
    } while (1);
340
341
24.6k
    if (HaveTrueTypes) {
342
24.6k
        char Buffer[256];
343
24.6k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
24.6k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
24.6k
        stream_write(s, Buffer, strlen(Buffer));
347
103M
        while (entry->Glyph) {
348
103M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
103M
            stream_write(s, Buffer, strlen(Buffer));
350
103M
            entry++;
351
103M
        };
352
24.6k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
24.6k
        stream_write(s, Buffer, strlen(Buffer));
354
355
24.6k
        index = 0;
356
885k
        do {
357
885k
            if (gs_mgl_e_ps[index] == 0x00)
358
24.6k
                break;
359
861k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
861k
            index++;
361
861k
        } while (1);
362
24.6k
    }
363
24.6k
    return 0;
364
24.6k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
24.6k
{
369
24.6k
    int code;
370
371
24.6k
    code = write_opdfread(s);
372
24.6k
    if (code < 0)
373
0
        return code;
374
375
24.6k
    code = write_tt_encodings(s, HaveTrueTypes);
376
24.6k
    return code;
377
378
24.6k
}
379
380
static int
381
encode(stream **s, const stream_template *t, gs_memory_t *mem)
382
0
{
383
0
    stream_state *st = s_alloc_state(mem, t->stype, "pdfwrite_pdf_open_document.encode");
384
385
0
    if (st == 0)
386
0
        return_error(gs_error_VMerror);
387
0
    if (t->set_defaults)
388
0
        t->set_defaults(st);
389
0
    if (s_add_filter(s, t, st, mem) == 0) {
390
0
        gs_free_object(mem, st, "pdfwrite_pdf_open_document.encode");
391
0
        return_error(gs_error_VMerror);
392
0
    }
393
0
    return 0;
394
0
}
395
396
/* ------ Document ------ */
397
398
/* Write out the arguments used to invoke GS as a comment in the PDF/PS
399
 * file. We don't write out paths, passwords, or any unrecognised string
400
 * parameters (all sanitised by the arg code) for privacy/security
401
 * reasons. This routine is only called by the PDF linearisation code.
402
 */
403
int
404
pdfwrite_fwrite_args_comment(gx_device_pdf *pdev, gp_file *f)
405
0
{
406
0
    const char * const *argv = NULL;
407
0
    const char *arg;
408
0
    int towrite, length, i, j, argc;
409
410
0
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
411
412
0
    gp_fwrite("%%Invocation:", 13, 1, f);
413
0
    length = 12;
414
0
    for (i=0;i < argc; i++) {
415
0
        arg = argv[i];
416
417
0
        if ((strlen(arg) + length) > 255) {
418
0
            gp_fwrite("\n%%+ ", 5, 1, f);
419
0
            length = 5;
420
0
        } else {
421
0
            gp_fwrite(" ", 1, 1, f);
422
0
            length++;
423
0
        }
424
425
0
        if (strlen(arg) > 250)
426
0
            towrite = 250;
427
0
        else
428
0
            towrite = strlen(arg);
429
430
0
        length += towrite;
431
432
0
        for (j=0;j < towrite;j++) {
433
0
            if (arg[j] == 0x0A) {
434
0
                gp_fwrite("<0A>", 4, 1, f);
435
0
            } else {
436
0
                if (arg[j] == 0x0D) {
437
0
                    gp_fwrite("<0D>", 4, 1, f);
438
0
                } else {
439
0
                    gp_fwrite(&arg[j], 1, 1, f);
440
0
                }
441
0
            }
442
0
        }
443
0
    }
444
0
    gp_fwrite("\n", 1, 1, f);
445
0
    return 0;
446
0
}
447
448
/* Exactly the same as pdfwrite_fwrite_args_comment() above, but uses a stream
449
 * instead of a gp_file *, because of course we aren't consistent.... This
450
 * routine is called by the regular PDF or PS file output code.
451
 */
452
int
453
pdfwrite_write_args_comment(gx_device_pdf *pdev, stream *s)
454
33.7k
{
455
33.7k
    const char * const *argv = NULL;
456
33.7k
    const char *arg;
457
33.7k
    int towrite, length, i, j, argc;
458
459
33.7k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
33.7k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
33.7k
    length = 12;
463
640k
    for (i=0;i < argc; i++) {
464
606k
        arg = argv[i];
465
466
606k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
606k
        } else {
470
606k
            stream_write(s, (byte *)" ", 1);
471
606k
            length++;
472
606k
        }
473
474
606k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
606k
        else
477
606k
            towrite = strlen(arg);
478
479
606k
        length += towrite;
480
481
7.33M
        for (j=0;j < towrite;j++) {
482
6.72M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
6.72M
            } else {
485
6.72M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
6.72M
                } else {
488
6.72M
                    stream_write(s, (byte *)&arg[j], 1);
489
6.72M
                }
490
6.72M
            }
491
6.72M
        }
492
606k
    }
493
33.7k
    stream_write(s, (byte *)"\n", 1);
494
33.7k
    return 0;
495
33.7k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
24.6k
{
499
24.6k
    stream *s = pdev->strm;
500
501
24.6k
    if (pdev->ForOPDFRead) {
502
24.6k
        char cre_date_time[41];
503
24.6k
        int code, status, cre_date_time_len;
504
24.6k
        char BBox[256];
505
506
24.6k
        if (pdev->Eps2Write)
507
12.2k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
12.3k
        else
509
12.3k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
24.6k
        pdfwrite_write_args_comment(pdev, s);
511
        /* We need to calculate the document BoundingBox which is a 'high water'
512
         * mark derived from the BoundingBox of all the individual pages.
513
         */
514
24.6k
        {
515
24.6k
            int pagecount = 1, j;
516
24.6k
            double urx=0, ury=0;
517
518
418k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
393k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
424k
                for (; pres != 0; pres = pres->next)
522
31.3k
                    if ((!pres->named || pdev->ForOPDFRead)
523
31.3k
                        && !pres->object->written) {
524
31.3k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
31.3k
                        if (ceil(page->MediaBox.x) > urx)
526
24.9k
                            urx = ceil(page->MediaBox.x);
527
31.3k
                        if (ceil(page->MediaBox.y) > ury)
528
24.7k
                            ury = ceil(page->MediaBox.y);
529
31.3k
                        pagecount++;
530
31.3k
                    }
531
393k
            }
532
24.6k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
17.8k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
6.75k
            else
535
6.75k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
536
24.6k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
24.6k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
17.8k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
6.75k
            else
540
6.75k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
541
24.6k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
24.6k
        }
543
24.6k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
24.6k
        cre_date_time[cre_date_time_len] = 0;
545
24.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
24.6k
                pdev->dname);
547
24.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
24.6k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
24.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
24.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
24.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
24.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
24.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
24.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
24.6k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
24.6k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
24.6k
        if (pdev->params.CompressPages) {
558
            /*  When CompressEntireFile is true and ASCII85EncodePages is false,
559
                the ASCII85Encode filter is applied, rather one may expect the opposite.
560
                Keeping it so due to no demand for this mode.
561
                A right implementation should compute the length of the compressed procset,
562
                write out an invocation of SubFileDecode filter, and write the length to
563
                there assuming the output file is positionable. */
564
0
            stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
565
0
            code = encode(&s, &s_A85E_template, pdev->pdf_memory);
566
0
            if (code < 0)
567
0
                return code;
568
0
            code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
569
0
            if (code < 0)
570
0
                return code;
571
0
        }
572
24.6k
        stream_puts(s, "10 dict dup begin\n");
573
24.6k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
24.6k
        if (pdev->Eps2Write) {
575
12.2k
            stream_puts(s, "/SetPageSize false def\n");
576
12.2k
            stream_puts(s, "/EPS2Write true def\n");
577
12.3k
        } else {
578
12.3k
            if (pdev->SetPageSize)
579
12.3k
                stream_puts(s, "/SetPageSize true def\n");
580
12.3k
            stream_puts(s, "/EPS2Write false def\n");
581
12.3k
        }
582
24.6k
        stream_puts(s, "end\n");
583
584
24.6k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
24.6k
        if (code < 0)
586
0
            return code;
587
24.6k
        status = s_close_filters(&s, pdev->strm);
588
24.6k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
24.6k
        stream_puts(s, "\n");
591
24.6k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
24.6k
    }
593
24.6k
    return 0;
594
24.6k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
399k
{
600
399k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
399k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
124k
        stream *s = pdev->strm;
605
124k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
124k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
124k
        if (pdev->ForOPDFRead) {
609
115k
            int code, status;
610
115k
            char BBox[256];
611
115k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
115k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
115k
            if (pdev->ProduceDSC)
615
115k
                pdev->CompressEntireFile = 0;
616
0
            else {
617
0
                stream_write(s, (byte *)"%!\r", 3);
618
0
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", width, height);
619
0
                stream_write(s, (byte *)BBox, strlen(BBox));
620
0
                if (pdev->params.CompressPages || pdev->CompressEntireFile) {
621
                    /*  When CompressEntireFile is true and ASCII85EncodePages is false,
622
                        the ASCII85Encode filter is applied, rather one may expect the opposite.
623
                        Keeping it so due to no demand for this mode.
624
                        A right implementation should compute the length of the compressed procset,
625
                        write out an invocation of SubFileDecode filter, and write the length to
626
                        there assuming the output file is positionable. */
627
0
                    stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
628
0
                    code = encode(&s, &s_A85E_template, pdev->pdf_memory);
629
0
                    if (code < 0)
630
0
                        return code;
631
0
                    code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
632
0
                    if (code < 0)
633
0
                        return code;
634
0
                }
635
0
                stream_puts(s, "10 dict dup begin\n");
636
0
                stream_puts(s, "/DSC_OPDFREAD false def\n");
637
0
                if (!pdev->Eps2Write)
638
0
                    stream_puts(s, "/EPS2Write false def\n");
639
0
                if(pdev->SetPageSize)
640
0
                    stream_puts(s, "/SetPageSize true def\n");
641
0
                if(pdev->RotatePages)
642
0
                    stream_puts(s, "/RotatePages true def\n");
643
0
                if(pdev->FitPages)
644
0
                    stream_puts(s, "/FitPages true def\n");
645
0
                if(pdev->CenterPages)
646
0
                    stream_puts(s, "/CenterPages true def\n");
647
0
                stream_puts(s, "end\n");
648
0
                code = copy_procsets(s, pdev->HaveTrueTypes, true);
649
0
                if (code < 0)
650
0
                    return code;
651
0
                if (!pdev->CompressEntireFile) {
652
0
                    status = s_close_filters(&s, pdev->strm);
653
0
                    if (status < 0)
654
0
                        return_error(gs_error_ioerror);
655
0
                } else
656
0
                    pdev->strm = s;
657
0
                pdev->OPDFRead_procset_length = stell(s);
658
0
            }
659
115k
        }
660
124k
        if (!(pdev->ForOPDFRead)) {
661
9.10k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
9.10k
            if (pdev->binary_ok)
663
9.10k
                stream_puts(s, "%\307\354\217\242\n");
664
9.10k
            pdfwrite_write_args_comment(pdev, s);
665
9.10k
        }
666
124k
    }
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
399k
    if (!pdev->params.CompressPages)
677
273k
        pdev->compression = pdf_compress_none;
678
126k
    else {
679
126k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
126k
        else
682
126k
            pdev->compression = pdf_compress_Flate;
683
126k
    }
684
399k
    return 0;
685
399k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
487k
{
693
487k
    return (pdev->next_id)++;
694
487k
}
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
977k
{
706
977k
    stream *s = pdev->strm;
707
977k
    gs_offset_t pos = stell(s);
708
709
977k
    if (s == pdev->asides.strm)
710
254k
        pos |= ASIDES_BASE_POSITION;
711
977k
    return pos;
712
977k
}
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
54.3k
{
730
54.3k
    int64_t id = pdf_next_id(pdev);
731
54.3k
    gs_offset_t pos = 0;
732
733
54.3k
    if (pdev->doubleXref) {
734
54.3k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
54.3k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
54.3k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
54.3k
    return id;
740
54.3k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
432k
{
746
432k
    int64_t id = pdf_next_id(pdev);
747
432k
    gs_offset_t pos = 0;
748
749
432k
    if (pdev->doubleXref) {
750
432k
        if (pdev->strm == pdev->ObjStm.strm)
751
13.6k
            pos = pdev->ObjStm_id;
752
419k
        else
753
419k
            pos = 0;
754
432k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
432k
        if (pdev->strm == pdev->ObjStm.strm)
756
13.6k
            pos = pdev->NumObjStmObjects;
757
419k
        else
758
419k
            pos = pdf_stell(pdev);
759
432k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
432k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
432k
    return id;
766
432k
}
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
406k
{
806
406k
    stream *s = pdev->strm;
807
808
406k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
406k
    if (id <= 0) {
812
142k
        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
66.4k
                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
66.4k
                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
406k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
233k
        switch(type) {
839
1.06k
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
1.06k
                break;
844
31.3k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
31.3k
                break;
847
2.05k
            case resourceColorSpace:
848
2.05k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
2.05k
                break;
850
9.37k
            case resourceExtGState:
851
9.37k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
9.37k
                break;
853
935
            case resourcePattern:
854
935
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
935
                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
26.0k
            case resourceFont:
861
                /* Ought to write the font name here */
862
26.0k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
26.0k
                break;
864
96.6k
            case resourceCharProc:
865
96.6k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
96.6k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
11.3k
            case resourceFontDescriptor:
871
11.3k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
11.3k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.12k
            case resourceFunction:
877
2.12k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.12k
                break;
879
5.77k
            case resourceEncoding:
880
5.77k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
5.77k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
10.3k
            case resourceHalftone:
886
10.3k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
10.3k
                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
11.3k
            case resourceFontFile:
952
11.3k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
11.3k
                break;
954
24.6k
            default:
955
24.6k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
24.6k
                break;
957
233k
        }
958
233k
    }
959
406k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
328k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
406k
    return id;
962
406k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
13.8k
{
966
13.8k
    return pdf_open_obj(pdev, 0L, type);
967
13.8k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
406k
{
973
406k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
328k
        stream_puts(pdev->strm, "endobj\n");
975
406k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
233k
        switch(type) {
977
31.3k
            case resourcePage:
978
31.3k
                break;
979
201k
            default:
980
201k
            stream_puts(pdev->strm, "%%EndResource\n");
981
201k
            break;
982
233k
        }
983
233k
    }
984
406k
    return 0;
985
406k
}
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
114k
{
1034
114k
    gs_memory_t *mem = pdev->v_memory;
1035
114k
    stream_arcfour_state *ss;
1036
114k
    gs_md5_byte_t key[16];
1037
114k
    int code, keylength;
1038
1039
114k
    if (!pdev->KeyLength)
1040
114k
        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
45.1k
{
1065
45.1k
    stream *s;
1066
45.1k
    int code;
1067
1068
45.1k
    if (pdev->contents_id != 0)
1069
13
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
45.1k
    pdev->compression_at_page_start = pdev->compression;
1071
45.1k
    if (pdev->ResourcesBeforeUsage) {
1072
31.3k
        pdf_resource_t *pres;
1073
1074
31.3k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
31.3k
                    true, pdev->params.CompressPages);
1076
31.3k
        if (code < 0)
1077
0
            return code;
1078
31.3k
        pdev->contents_id = pres->object->id;
1079
31.3k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
31.3k
        pdev->contents_pos = -1; /* inapplicable */
1081
31.3k
        s = pdev->strm;
1082
31.3k
    } else {
1083
13.8k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
13.8k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
13.8k
        s = pdev->strm;
1086
13.8k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
13.8k
        if (pdev->compression == pdf_compress_Flate) {
1088
13.8k
            if (pdev->binary_ok)
1089
13.8k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
13.8k
        }
1093
13.8k
        if (pdev->compression == pdf_compress_Brotli) {
1094
0
            if (pdev->binary_ok)
1095
0
                pprints1(s, "/Filter /%s", Brotli_filter_name);
1096
0
            else
1097
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Brotli_filter_name);
1098
0
        }
1099
13.8k
        stream_puts(s, ">>\nstream\n");
1100
13.8k
        pdev->contents_pos = pdf_stell(pdev);
1101
13.8k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
13.8k
        if (code < 0)
1103
0
            return code;
1104
13.8k
        pdev->strm = s;
1105
13.8k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
13.8k
            const stream_template *templat;
1107
13.8k
            stream *es;
1108
13.8k
            byte *buf;
1109
13.8k
            Flate_filter_state *st;
1110
1111
13.8k
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1112
0
                const stream_template *templat2 = &s_A85E_template;
1113
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1114
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1115
0
                                           "PDF contents buffer");
1116
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1117
0
                                templat2->stype, "PDF contents state");
1118
0
                if (as == 0 || ast == 0 || buf == 0)
1119
0
                    return_error(gs_error_VMerror);
1120
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1121
0
                           s_mode_write);
1122
0
                ast->memory = pdev->pdf_memory;
1123
0
                ast->templat = templat2;
1124
0
                as->state = (stream_state *) ast;
1125
0
                as->procs.process = templat2->process;
1126
0
                as->strm = s;
1127
0
                (*templat2->init) ((stream_state *) ast);
1128
0
                pdev->strm = s = as;
1129
0
            }
1130
13.8k
            templat = &Flate_filter_template;
1131
13.8k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
13.8k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
13.8k
                                       "PDF compression buffer");
1134
13.8k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
13.8k
                                 templat->stype, "PDF compression state");
1136
13.8k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
13.8k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
13.8k
                       s_mode_write);
1140
13.8k
            st->memory = pdev->pdf_memory;
1141
13.8k
            st->templat = templat;
1142
13.8k
            es->state = (stream_state *) st;
1143
13.8k
            es->procs.process = templat->process;
1144
13.8k
            es->strm = s;
1145
13.8k
            (*templat->set_defaults) ((stream_state *) st);
1146
13.8k
            if (pdev->StreamEffort > 0) {
1147
0
                stream_zlib_state *const ss = (stream_zlib_state *)st;
1148
0
                int effort = pdev->StreamEffort;
1149
0
                if (effort > 9)
1150
0
                    effort = 9;
1151
0
                ss->level = effort;
1152
0
            }
1153
13.8k
            code = (*templat->init) ((stream_state *) st);
1154
13.8k
            if (code < 0) {
1155
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1156
0
                return code;
1157
0
            }
1158
13.8k
            pdev->strm = s = es;
1159
13.8k
        }
1160
13.8k
        if (pdev->compression == pdf_compress_Brotli) { /* Set up the Brotli filter. */
1161
0
            const stream_template *templat;
1162
0
            stream *es;
1163
0
            byte *buf;
1164
0
            Brotli_filter_state *st;
1165
1166
0
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1167
0
                const stream_template *templat2 = &s_A85E_template;
1168
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1169
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1170
0
                                           "PDF contents buffer");
1171
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1172
0
                                templat2->stype, "PDF contents state");
1173
0
                if (as == 0 || ast == 0 || buf == 0)
1174
0
                    return_error(gs_error_VMerror);
1175
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1176
0
                           s_mode_write);
1177
0
                ast->memory = pdev->pdf_memory;
1178
0
                ast->templat = templat2;
1179
0
                as->state = (stream_state *) ast;
1180
0
                as->procs.process = templat2->process;
1181
0
                as->strm = s;
1182
0
                (*templat2->init) ((stream_state *) ast);
1183
0
                pdev->strm = s = as;
1184
0
            }
1185
0
            templat = &Brotli_filter_template;
1186
0
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1187
0
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1188
0
                                       "PDF compression buffer");
1189
0
            st = gs_alloc_struct(pdev->pdf_memory, Brotli_filter_state,
1190
0
                                 templat->stype, "PDF compression state");
1191
0
            if (es == 0 || st == 0 || buf == 0)
1192
0
                return_error(gs_error_VMerror);
1193
0
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1194
0
                       s_mode_write);
1195
0
            st->memory = pdev->pdf_memory;
1196
0
            st->templat = templat;
1197
0
            es->state = (stream_state *) st;
1198
0
            es->procs.process = templat->process;
1199
0
            es->strm = s;
1200
0
            (*templat->set_defaults) ((stream_state *) st);
1201
0
            if (pdev->StreamEffort > 0) {
1202
0
                stream_brotlie_state *const ss = (stream_brotlie_state *)st;
1203
0
                int effort = pdev->StreamEffort;
1204
0
                if (effort > 11)
1205
0
                    effort = 11;
1206
0
                ss->level = effort;
1207
0
            }
1208
0
            code = (*templat->init) ((stream_state *) st);
1209
0
            if (code < 0) {
1210
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1211
0
                return code;
1212
0
            }
1213
0
            pdev->strm = s = es;
1214
0
        }
1215
13.8k
    }
1216
    /*
1217
     * Scale the coordinate system.  Use an extra level of q/Q for the
1218
     * sake of poorly designed PDF tools that assume that the contents
1219
     * stream restores the CTM.
1220
     */
1221
45.1k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1222
45.1k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1223
45.1k
    if (pdev->CompatibilityLevel >= 1.3) {
1224
        /* Set the default rendering intent. */
1225
13.8k
        if (pdev->params.DefaultRenderingIntent != ri_Default) {
1226
0
            static const char *const ri_names[] = { psdf_ri_names };
1227
1228
0
            pprints1(s, "/%s ri\n",
1229
0
                     ri_names[(int)pdev->params.DefaultRenderingIntent]);
1230
0
        }
1231
13.8k
    }
1232
45.1k
    pdev->AR4_save_bug = false;
1233
45.1k
    return PDF_IN_STREAM;
1234
45.1k
}
1235
/* Enter text context from stream context. */
1236
static int
1237
stream_to_text(gx_device_pdf * pdev)
1238
144k
{
1239
144k
    int code;
1240
1241
    /* This is to handle clipped text rendering modes. If we have started
1242
     * a text clipping operation (signalled via a spec_op) then we do
1243
     * *NOT* want to save the viewer state as that will write a q/Q round the text
1244
     * which will discard the clip.
1245
     */
1246
144k
    if (!pdev->clipped_text_pending) {
1247
144k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1248
144k
        if (code < 0)
1249
0
            return 0;
1250
144k
    }
1251
    /*
1252
     * Bizarrely enough, Acrobat Reader cares how the final font size is
1253
     * obtained -- the CTM (cm), text matrix (Tm), and font size (Tf)
1254
     * are *not* all equivalent.  In particular, it seems to use the
1255
     * product of the text matrix and font size to decide how to
1256
     * anti-alias characters.  Therefore, we have to temporarily patch
1257
     * the CTM so that the scale factors are unity.  What a nuisance!
1258
     */
1259
144k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1260
144k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1261
144k
    pdev->procsets |= Text;
1262
144k
    code = pdf_from_stream_to_text(pdev);
1263
144k
    return (code < 0 ? code : PDF_IN_TEXT);
1264
144k
}
1265
/* Exit string context to text context. */
1266
static int
1267
string_to_text(gx_device_pdf * pdev)
1268
144k
{
1269
144k
    int code = pdf_from_string_to_text(pdev);
1270
1271
144k
    return (code < 0 ? code : PDF_IN_TEXT);
1272
144k
}
1273
/* Exit text context to stream context. */
1274
static int
1275
text_to_stream(gx_device_pdf * pdev)
1276
144k
{
1277
144k
    int code;
1278
1279
144k
    stream_puts(pdev->strm, "ET\n");
1280
    /* This is to handle clipped text rendering modes. If we are in
1281
     * a text clipping operation (signalledd via a spec_op) then we do
1282
     * *NOT* want to restore the viewer state as that will write a q/Q round the text
1283
     * which will discard the clip. However, we *do* have to undo the 'cm'
1284
     * operation which is done for Acrobat (see stream_to_text above).
1285
     */
1286
144k
    if (pdev->clipped_text_pending)
1287
54
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1288
54
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1289
144k
    else {
1290
144k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1291
144k
        if (code < 0)
1292
0
            return code;
1293
144k
        pdf_reset_text(pdev); /* because of Q */
1294
144k
    }
1295
144k
    return PDF_IN_STREAM;
1296
144k
}
1297
/* Exit stream context. */
1298
static int
1299
stream_to_none(gx_device_pdf * pdev)
1300
45.1k
{
1301
45.1k
    stream *s = pdev->strm;
1302
45.1k
    gs_offset_t length;
1303
45.1k
    int code;
1304
45.1k
    stream *target;
1305
45.1k
     char str[21];
1306
1307
45.1k
    if (pdev->ResourcesBeforeUsage) {
1308
31.3k
        int code = pdf_exit_substream(pdev);
1309
1310
31.3k
        if (code < 0)
1311
0
            return code;
1312
31.3k
    } else {
1313
13.8k
        if (pdev->vgstack_depth) {
1314
53
            code = pdf_restore_viewer_state(pdev, s);
1315
53
            if (code < 0)
1316
2
                return code;
1317
53
        }
1318
13.8k
        target = pdev->strm;
1319
1320
13.8k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1321
13.8k
            target = target->strm;
1322
13.8k
        if (!pdev->binary_ok)
1323
0
            target = target->strm;
1324
13.8k
        if (pdf_end_encrypt(pdev))
1325
0
            target = target->strm;
1326
13.8k
        s_close_filters(&pdev->strm, target);
1327
1328
13.8k
        s = pdev->strm;
1329
13.8k
        length = pdf_stell(pdev) - pdev->contents_pos;
1330
13.8k
        if (pdev->PDFA != 0)
1331
0
            stream_puts(s, "\n");
1332
13.8k
        stream_puts(s, "endstream\n");
1333
13.8k
        pdf_end_obj(pdev, resourceStream);
1334
1335
13.8k
        if (pdev->WriteObjStms) {
1336
13.8k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1337
13.8k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1338
13.8k
            stream_puts(pdev->strm, str);
1339
13.8k
            pdf_end_separate(pdev, resourceLength);
1340
13.8k
        } else {
1341
0
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1342
0
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1343
0
            stream_puts(s, str);
1344
0
            pdf_end_obj(pdev, resourceLength);
1345
0
        }
1346
13.8k
    }
1347
45.1k
    return PDF_IN_NONE;
1348
45.1k
}
1349
1350
/* Begin a page contents part. */
1351
int
1352
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1353
13.8M
{
1354
13.8M
    int (*proc) (gx_device_pdf *);
1355
1356
14.4M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1357
524k
        int code = (*proc) (pdev);
1358
1359
524k
        if (code < 0)
1360
15
            return code;
1361
524k
        pdev->context = (pdf_context_t) code;
1362
524k
    }
1363
13.8M
    pdev->context = context;
1364
13.8M
    return 0;
1365
13.8M
}
1366
1367
/* Close the current contents part if we are in one. */
1368
int
1369
pdf_close_contents(gx_device_pdf * pdev, bool last)
1370
45.1k
{
1371
45.1k
    if (pdev->context == PDF_IN_NONE)
1372
2
        return 0;
1373
45.1k
    if (last) {     /* Exit from the clipping path gsave. */
1374
45.1k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1375
1376
45.1k
        if (code < 0)
1377
0
            return code;
1378
45.1k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1379
45.1k
        pdf_close_text_contents(pdev);
1380
45.1k
    }
1381
45.1k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1382
45.1k
}
1383
1384
/* ------ Resources et al ------ */
1385
1386
/* Define the allocator descriptors for the resource types. */
1387
const char *const pdf_resource_type_names[] = {
1388
    PDF_RESOURCE_TYPE_NAMES
1389
};
1390
const gs_memory_struct_type_t *const pdf_resource_type_structs[] = {
1391
    PDF_RESOURCE_TYPE_STRUCTS
1392
};
1393
1394
/* Cancel a resource (do not write it into PDF). */
1395
int
1396
pdf_cancel_resource(gx_device_pdf * pdev, pdf_resource_t *pres, pdf_resource_type_t rtype)
1397
114k
{
1398
    /* fixme : Remove *pres from resource chain. */
1399
114k
    pres->where_used = 0;
1400
114k
    if (pres->object) {
1401
114k
        pres->object->written = true;
1402
114k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1403
72.2k
            || rtype >= NUM_RESOURCE_TYPES) {
1404
72.2k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1405
1406
72.2k
            if (code < 0)
1407
0
                return code;
1408
72.2k
        }
1409
114k
        cos_release(pres->object, "pdf_cancel_resource");
1410
114k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1411
114k
        pres->object = 0;
1412
114k
    }
1413
114k
    return 0;
1414
114k
}
1415
1416
/* Remove a resource. */
1417
void
1418
pdf_forget_resource(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1419
113k
{   /* fixme : optimize. */
1420
113k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1421
113k
    pdf_resource_t *pres;
1422
113k
    pdf_resource_t **pprev = &pdev->last_resource;
1423
113k
    int i;
1424
1425
    /* since we're about to free the resource, we can just set
1426
       any of these references to null
1427
    */
1428
1.35M
    for (i = 0; i < pdev->sbstack_size; i++) {
1429
1.24M
        if (pres1 == pdev->sbstack[i].font3) {
1430
0
            pdev->sbstack[i].font3 = NULL;
1431
0
        }
1432
1.24M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1433
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1434
0
        }
1435
1.24M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1436
1
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1437
1
        }
1438
1.24M
    }
1439
1440
115k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1441
115k
        if (pres == pres1) {
1442
113k
            *pprev = pres->prev;
1443
113k
            break;
1444
113k
        }
1445
1446
113k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1447
113k
        pprev = pchain + i;
1448
115k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1449
115k
            if (pres == pres1) {
1450
113k
                *pprev = pres->next;
1451
113k
                if (pres->object) {
1452
0
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1453
0
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1454
0
                    pres->object = 0;
1455
0
                }
1456
113k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1457
113k
                return;
1458
113k
            }
1459
113k
    }
1460
113k
}
1461
1462
static int
1463
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1464
31.0k
{
1465
31.0k
    return 1;
1466
31.0k
}
1467
1468
/* Substitute a resource with a same one. */
1469
/* NB we cannot substitute resources which have already had an
1470
   id assigned to them, because they already have an entry in the
1471
   xref table. If we want to substiute a resource then it should
1472
   have been allocated with an initial id of -1.
1473
   (see pdf_alloc_resource)
1474
*/
1475
int
1476
pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
1477
        pdf_resource_type_t rtype,
1478
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
1479
        bool write)
1480
74.3k
{
1481
74.3k
    pdf_resource_t *pres1 = *ppres;
1482
74.3k
    int code;
1483
1484
74.3k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1485
74.3k
    if (code < 0)
1486
0
        return code;
1487
74.3k
    if (code != 0) {
1488
47.1k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1489
47.1k
        if (code < 0)
1490
0
            return code;
1491
47.1k
        pdf_forget_resource(pdev, pres1, rtype);
1492
47.1k
        return 0;
1493
47.1k
    } else {
1494
27.2k
        if (pres1->object->id < 0)
1495
27.2k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1496
27.2k
        if (write) {
1497
13.8k
            code = cos_write_object(pres1->object, pdev, rtype);
1498
13.8k
            if (code < 0)
1499
0
                return code;
1500
13.8k
            pres1->object->written = 1;
1501
13.8k
        }
1502
27.2k
        return 1;
1503
27.2k
    }
1504
74.3k
}
1505
1506
/* Find a resource of a given type by gs_id. */
1507
pdf_resource_t *
1508
pdf_find_resource_by_gs_id(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1509
                           gs_id rid)
1510
1.07M
{
1511
1.07M
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1512
1.07M
    pdf_resource_t **pprev = pchain;
1513
1.07M
    pdf_resource_t *pres;
1514
1515
2.62M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1516
2.49M
        if (pres->rid == rid) {
1517
950k
            if (pprev != pchain) {
1518
338k
                *pprev = pres->next;
1519
338k
                pres->next = *pchain;
1520
338k
                *pchain = pres;
1521
338k
            }
1522
950k
            return pres;
1523
950k
        }
1524
123k
    return 0;
1525
1.07M
}
1526
1527
/* Find resource by resource id. */
1528
pdf_resource_t *
1529
pdf_find_resource_by_resource_id(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id id)
1530
11.4k
{
1531
11.4k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1532
11.4k
    pdf_resource_t *pres;
1533
11.4k
    int i;
1534
1535
191k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1536
1.90M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1537
1.72M
            if (pres->object && pres->object->id == id)
1538
270
                return pres;
1539
1.72M
        }
1540
180k
    }
1541
11.1k
    return 0;
1542
11.4k
}
1543
1544
/* Find same resource. */
1545
int
1546
pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
1547
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
1548
90.1k
{
1549
90.1k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1550
90.1k
    pdf_resource_t *pres;
1551
90.1k
    cos_object_t *pco0 = (*ppres)->object;
1552
90.1k
    int i;
1553
1554
764k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1555
2.39M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1556
1.71M
            if (*ppres != pres) {
1557
1.62M
                int code;
1558
1.62M
                cos_object_t *pco1 = pres->object;
1559
1560
1.62M
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1561
45.9k
                    continue;      /* don't compare different types */
1562
1.58M
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1563
1.58M
                if (code < 0)
1564
0
                    return code;
1565
1.58M
                if (code > 0) {
1566
48.2k
                    code = eq(pdev, *ppres, pres);
1567
48.2k
                    if (code < 0)
1568
0
                        return code;
1569
48.2k
                    if (code > 0) {
1570
48.2k
                        *ppres = pres;
1571
48.2k
                        return 1;
1572
48.2k
                    }
1573
48.2k
                }
1574
1.58M
            }
1575
1.71M
        }
1576
722k
    }
1577
41.9k
    return 0;
1578
90.1k
}
1579
1580
void
1581
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1582
2.27k
{
1583
2.27k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1584
2.27k
    pdf_resource_t *pres;
1585
2.27k
    pdf_resource_t **pprev = &pdev->last_resource;
1586
2.27k
    int i;
1587
1588
    /* since we're about to free the resource, we can just set
1589
       any of these references to null
1590
    */
1591
27.3k
    for (i = 0; i < pdev->sbstack_size; i++) {
1592
25.0k
        if (pres1 == pdev->sbstack[i].font3) {
1593
0
            pdev->sbstack[i].font3 = NULL;
1594
0
        }
1595
25.0k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1596
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1597
0
        }
1598
25.0k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1599
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1600
0
        }
1601
25.0k
    }
1602
1603
2.80k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1604
2.80k
        if (pres == pres1) {
1605
2.27k
            *pprev = pres->prev;
1606
2.27k
            break;
1607
2.27k
        }
1608
1609
2.27k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1610
2.27k
        pprev = pchain + i;
1611
2.38k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1612
2.38k
            if (pres == pres1) {
1613
2.27k
                *pprev = pres->next;
1614
#if 0
1615
                if (pres->object) {
1616
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1617
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1618
                    pres->object = 0;
1619
                }
1620
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1621
#endif
1622
2.27k
                return;
1623
2.27k
            }
1624
2.27k
    }
1625
2.27k
}
1626
1627
/* Drop resources by a condition. */
1628
void
1629
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1630
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1631
0
{
1632
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1633
0
    pdf_resource_t **pprev;
1634
0
    pdf_resource_t *pres;
1635
0
    int i;
1636
1637
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1638
0
        pprev = pchain + i;
1639
0
        for (; (pres = *pprev) != 0; ) {
1640
0
            if (cond(pdev, pres)) {
1641
0
                *pprev = pres->next;
1642
0
                pres->next = pres; /* A temporary mark - see below */
1643
0
            } else
1644
0
                pprev = &pres->next;
1645
0
        }
1646
0
    }
1647
0
    pprev = &pdev->last_resource;
1648
0
    for (; (pres = *pprev) != 0; )
1649
0
        if (pres->next == pres) {
1650
0
            *pprev = pres->prev;
1651
0
            if (pres->object) {
1652
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1653
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1654
0
                pres->object = 0;
1655
0
            }
1656
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1657
0
        } else
1658
0
            pprev = &pres->prev;
1659
0
}
1660
1661
/* Print resource statistics. */
1662
void
1663
pdf_print_resource_statistics(gx_device_pdf * pdev)
1664
0
{
1665
1666
0
    int rtype;
1667
1668
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1669
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1670
0
        pdf_resource_t *pres;
1671
0
        const char *name = pdf_resource_type_names[rtype];
1672
0
        int i, n = 0;
1673
1674
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1675
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1676
0
        }
1677
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1678
0
                (name ? name : ""), n);
1679
0
    }
1680
0
}
1681
1682
int FlushObjStm(gx_device_pdf *pdev)
1683
9.15k
{
1684
9.15k
    int code = 0, i, len = 0, id = 0, end;
1685
9.15k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1686
9.15k
    pdf_resource_t *pres;
1687
9.15k
    int options = DATA_STREAM_BINARY;
1688
1689
9.15k
    if (pdev->ObjStm_id == 0)
1690
0
        return 0;
1691
1692
9.15k
    pdev->WriteObjStms = false;
1693
1694
9.15k
    sflush(pdev->strm);
1695
9.15k
    sflush(pdev->ObjStm.strm);
1696
9.15k
    end = stell(pdev->ObjStm.strm);
1697
1698
9.15k
    if (pdev->CompressStreams)
1699
9.15k
        options |= DATA_STREAM_COMPRESS;
1700
1701
9.15k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1702
9.15k
    if (code < 0) {
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
9.15k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1707
1708
9.15k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1709
9.15k
    if (code < 0) {
1710
0
        pdf_close_aside(pdev);
1711
0
        pdev->WriteObjStms = true;
1712
0
        return code;
1713
0
    }
1714
9.15k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1715
9.15k
    if (code < 0) {
1716
0
        pdf_close_aside(pdev);
1717
0
        pdev->WriteObjStms = true;
1718
0
        return code;
1719
0
    }
1720
1721
9.15k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1722
87.2k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1723
78.1k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1724
78.1k
        id = pdev->ObjStmOffsets[(i * 2)];
1725
78.1k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1726
78.1k
        strcat(offsets, offset);
1727
78.1k
    }
1728
1729
9.15k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1730
9.15k
    if (code < 0) {
1731
0
        pdf_close_aside(pdev);
1732
0
        pdev->WriteObjStms = true;
1733
0
        return code;
1734
0
    }
1735
1736
9.15k
    stream_puts(pdev->strm, offsets);
1737
1738
9.15k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1739
9.15k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1740
9.15k
    if (code < 0) {
1741
0
        pdf_close_aside(pdev);
1742
0
        pdev->WriteObjStms = true;
1743
0
        return code;
1744
0
    }
1745
9.15k
    code = pdf_close_aside(pdev);
1746
9.15k
    if (code < 0)
1747
0
        return code;
1748
9.15k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1749
9.15k
    if (code < 0) {
1750
0
        pdev->WriteObjStms = true;
1751
0
        return code;
1752
0
    }
1753
9.15k
    pdev->WriteObjStms = true;
1754
9.15k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1755
9.15k
    if (pdev->ObjStmOffsets != NULL) {
1756
9.15k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1757
9.15k
        pdev->ObjStmOffsets = NULL;
1758
9.15k
    }
1759
9.15k
    pdev->NumObjStmObjects = 0;
1760
9.15k
    pdev->ObjStm_id = 0;
1761
1762
9.15k
    pdev->WriteObjStms = true;
1763
9.15k
    return code;
1764
9.15k
}
1765
1766
int NewObjStm(gx_device_pdf *pdev)
1767
9.15k
{
1768
9.15k
    int code;
1769
1770
9.15k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1771
1772
9.15k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1773
9.15k
    if (code < 0)
1774
0
        return code;
1775
1776
9.15k
    pdev->NumObjStmObjects = 0;
1777
9.15k
    if (pdev->ObjStmOffsets != NULL)
1778
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1779
1780
9.15k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1781
9.15k
    if (pdev->ObjStmOffsets == NULL) {
1782
0
        code = gs_note_error(gs_error_VMerror);
1783
0
    } else
1784
9.15k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1785
9.15k
    return code;
1786
9.15k
}
1787
1788
/* Begin an object logically separate from the contents. */
1789
int64_t
1790
pdf_open_separate_noObjStm(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1791
64.8k
{
1792
64.8k
    int code;
1793
1794
64.8k
    code = pdfwrite_pdf_open_document(pdev);
1795
64.8k
    if (code < 0)
1796
0
        return code;
1797
64.8k
    pdev->asides.save_strm = pdev->strm;
1798
64.8k
    pdev->strm = pdev->asides.strm;
1799
64.8k
    code = pdf_open_obj(pdev, id, type);
1800
64.8k
    return code;
1801
64.8k
}
1802
1803
static int is_stream_resource(pdf_resource_type_t type)
1804
165k
{
1805
165k
    if (type == resourceStream)
1806
0
        return true;
1807
165k
    if (type == resourceCharProc)
1808
48
        return true;
1809
165k
    if (type == resourcePattern)
1810
6.74k
        return true;
1811
158k
    if (type == resourceXObject)
1812
2.44k
        return true;
1813
156k
    return false;
1814
158k
}
1815
1816
int64_t
1817
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1818
264k
{
1819
264k
    int code;
1820
1821
264k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1822
186k
        code = pdfwrite_pdf_open_document(pdev);
1823
186k
        if (code < 0)
1824
0
            return code;
1825
186k
        pdev->asides.save_strm = pdev->strm;
1826
186k
        pdev->strm = pdev->asides.strm;
1827
186k
        code = pdf_open_obj(pdev, id, type);
1828
186k
    } else {
1829
78.1k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1830
50
            code = FlushObjStm(pdev);
1831
50
            if (code < 0)
1832
0
                return code;
1833
50
        }
1834
78.1k
        if (!pdev->ObjStm.strm) {
1835
9.15k
            code = NewObjStm(pdev);
1836
9.15k
            if (code < 0)
1837
0
                return code;
1838
9.15k
        }
1839
78.1k
        pdev->ObjStm.save_strm = pdev->strm;
1840
78.1k
        pdev->strm = pdev->ObjStm.strm;
1841
78.1k
        code = pdf_open_obj(pdev, id, type);
1842
78.1k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1843
78.1k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1844
78.1k
    }
1845
264k
    return code;
1846
264k
}
1847
int64_t
1848
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1849
127k
{
1850
127k
    return pdf_open_separate(pdev, 0L, type);
1851
127k
}
1852
1853
void
1854
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1855
259k
{
1856
259k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1857
259k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1858
259k
}
1859
1860
/* Begin an aside (resource, annotation, ...). */
1861
int
1862
pdf_alloc_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1863
                const gs_memory_struct_type_t * pst, pdf_resource_t **ppres,
1864
                int64_t id)
1865
382k
{
1866
382k
    pdf_resource_t *pres;
1867
382k
    cos_object_t *object;
1868
1869
382k
    if (pst == NULL)
1870
0
        pst = &st_pdf_resource;
1871
382k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1872
382k
                           "pdf_alloc_aside(resource)");
1873
382k
    if (pres == 0)
1874
0
        return_error(gs_error_VMerror);
1875
382k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1876
382k
    if (object == 0)
1877
0
        return_error(gs_error_VMerror);
1878
382k
    memset(pres, 0, pst->ssize);
1879
382k
    pres->object = object;
1880
382k
    if (id < 0) {
1881
220k
        object->id = -1L;
1882
220k
        pres->rname[0] = 0;
1883
220k
    } else
1884
161k
        pdf_reserve_object_id(pdev, pres, id);
1885
382k
    pres->next = *plist;
1886
382k
    pres->rid = 0;
1887
382k
    *plist = pres;
1888
382k
    pres->prev = pdev->last_resource;
1889
382k
    pdev->last_resource = pres;
1890
382k
    pres->named = false;
1891
382k
    pres->global = false;
1892
382k
    pres->where_used = pdev->used_mask;
1893
382k
    *ppres = pres;
1894
382k
    return 0;
1895
382k
}
1896
int64_t
1897
pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1898
                const gs_memory_struct_type_t * pst, pdf_resource_t ** ppres,
1899
                pdf_resource_type_t type)
1900
91.9k
{
1901
91.9k
    int64_t id = pdf_begin_separate(pdev, type);
1902
91.9k
    int code = 0;
1903
1904
91.9k
    if (id < 0)
1905
0
        return (int)id;
1906
91.9k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1907
91.9k
    if (code < 0)
1908
0
        (void)pdf_end_separate(pdev, type);
1909
1910
91.9k
    return code;
1911
91.9k
}
1912
1913
/* Begin a resource of a given type. */
1914
int
1915
pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1916
                        gs_id rid, pdf_resource_t ** ppres)
1917
91.9k
{
1918
91.9k
    int code;
1919
1920
91.9k
    if (rtype >= NUM_RESOURCE_TYPES)
1921
0
        rtype = resourceOther;
1922
1923
91.9k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1924
91.9k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1925
1926
91.9k
    if (code >= 0)
1927
91.9k
        (*ppres)->rid = rid;
1928
91.9k
    return code;
1929
91.9k
}
1930
int
1931
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1932
                   pdf_resource_t ** ppres)
1933
91.6k
{
1934
91.6k
    int code;
1935
1936
91.6k
    if (rtype >= NUM_RESOURCE_TYPES)
1937
0
        rtype = resourceOther;
1938
1939
91.6k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1940
1941
91.6k
    if (code >= 0 && pdf_resource_type_names[rtype] != 0) {
1942
0
        stream *s = pdev->strm;
1943
1944
0
        pprints1(s, "<</Type%s", pdf_resource_type_names[rtype]);
1945
0
        pprinti64d1(s, "/Name/R%"PRId64, (*ppres)->object->id);
1946
0
    }
1947
91.6k
    return code;
1948
91.6k
}
1949
1950
/* Allocate a resource, but don't open the stream. */
1951
/* If the passed in id 'id' is -1 then in pdf_alloc_aside
1952
   We *don't* reserve an object id (if its 0 or more we do).
1953
   This has important consequences; once an id is created we
1954
   can't 'cancel' it, it will always be written to the xref.
1955
   So if we want to not write duplicates we should create
1956
   the object with an 'id' of -1, and when we finish writing it
1957
   we should call 'pdf_substitute_resource'. If that finds a
1958
   duplicate then it will throw away the new one ands use the old.
1959
   If it doesn't find a duplicate then it will create an object
1960
   id for the new resource.
1961
*/
1962
int
1963
pdf_alloc_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1964
                   pdf_resource_t ** ppres, int64_t id)
1965
135k
{
1966
135k
    int code;
1967
1968
135k
    if (rtype >= NUM_RESOURCE_TYPES)
1969
0
        rtype = resourceOther;
1970
1971
135k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1972
135k
                               pdf_resource_type_structs[rtype], ppres, id);
1973
1974
135k
    if (code >= 0)
1975
135k
        (*ppres)->rid = rid;
1976
135k
    return code;
1977
135k
}
1978
1979
/* Get the object id of a resource. */
1980
int64_t
1981
pdf_resource_id(const pdf_resource_t *pres)
1982
1.70M
{
1983
1.70M
    return pres->object->id;
1984
1.70M
}
1985
1986
/* End an aside or other separate object. */
1987
int
1988
pdf_end_separate_noObjStm(gx_device_pdf * pdev, pdf_resource_type_t type)
1989
64.8k
{
1990
64.8k
    int code = pdf_end_obj(pdev, type);
1991
1992
64.8k
    pdev->strm = pdev->asides.save_strm;
1993
64.8k
    pdev->asides.save_strm = 0;
1994
64.8k
    return code;
1995
64.8k
}
1996
int
1997
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1998
264k
{
1999
264k
    int code = pdf_end_obj(pdev, type);
2000
2001
264k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
2002
186k
        pdev->strm = pdev->asides.save_strm;
2003
186k
        pdev->asides.save_strm = 0;
2004
186k
    } else {
2005
78.1k
        pdev->strm = pdev->ObjStm.save_strm;
2006
78.1k
        pdev->ObjStm.save_strm = 0;
2007
78.1k
        pdev->NumObjStmObjects++;
2008
78.1k
    }
2009
264k
    return code;
2010
264k
}
2011
int
2012
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
2013
343
{
2014
343
    return pdf_end_separate(pdev, type);
2015
343
}
2016
2017
/* End a resource. */
2018
int
2019
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2020
343
{
2021
343
    return pdf_end_aside(pdev, type);
2022
343
}
2023
2024
/*
2025
 * Write the Cos objects for resources local to a content stream.  Formerly,
2026
 * this procedure also freed such objects, but this doesn't work, because
2027
 * resources of one type might refer to resources of another type.
2028
 */
2029
int
2030
pdf_write_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2031
448k
{
2032
448k
    int j, code = 0;
2033
2034
7.62M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2035
7.17M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2036
2037
7.36M
        for (; pres != 0; pres = pres->next)
2038
189k
            if ((!pres->named || pdev->ForOPDFRead)
2039
189k
                && pres->object && !pres->object->written) {
2040
26.5k
                    code = cos_write_object(pres->object, pdev, rtype);
2041
26.5k
            }
2042
7.17M
    }
2043
448k
    return code;
2044
448k
}
2045
2046
/*
2047
 * Reverse resource chains.
2048
 * ps2write uses it with page resources.
2049
 * Assuming only the 0th chain contauns something.
2050
 */
2051
void
2052
pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2053
24.6k
{
2054
24.6k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2055
24.6k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2056
2057
24.6k
    if (pres == NULL)
2058
0
        return;
2059
24.6k
    pres1 = pres->next;
2060
31.3k
    for (;;) {
2061
31.3k
        if (pres1 == NULL)
2062
24.6k
            break;
2063
6.74k
        pres2 = pres1->next;
2064
6.74k
        pres1->next = pres;
2065
6.74k
        pres = pres1;
2066
6.74k
        pres1 = pres2;
2067
6.74k
    }
2068
24.6k
    pres0->next = NULL;
2069
24.6k
    pdev->resources[rtype].chains[0] = pres;
2070
24.6k
}
2071
2072
/*
2073
 * Free unnamed Cos objects for resources local to a content stream,
2074
 * since they can't be used again.
2075
 */
2076
int
2077
pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2078
235k
{
2079
235k
    int j;
2080
2081
4.01M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2082
3.77M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2083
3.77M
        pdf_resource_t *pres;
2084
2085
3.86M
        while ((pres = *prev) != 0) {
2086
86.3k
            if (pres->named) { /* named, don't free */
2087
6
                prev = &pres->next;
2088
86.3k
            } else {
2089
86.3k
                if (pres->object) {
2090
86.3k
                    cos_free(pres->object, "pdf_free_resource_objects");
2091
86.3k
                    pres->object = 0;
2092
86.3k
                }
2093
86.3k
                *prev = pres->next;
2094
86.3k
            }
2095
86.3k
        }
2096
3.77M
    }
2097
235k
    return 0;
2098
235k
}
2099
2100
/*
2101
 * Store the resource sets for a content stream (page or XObject).
2102
 * Sets page->{procsets, resource_ids[]}.
2103
 */
2104
int
2105
pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage)
2106
45.1k
{
2107
45.1k
    int i;
2108
2109
    /* Write any resource dictionaries. */
2110
2111
406k
    for (i = 0; i <= resourceFont; ++i) {
2112
361k
        stream *s = 0;
2113
361k
        int j;
2114
2115
361k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2116
45.1k
            continue;
2117
316k
        page->resource_ids[i] = 0;
2118
5.37M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2119
5.05M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2120
2121
5.21M
            for (; pres != 0; pres = pres->next) {
2122
155k
                if (pres->where_used & pdev->used_mask) {
2123
83.2k
                    int64_t id = pdf_resource_id(pres);
2124
2125
83.2k
                    if (id == -1L)
2126
7.02k
                        continue;
2127
76.2k
                    if (s == 0) {
2128
24.8k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2129
24.8k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2130
24.8k
                        s = pdev->strm;
2131
24.8k
                        stream_puts(s, "<<");
2132
24.8k
                    }
2133
76.2k
                    pprints1(s, "/%s\n", pres->rname);
2134
76.2k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2135
76.2k
                    pdf_record_usage(pdev, id, pdev->next_page);
2136
76.2k
                    if (clear_usage)
2137
76.2k
                        pres->where_used -= pdev->used_mask;
2138
76.2k
                }
2139
155k
            }
2140
5.05M
        }
2141
316k
        if (s) {
2142
24.8k
            stream_puts(s, ">>\n");
2143
24.8k
            pdf_end_separate(pdev, i);
2144
24.8k
        }
2145
        /* If an object isn't used, we still need to emit it :-( This is because
2146
         * we reserved an object number for it, and the xref will have an entry
2147
         * for it. If we don't actually emit it then the xref will be invalid.
2148
         * An alternative would be to modify the xref to mark the object as unused.
2149
         */
2150
316k
        if (i != resourceFont && i != resourceProperties)
2151
225k
            pdf_write_resource_objects(pdev, i);
2152
316k
    }
2153
45.1k
    page->procsets = pdev->procsets;
2154
45.1k
    return 0;
2155
45.1k
}
2156
2157
/* Copy data from a temporary file to a stream. */
2158
int
2159
pdf_copy_data(stream *s, gp_file *file, gs_offset_t count, stream_arcfour_state *ss)
2160
210k
{
2161
210k
    gs_offset_t r, left = count;
2162
210k
    byte buf[sbuf_size];
2163
2164
2.54M
    while (left > 0) {
2165
2.33M
        uint copy = min(left, sbuf_size);
2166
2167
2.33M
        r = gp_fread(buf, 1, copy, file);
2168
2.33M
        if (r < 1) {
2169
0
            return gs_note_error(gs_error_ioerror);
2170
0
        }
2171
2.33M
        if (ss)
2172
0
            s_arcfour_process_buffer(ss, buf, copy);
2173
2.33M
        stream_write(s, buf, copy);
2174
2.33M
        left -= copy;
2175
2.33M
    }
2176
210k
    return 0;
2177
210k
}
2178
2179
/* Copy data from a temporary file to a stream,
2180
   which may be targetted to the same file. */
2181
int
2182
pdf_copy_data_safe(stream *s, gp_file *file, gs_offset_t position, int64_t count)
2183
154k
{
2184
154k
    int64_t r, left = count;
2185
2186
1.17M
    while (left > 0) {
2187
1.01M
        byte buf[sbuf_size];
2188
1.01M
        int64_t copy = min(left, (int64_t)sbuf_size);
2189
1.01M
        int64_t end_pos = gp_ftell(file);
2190
2191
1.01M
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2192
0
            return_error(gs_error_ioerror);
2193
0
        }
2194
1.01M
        r = gp_fread(buf, 1, copy, file);
2195
1.01M
        if (r < 1) {
2196
0
            return_error(gs_error_ioerror);
2197
0
        }
2198
1.01M
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2199
0
            return_error(gs_error_ioerror);
2200
0
        }
2201
1.01M
        stream_write(s, buf, copy);
2202
1.01M
        sflush(s);
2203
1.01M
        left -= copy;
2204
1.01M
    }
2205
154k
    return 0;
2206
154k
}
2207
2208
/* ------ Pages ------ */
2209
2210
/* Get or assign the ID for a page. */
2211
/* Returns 0 if the page number is out of range. */
2212
int64_t
2213
pdf_page_id(gx_device_pdf * pdev, int page_num)
2214
149k
{
2215
149k
    cos_dict_t *Page;
2216
2217
149k
    if (page_num < 1 || pdev->pages == NULL)
2218
0
        return 0;
2219
149k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2220
71
        uint new_num_pages;
2221
71
        pdf_page_t *new_pages;
2222
2223
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2224
71
        if (page_num > (1LU << 31) - 11)
2225
0
            page_num = (1LU << 31) - 11;
2226
71
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2227
2228
71
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2229
71
                             "pdf_page_id(resize pages)");
2230
2231
71
        if (new_pages == 0)
2232
0
            return 0;
2233
71
        memset(&new_pages[pdev->num_pages], 0,
2234
71
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2235
71
        pdev->pages = new_pages;
2236
71
        pdev->num_pages = new_num_pages;
2237
71
    }
2238
149k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2239
45.1k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2240
45.1k
        if (Page == NULL) {
2241
0
            return 0;
2242
0
        }
2243
45.1k
        Page->id = pdf_obj_forward_ref(pdev);
2244
45.1k
    }
2245
149k
    return Page->id;
2246
149k
}
2247
2248
/* Get the page structure for the current page. */
2249
pdf_page_t *
2250
pdf_current_page(gx_device_pdf *pdev)
2251
5.04M
{
2252
5.04M
    return &pdev->pages[pdev->next_page];
2253
5.04M
}
2254
2255
/* Get the dictionary object for the current page. */
2256
cos_dict_t *
2257
pdf_current_page_dict(gx_device_pdf *pdev)
2258
6.23k
{
2259
6.23k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2260
0
        return 0;
2261
6.23k
    return pdev->pages[pdev->next_page].Page;
2262
6.23k
}
2263
2264
/* Write saved page- or document-level information. */
2265
int
2266
pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr)
2267
0
{
2268
0
    if (pstr->data != 0) {
2269
0
        stream_write(pdev->strm, pstr->data, pstr->size);
2270
0
        gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
2271
0
                       "pdf_write_saved_string");
2272
0
        pstr->data = 0;
2273
0
    }
2274
0
    return 0;
2275
0
}
2276
2277
/* Open a page for writing. */
2278
int
2279
pdf_open_page(gx_device_pdf * pdev, pdf_context_t context)
2280
13.5M
{
2281
13.5M
    if (!is_in_page(pdev)) {
2282
44.7k
        int code;
2283
2284
44.7k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2285
0
            return_error(gs_error_VMerror);
2286
44.7k
        code = pdfwrite_pdf_open_document(pdev);
2287
44.7k
        if (code < 0)
2288
0
            return code;
2289
44.7k
    }
2290
    /* Note that context may be PDF_IN_NONE here. */
2291
13.5M
    return pdf_open_contents(pdev, context);
2292
13.5M
}
2293
2294
/*  Go to the unclipped stream context. */
2295
int
2296
pdf_unclip(gx_device_pdf * pdev)
2297
96.4k
{
2298
96.4k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2299
    /* When ResourcesBeforeUsage != 0, one sbstack element
2300
       appears from the page contents stream. */
2301
2302
96.4k
    if (pdev->sbstack_depth <= bottom) {
2303
90.7k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2304
2305
90.7k
        if (code < 0)
2306
0
            return code;
2307
90.7k
    }
2308
96.4k
    if (pdev->context > PDF_IN_STREAM) {
2309
1
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2310
2311
1
        if (code < 0)
2312
0
            return code;
2313
1
    }
2314
96.4k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2315
64.3k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2316
2317
64.3k
        if (code < 0)
2318
0
            return code;
2319
64.3k
        code = pdf_remember_clip_path(pdev, NULL);
2320
64.3k
        if (code < 0)
2321
0
            return code;
2322
64.3k
        pdev->clip_path_id = pdev->no_clip_path_id;
2323
64.3k
    }
2324
96.4k
    return 0;
2325
96.4k
}
2326
2327
/* ------ Miscellaneous output ------ */
2328
2329
/* Generate the default Producer string. */
2330
/* This calculation is also performed for Ghostscript generally
2331
 * The code is in ghostpdl/base/gsmisc.c printf_program_ident().
2332
 * Should we change this calculation both sets of code need to be updated.
2333
 */
2334
void
2335
pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER])
2336
33.7k
{
2337
33.7k
    int major = (int)(gs_revision / 1000);
2338
33.7k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2339
33.7k
    int patch = gs_revision % 10;
2340
2341
33.7k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2342
33.7k
}
2343
2344
/* Write matrix values. */
2345
void
2346
pdf_put_matrix(gx_device_pdf * pdev, const char *before,
2347
               const gs_matrix * pmat, const char *after)
2348
114k
{
2349
114k
    stream *s = pdev->strm;
2350
2351
114k
    if (before)
2352
111k
        stream_puts(s, before);
2353
114k
    pprintg6(s, "%g %g %g %g %g %g ",
2354
114k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2355
114k
    if (after)
2356
114k
        stream_puts(s, after);
2357
114k
}
2358
2359
/*
2360
 * Write a name, with escapes for unusual characters.  Since we only support
2361
 * PDF 1.2 and above, we can use an escape sequence for anything except a
2362
 * null <00>, and the machinery for selecting the put_name_chars procedure
2363
 * depending on CompatibilityLevel is no longer needed.
2364
 */
2365
static int
2366
pdf_put_name_chars_1_2(stream *s, const byte *nstr, uint size)
2367
2.73M
{
2368
2.73M
    uint i;
2369
2370
13.6M
    for (i = 0; i < size; ++i) {
2371
10.9M
        uint c = nstr[i];
2372
10.9M
        char hex[4];
2373
2374
10.9M
        switch (c) {
2375
10.9M
            default:
2376
10.9M
                if (c >= 0x21 && c <= 0x7e) {
2377
10.9M
                    stream_putc(s, (byte)c);
2378
10.9M
                    break;
2379
10.9M
                }
2380
                /* falls through */
2381
2.45k
            case '#':
2382
2.46k
            case '%':
2383
2.47k
            case '(': case ')':
2384
2.47k
            case '<': case '>':
2385
2.49k
            case '[': case ']':
2386
2.51k
            case '{': case '}':
2387
2.52k
            case '/':
2388
2.52k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2389
2.52k
                stream_puts(s, hex);
2390
2.52k
                break;
2391
0
            case 0:
2392
0
                stream_puts(s, "BnZr"); /* arbitrary */
2393
10.9M
        }
2394
10.9M
    }
2395
2.73M
    return 0;
2396
2.73M
}
2397
pdf_put_name_chars_proc_t
2398
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2399
2.73M
{
2400
2.73M
    return &pdf_put_name_chars_1_2;
2401
2.73M
}
2402
int
2403
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2404
2.72M
{
2405
2.72M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2406
2.72M
}
2407
int
2408
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2409
2.72M
{
2410
2.72M
    stream_putc(pdev->strm, '/');
2411
2.72M
    return pdf_put_name_chars(pdev, nstr, size);
2412
2.72M
}
2413
2414
/* Write an encoded string with encryption. */
2415
static int
2416
pdf_encrypt_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2417
0
{
2418
0
    stream sinp, sstr, sout;
2419
0
    stream_PSSD_state st;
2420
0
    stream_state so;
2421
0
    byte buf[100], bufo[100];
2422
0
    stream_arcfour_state sarc4;
2423
2424
0
    if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
2425
        /* The interface can't pass an error. */
2426
0
        stream_write(pdev->strm, str, size);
2427
0
        return size;
2428
0
    }
2429
0
    s_init(&sinp, NULL);
2430
0
    sread_string(&sinp, str + 1, size);
2431
0
    s_init(&sstr, NULL);
2432
0
    sstr.close_at_eod = false;
2433
0
    s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
2434
0
    s_init_filter(&sstr, (stream_state *)&st, buf, sizeof(buf), &sinp);
2435
0
    s_init(&sout, NULL);
2436
0
    s_init_state(&so, &s_PSSE_template, NULL);
2437
0
    s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
2438
0
    stream_putc(pdev->strm, '(');
2439
0
    for (;;) {
2440
0
        uint n;
2441
0
        int code = sgets(&sstr, buf, sizeof(buf), &n);
2442
2443
0
        if (n > 0) {
2444
0
            s_arcfour_process_buffer(&sarc4, buf, n);
2445
0
            stream_write(&sout, buf, n);
2446
0
        }
2447
0
        if (code == EOFC)
2448
0
            break;
2449
0
        if (code < 0 || n < sizeof(buf)) {
2450
            /* The interface can't pass an error. */
2451
0
            break;
2452
0
        }
2453
0
    }
2454
    /* Another case where we use sclose() and not sclose_filters(), because the
2455
     * buffer we supplied to s_init_filter is a heap based C object, so we
2456
     * must not free it.
2457
     */
2458
0
    sclose(&sout); /* Writes ')'. */
2459
0
    return (int)stell(&sinp) + 1;
2460
0
}
2461
2462
/* Write an encoded string with possible encryption. */
2463
static int
2464
pdf_put_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2465
68.8k
{
2466
68.8k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2467
68.8k
        stream_write(pdev->strm, str, size);
2468
68.8k
        return 0;
2469
68.8k
    } else
2470
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2471
68.8k
}
2472
/* Write an encoded string with possible encryption. */
2473
static int
2474
pdf_put_encoded_string_as_hex(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2475
74.2k
{
2476
74.2k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2477
74.2k
        int i, oct, width = 0;
2478
74.2k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2479
2480
74.2k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2481
74.2k
            stream_write(pdev->strm, "\n", 1);
2482
74.2k
        stream_write(pdev->strm, "<", 1);
2483
74.2k
        width++;
2484
2.31M
        for (i = 1; i < size - 1; i++) {
2485
2.24M
            if (str[i] == '\\') {
2486
148k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2487
148k
                    oct = (str[i+1] - 0x30) * 64;
2488
148k
                    oct += (str[i+2] - 0x30) *8;
2489
148k
                    oct += str[i+3] - 0x30;
2490
148k
                    i+=3;
2491
148k
                } else {
2492
834
                    switch (str[++i]) {
2493
8
                        case 'b' :
2494
8
                            oct = 8;
2495
8
                            break;
2496
684
                        case 't' :
2497
684
                            oct = 9;
2498
684
                            break;
2499
45
                        case 'n' :
2500
45
                            oct = 10;
2501
45
                            break;
2502
35
                        case 'f' :
2503
35
                            oct = 12;
2504
35
                            break;
2505
8
                        case 'r' :
2506
8
                            oct = 13;
2507
8
                            break;
2508
54
                        default:
2509
54
                            oct = str[i];
2510
54
                            break;
2511
834
                    }
2512
834
                }
2513
148k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2514
1.12k
                    stream_write(pdev->strm, "\n", 1);
2515
1.12k
                    width = 0;
2516
1.12k
                }
2517
148k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2518
148k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2519
148k
                width += 2;
2520
2.09M
            } else {
2521
2.09M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2522
244
                    stream_write(pdev->strm, "\n", 1);
2523
244
                    width = 0;
2524
244
                }
2525
2.09M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2526
2.09M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2527
2.09M
                width += 2;
2528
2.09M
            }
2529
2.24M
        }
2530
74.2k
        stream_write(pdev->strm, ">", 1);
2531
74.2k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2532
74.2k
            stream_write(pdev->strm, "\n", 1);
2533
74.2k
        return 0;
2534
74.2k
    } else
2535
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2536
74.2k
}
2537
2538
/* Write an encoded hexadecimal string with possible encryption. */
2539
static int
2540
pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2541
0
{
2542
0
    emprintf(pdev->memory,
2543
0
             "Unimplemented function : pdf_put_encoded_hex_string\n");
2544
0
    stream_write(pdev->strm, str, size);
2545
0
    return_error(gs_error_unregistered);
2546
0
}
2547
/*  Scan an item in a serialized array or dictionary.
2548
    This is a very simplified Postscript lexical scanner.
2549
    It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
2550
    We only need to select strings and encrypt them.
2551
    Other items are passed identically.
2552
    Note we don't reconstruct the nesting of arrays|dictionaries.
2553
*/
2554
static int
2555
pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
2556
0
{
2557
0
    const byte *q = p;
2558
0
    int n = l;
2559
2560
0
    if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2561
0
        return (l > 0 ? 1 : 0);
2562
0
    for (q++, n--; n; q++, n--) {
2563
0
        if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2564
0
            return q - p;
2565
0
        if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
2566
0
            return q - p;
2567
        /* Note : immediate names are not allowed in PDF. */
2568
0
    }
2569
0
    return l;
2570
0
}
2571
2572
/* Write a serialized array or dictionary with possible encryption. */
2573
static int
2574
pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2575
27.2k
{
2576
27.2k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2577
27.2k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2578
726
            stream_putc(pdev->strm, (byte)'\n');
2579
726
            if (size > 255) {
2580
0
                const byte *start, *p, *end, *save;
2581
0
                int width = 0;
2582
2583
0
                end = vstr + size;
2584
0
                start = p = vstr;
2585
0
                while (p < end) {
2586
0
                    if(*p == '\r' || *p == '\n') {
2587
0
                        width = 0;
2588
0
                        p++;
2589
0
                        continue;
2590
0
                    }
2591
0
                    if (width > 254) {
2592
0
                        save = p;
2593
                        /* search backwards for a point to split */
2594
0
                        while (p > start) {
2595
0
                            if (*p == '/' || *p == '[' || *p == '{' || *p == '(' || *p == ' ') {
2596
0
                                stream_write(pdev->strm, start, p - start);
2597
0
                                stream_putc(pdev->strm, (byte)'\n');
2598
0
                                width = 0;
2599
0
                                start = p;
2600
0
                                break;
2601
0
                            }
2602
0
                            else p--;
2603
0
                        }
2604
0
                        if (p == start && width != 0) {
2605
0
                            stream_write(pdev->strm, start, save - start);
2606
0
                            stream_putc(pdev->strm, (byte)'\n');
2607
0
                            p = start = save;
2608
0
                            width = 0;
2609
0
                        }
2610
0
                    } else {
2611
0
                        width++;
2612
0
                        p++;
2613
0
                    }
2614
0
                }
2615
0
                if (width) {
2616
0
                    stream_write(pdev->strm, start, p - start);
2617
0
                    stream_putc(pdev->strm, (byte)'\n');
2618
0
                }
2619
726
            } else {
2620
726
                stream_write(pdev->strm, vstr, size);
2621
726
            }
2622
26.5k
        } else {
2623
26.5k
            stream_write(pdev->strm, vstr, size);
2624
26.5k
        }
2625
27.2k
    } else {
2626
0
        const byte *p = vstr;
2627
0
        int l = size, n;
2628
2629
0
        for (;l > 0 ;) {
2630
0
            if (*p == '(')
2631
0
                n = pdf_encrypt_encoded_string(pdev, p, l, object_id);
2632
0
            else {
2633
0
                n = pdf_scan_item(pdev, p, l, object_id);
2634
0
                stream_write(pdev->strm, p, n);
2635
0
            }
2636
0
            l -= n;
2637
0
            p += n;
2638
0
        }
2639
0
    }
2640
27.2k
    return 0;
2641
27.2k
}
2642
2643
/*
2644
 * Write a string in its shortest form ( () or <> ).  Note that
2645
 * this form is different depending on whether binary data are allowed.
2646
 * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
2647
 */
2648
int
2649
pdf_put_string(const gx_device_pdf * pdev, const byte * str, uint size)
2650
8.06M
{
2651
8.06M
    psdf_write_string(pdev->strm, str, size,
2652
8.06M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2653
8.06M
    return 0;
2654
8.06M
}
2655
2656
/* Write a value, treating names specially. */
2657
int
2658
pdf_write_value(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2659
4.08M
{
2660
4.08M
    if (size > 0 && vstr[0] == '/')
2661
2.31M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2662
1.76M
    else if (size > 5 && vstr[0] == 0 && vstr[1] == 0 && vstr[2] == 0 && vstr[size - 1] == 0 && vstr[size - 2] == 0)
2663
0
        return pdf_put_name(pdev, vstr + 4, size - 5);
2664
1.76M
    else if (size > 3 && vstr[0] == 0 && vstr[1] == 0 && vstr[size - 1] == 0)
2665
0
        return pdf_put_name(pdev, vstr + 3, size - 4);
2666
1.76M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2667
23.3k
        return pdf_put_composite(pdev, vstr, size, object_id);
2668
1.74M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2669
3.86k
        return pdf_put_composite(pdev, vstr, size, object_id);
2670
1.73M
    else if (size > 1 && vstr[0] == '(') {
2671
143k
        if (pdev->ForOPDFRead)
2672
74.2k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2673
68.8k
        else
2674
68.8k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2675
143k
    }
2676
1.59M
    else if (size > 1 && vstr[0] == '<')
2677
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2678
1.59M
    stream_write(pdev->strm, vstr, size);
2679
1.59M
    return 0;
2680
4.08M
}
2681
2682
/* Store filters for a stream. */
2683
/* Currently this only saves parameters for CCITTFaxDecode. */
2684
int
2685
pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
2686
                const pdf_filter_names_t *pfn)
2687
345k
{
2688
345k
    const char *filter_name = 0;
2689
345k
    bool binary_ok = true;
2690
345k
    stream *fs = s;
2691
345k
    cos_dict_t *decode_parms = 0;
2692
345k
    int code;
2693
2694
1.32M
    for (; fs != 0; fs = fs->strm) {
2695
981k
        const stream_state *st = fs->state;
2696
981k
        const stream_template *templat = st->templat;
2697
2698
981k
#define TEMPLATE_IS(atemp)\
2699
5.33M
  (templat->process == (atemp).process)
2700
981k
        if (TEMPLATE_IS(s_A85E_template))
2701
200k
            binary_ok = false;
2702
780k
        else if (TEMPLATE_IS(s_CFE_template)) {
2703
103k
            cos_param_list_writer_t writer;
2704
103k
            stream_CF_state cfs;
2705
2706
103k
            decode_parms =
2707
103k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2708
103k
            if (decode_parms == 0)
2709
0
                return_error(gs_error_VMerror);
2710
103k
            CHECK(cos_param_list_writer_init(pdev, &writer, decode_parms, 0));
2711
            /*
2712
             * If EndOfBlock is true, we mustn't write a Rows value.
2713
             * This is a hack....
2714
             */
2715
103k
            cfs = *(const stream_CF_state *)st;
2716
103k
            if (cfs.EndOfBlock)
2717
11.9k
                cfs.Rows = 0;
2718
103k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2719
103k
            filter_name = pfn->CCITTFaxDecode;
2720
677k
        } else if (TEMPLATE_IS(s_DCTE_template))
2721
8.62k
            filter_name = pfn->DCTDecode;
2722
668k
        else if (TEMPLATE_IS(s_zlibE_template))
2723
72.1k
            filter_name = pfn->FlateDecode;
2724
596k
        else if (TEMPLATE_IS(s_brotliE_template))
2725
0
            filter_name = pfn->BrotliDecode;
2726
596k
        else if (TEMPLATE_IS(s_LZWE_template))
2727
73.2k
            filter_name = pfn->LZWDecode;
2728
522k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2729
            /* This is a predictor for FlateDecode or LZWEncode. */
2730
10.2k
            const stream_PNGP_state *const ss =
2731
10.2k
                (const stream_PNGP_state *)st;
2732
2733
10.2k
            decode_parms =
2734
10.2k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2735
10.2k
            if (decode_parms == 0)
2736
0
                return_error(gs_error_VMerror);
2737
10.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2738
10.2k
                                         ss->Predictor));
2739
10.2k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2740
10.2k
                                         ss->Columns));
2741
10.2k
            if (ss->Colors != 1)
2742
10.2k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2743
10.2k
                                             ss->Colors));
2744
10.2k
            if (ss->BitsPerComponent != 8)
2745
10.2k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2746
10.2k
                                             "/BitsPerComponent",
2747
10.2k
                                             ss->BitsPerComponent));
2748
512k
        } else if (TEMPLATE_IS(s_RLE_template))
2749
0
            filter_name = pfn->RunLengthDecode;
2750
981k
#undef TEMPLATE_IS
2751
981k
    }
2752
345k
    if (filter_name) {
2753
257k
        if (binary_ok) {
2754
100k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2755
100k
            if (decode_parms)
2756
100k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2757
100k
                                                COS_OBJECT(decode_parms)));
2758
156k
        } else {
2759
156k
            cos_array_t *pca =
2760
156k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2761
2762
156k
            if (pca == 0)
2763
0
                return_error(gs_error_VMerror);
2764
156k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2765
156k
            CHECK(cos_array_add_c_string(pca, filter_name));
2766
156k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2767
156k
                                            COS_OBJECT(pca)));
2768
156k
            if (decode_parms) {
2769
99.5k
                pca = cos_array_alloc(pdev,
2770
99.5k
                                      "pdf_put_image_filters(DecodeParms)");
2771
99.5k
                if (pca == 0)
2772
0
                    return_error(gs_error_VMerror);
2773
99.5k
                CHECK(cos_array_add_c_string(pca, "null"));
2774
99.5k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2775
99.5k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2776
99.5k
                                                COS_OBJECT(pca)));
2777
99.5k
            }
2778
156k
        }
2779
257k
    } else if (!binary_ok)
2780
88.3k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2781
345k
    return 0;
2782
345k
}
2783
2784
/* Add a Flate compression filter to a binary writer. */
2785
static int
2786
pdf_flate_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
2787
125k
{
2788
125k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2789
64.0k
                    &s_LZWE_template : &s_zlibE_template);
2790
125k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2791
125k
                                     "pdf_write_function");
2792
2793
125k
    if (st == 0)
2794
0
        return_error(gs_error_VMerror);
2795
125k
    if (templat->set_defaults)
2796
125k
        templat->set_defaults(st);
2797
125k
    return psdf_encode_binary(pbw, templat, st);
2798
125k
}
2799
2800
/* Add a Brotli compression filter to a binary writer. */
2801
static int
2802
pdf_brotli_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
2803
0
{
2804
0
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2805
0
                    &s_LZWE_template : &s_brotliE_template);
2806
0
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2807
0
                                     "pdf_write_function");
2808
2809
0
    if (st == 0)
2810
0
        return_error(gs_error_VMerror);
2811
0
    if (templat->set_defaults)
2812
0
        templat->set_defaults(st);
2813
0
    return psdf_encode_binary(pbw, templat, st);
2814
0
}
2815
2816
/*
2817
 * Begin a data stream.  The client has opened the object and written
2818
 * the << and any desired dictionary keys.
2819
 */
2820
int
2821
pdf_begin_data(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
2822
0
{
2823
0
    return pdf_begin_data_stream(pdev, pdw,
2824
0
                                 DATA_STREAM_BINARY | DATA_STREAM_COMPRESS, 0);
2825
0
}
2826
2827
int
2828
pdf_append_data_stream_filters(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2829
                      int orig_options, gs_id object_id)
2830
154k
{
2831
154k
    stream *s = pdev->strm;
2832
154k
    int options = orig_options;
2833
154k
#define USE_ASCII85 1
2834
267k
#define USE_FLATE 2
2835
154k
#define USE_BROTLI 4
2836
154k
    static const char *const fnames[6] = {
2837
154k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2838
154k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2839
154k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2840
154k
    };
2841
154k
    static const char *const fnames1_2[6] = {
2842
154k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2843
154k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2844
154k
        "/Filter[/ASCII85Decode/LZWDecode]"
2845
154k
    };
2846
154k
    int filters = 0;
2847
154k
    int code;
2848
2849
154k
    if (options & DATA_STREAM_COMPRESS) {
2850
113k
        if (pdev->UseBrotli) {
2851
0
            filters |= USE_BROTLI;
2852
0
            options |= DATA_STREAM_BINARY;
2853
113k
        } else {
2854
113k
            filters |= USE_FLATE;
2855
113k
            options |= DATA_STREAM_BINARY;
2856
113k
        }
2857
113k
    }
2858
154k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2859
41.3k
        filters |= USE_ASCII85;
2860
154k
    if (!(options & DATA_STREAM_NOLENGTH)) {
2861
0
        stream_puts(s, (pdev->CompatibilityLevel < 1.3 ?
2862
0
            fnames1_2[filters] : fnames[filters]));
2863
0
        if (pdev->ResourcesBeforeUsage) {
2864
0
            pdw->length_pos = stell(s) + 8;
2865
0
            stream_puts(s, "/Length             >>stream\n");
2866
0
            pdw->length_id = -1;
2867
0
        } else {
2868
0
            pdw->length_pos = -1;
2869
0
            pdw->length_id = pdf_obj_ref(pdev);
2870
0
            pprinti64d1(s, "/Length %"PRId64" 0 R>>stream\n", pdw->length_id);
2871
0
        }
2872
0
    }
2873
154k
    if (options & DATA_STREAM_ENCRYPT) {
2874
9.10k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2875
9.10k
        if (code < 0)
2876
0
            return code;
2877
9.10k
        pdev->strm = s;
2878
9.10k
        pdw->encrypted = true;
2879
9.10k
    } else
2880
145k
        pdw->encrypted = false;
2881
154k
    if (options & DATA_STREAM_BINARY) {
2882
113k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2883
113k
        if (code < 0)
2884
0
            return code;
2885
113k
    } else {
2886
40.4k
        code = 0;
2887
40.4k
        pdw->binary.target = pdev->strm;
2888
40.4k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2889
40.4k
        pdw->binary.strm = pdev->strm;
2890
40.4k
    }
2891
154k
    pdw->start = stell(s);
2892
154k
    if (filters & USE_BROTLI)
2893
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2894
154k
    else
2895
154k
        if (filters & USE_FLATE)
2896
113k
            code = pdf_flate_binary(pdev, &pdw->binary);
2897
154k
    return code;
2898
154k
#undef USE_ASCII85
2899
154k
#undef USE_FLATE
2900
154k
#undef USE_BROTLI
2901
154k
}
2902
2903
int
2904
pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2905
                      int options, gs_id object_id)
2906
21.5k
{   int code;
2907
    /* object_id is an unused rudiment from the old code,
2908
       when the encription was applied when creating the stream.
2909
       The new code encrypts than copying stream from the temporary file. */
2910
21.5k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2911
21.5k
    pdw->binary.target = pdev->strm;
2912
21.5k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2913
21.5k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2914
21.5k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2915
21.5k
                options);
2916
21.5k
    if (object_id != 0)
2917
48
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2918
21.5k
    pdw->binary.strm = pdev->strm;
2919
21.5k
    return code;
2920
21.5k
}
2921
2922
/* End a data stream. */
2923
int
2924
pdf_end_data(pdf_data_writer_t *pdw)
2925
2.96k
{   int code;
2926
2927
2.96k
    code = pdf_close_aside(pdw->pdev);
2928
2.96k
    if (code < 0)
2929
0
        return code;
2930
2.96k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2931
2.96k
    if (code < 0)
2932
0
        return code;
2933
2.96k
    return 0;
2934
2.96k
}
2935
2936
/* Create a Function object. */
2937
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
2938
                               const gs_function_info_t *pinfo);
2939
int
2940
pdf_function_scaled(gx_device_pdf *pdev, const gs_function_t *pfn,
2941
                    const gs_range_t *pranges, cos_value_t *pvalue)
2942
4.70k
{
2943
4.70k
    if (pranges == NULL)
2944
4.70k
        return pdf_function(pdev, pfn, pvalue);
2945
0
    {
2946
        /*
2947
         * Create a temporary scaled function.  Note that the ranges
2948
         * represent the inverse scaling from what gs_function_make_scaled
2949
         * expects.
2950
         */
2951
0
        gs_memory_t *mem = pdev->pdf_memory;
2952
0
        gs_function_t *psfn;
2953
0
        gs_range_t *ranges = (gs_range_t *)
2954
0
            gs_alloc_byte_array(mem, pfn->params.n, sizeof(gs_range_t),
2955
0
                                "pdf_function_scaled");
2956
0
        int i, code;
2957
2958
0
        if (ranges == 0)
2959
0
            return_error(gs_error_VMerror);
2960
0
        for (i = 0; i < pfn->params.n; ++i) {
2961
0
            double rbase = pranges[i].rmin;
2962
0
            double rdiff = pranges[i].rmax - rbase;
2963
0
            double invbase = -rbase / rdiff;
2964
2965
0
            ranges[i].rmin = invbase;
2966
0
            ranges[i].rmax = invbase + 1.0 / rdiff;
2967
0
        }
2968
0
        code = gs_function_make_scaled(pfn, &psfn, ranges, mem);
2969
0
        if (code >= 0) {
2970
0
            code = pdf_function(pdev, psfn, pvalue);
2971
0
            gs_function_free(psfn, true, mem);
2972
0
        }
2973
0
        gs_free_object(mem, ranges, "pdf_function_scaled");
2974
0
        return code;
2975
0
    }
2976
0
}
2977
static int
2978
pdf_function_aux(gx_device_pdf *pdev, const gs_function_t *pfn,
2979
             pdf_resource_t **ppres)
2980
13.7k
{
2981
13.7k
    gs_function_info_t info;
2982
13.7k
    cos_param_list_writer_t rlist;
2983
13.7k
    pdf_resource_t *pres;
2984
13.7k
    cos_object_t *pcfn;
2985
13.7k
    cos_dict_t *pcd;
2986
13.7k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2987
2988
13.7k
    if (code < 0) {
2989
0
        *ppres = 0;
2990
0
        return code;
2991
0
    }
2992
13.7k
    *ppres = pres;
2993
13.7k
    pcfn = pres->object;
2994
13.7k
    gs_function_get_info(pfn, &info);
2995
13.7k
    if (FunctionType(pfn) == function_type_ArrayedOutput) {
2996
        /*
2997
         * Arrayed Output Functions are used internally to represent
2998
         * Shading Function entries that are arrays of Functions.
2999
         * They require special handling.
3000
         */
3001
0
        cos_array_t *pca;
3002
3003
0
        cos_become(pcfn, cos_type_array);
3004
0
        pca = (cos_array_t *)pcfn;
3005
0
        return pdf_function_array(pdev, pca, &info);
3006
0
    }
3007
13.7k
    if (info.DataSource != 0) {
3008
11.8k
        psdf_binary_writer writer;
3009
11.8k
        stream *save = pdev->strm;
3010
11.8k
        cos_stream_t *pcos;
3011
11.8k
        stream *s;
3012
3013
11.8k
        cos_become(pcfn, cos_type_stream);
3014
11.8k
        pcos = (cos_stream_t *)pcfn;
3015
11.8k
        pcd = cos_stream_dict(pcos);
3016
11.8k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3017
11.8k
        if (s == 0)
3018
0
            return_error(gs_error_VMerror);
3019
11.8k
        pdev->strm = s;
3020
11.8k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3021
11.8k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3022
11.8k
            )
3023
11.2k
            code = pdf_flate_binary(pdev, &writer);
3024
11.8k
        if (code >= 0) {
3025
11.8k
            static const pdf_filter_names_t fnames = {
3026
11.8k
                PDF_FILTER_NAMES
3027
11.8k
            };
3028
3029
11.8k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3030
11.8k
        }
3031
11.8k
        if (code >= 0) {
3032
11.8k
            byte buf[100];    /* arbitrary */
3033
11.8k
            ulong pos;
3034
11.8k
            uint count;
3035
11.8k
            const byte *ptr;
3036
3037
174k
            for (pos = 0; pos < info.data_size; pos += count) {
3038
162k
                count = min(sizeof(buf), info.data_size - pos);
3039
162k
                data_source_access_only(info.DataSource, pos, count, buf,
3040
162k
                                        &ptr);
3041
162k
                stream_write(writer.strm, ptr, count);
3042
162k
            }
3043
11.8k
            code = psdf_end_binary(&writer);
3044
11.8k
            s_close_filters(&s, s->strm);
3045
11.8k
        }
3046
11.8k
        pdev->strm = save;
3047
11.8k
        if (code < 0)
3048
0
            return code;
3049
11.8k
    } else {
3050
1.85k
        cos_become(pcfn, cos_type_dict);
3051
1.85k
        pcd = (cos_dict_t *)pcfn;
3052
1.85k
    }
3053
13.7k
    if (info.Functions != 0) {
3054
400
        cos_array_t *functions =
3055
400
            cos_array_alloc(pdev, "pdf_function(Functions)");
3056
400
        cos_value_t v;
3057
3058
400
        if (functions == 0)
3059
0
            return_error(gs_error_VMerror);
3060
400
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3061
400
            (code = cos_dict_put_c_key(pcd, "/Functions",
3062
400
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3063
400
            ) {
3064
0
            COS_FREE(functions, "pdf_function(Functions)");
3065
0
            return code;
3066
0
        }
3067
400
    }
3068
13.7k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3069
13.7k
    if (code < 0)
3070
0
        return code;
3071
13.7k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3072
13.7k
}
3073
static int
3074
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3075
10.2k
{
3076
10.2k
    return true;
3077
10.2k
}
3078
int
3079
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3080
13.7k
{
3081
13.7k
    pdf_resource_t *pres;
3082
13.7k
    int code = pdf_function_aux(pdev, pfn, &pres);
3083
3084
13.7k
    if (code < 0)
3085
0
        return code;
3086
13.7k
    if (pres->object->md5_valid)
3087
0
        pres->object->md5_valid = 0;
3088
3089
13.7k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3090
13.7k
    if (code < 0)
3091
0
        return code;
3092
13.7k
    pres->where_used |= pdev->used_mask;
3093
13.7k
    COS_OBJECT_VALUE(pvalue, pres->object);
3094
13.7k
    return 0;
3095
13.7k
}
3096
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3097
                               const gs_function_info_t *pinfo)
3098
400
{
3099
400
    int i, code = 0;
3100
400
    cos_value_t v;
3101
3102
1.77k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3103
1.37k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3104
1.37k
            (code = cos_array_add(pca, &v)) < 0
3105
1.37k
            ) {
3106
0
            break;
3107
0
        }
3108
1.37k
    }
3109
400
    return code;
3110
400
}
3111
3112
/* Write a Function object. */
3113
int
3114
pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, int64_t *pid)
3115
7.65k
{
3116
7.65k
    cos_value_t value;
3117
7.65k
    int code = pdf_function(pdev, pfn, &value);
3118
3119
7.65k
    if (code < 0)
3120
0
        return code;
3121
7.65k
    *pid = value.contents.object->id;
3122
7.65k
    return 0;
3123
7.65k
}
3124
3125
int
3126
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3127
3.42k
{
3128
3.42k
    char key[] = "/Functions";
3129
3.42k
    cos_value_t *v, v2;
3130
3131
3.42k
    if (cos_type(pco) == cos_type_dict) {
3132
580
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3133
580
        if (v && v->value_type == COS_VALUE_OBJECT) {
3134
139
            if (cos_type(v->contents.object) == cos_type_array){
3135
139
                int code=0;
3136
694
                while (code == 0) {
3137
555
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3138
555
                }
3139
139
            }
3140
139
        }
3141
580
    }
3142
3.42k
    if (cos_type(pco) == cos_type_array) {
3143
0
        int64_t index;
3144
0
        cos_array_t *pca = (cos_array_t *)pco;
3145
0
        const cos_array_element_t *element = cos_array_element_first(pca);
3146
0
        cos_value_t *v;
3147
3148
0
        while (element) {
3149
0
            element = cos_array_element_next(element, &index, (const cos_value_t **)&v);
3150
0
            if (v->value_type == COS_VALUE_OBJECT) {
3151
0
                if (pdf_find_resource_by_resource_id(pdev, resourceFunction, v->contents.object->id)){
3152
0
                    v->value_type = COS_VALUE_CONST;
3153
                    /* Need to remove the element from the array here */
3154
0
                }
3155
0
            }
3156
0
        }
3157
0
    }
3158
3.42k
    return 0;
3159
3.42k
}
3160
3161
/* Write a FontBBox dictionary element. */
3162
int
3163
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3164
18.5k
{
3165
18.5k
    stream *s = pdev->strm;
3166
    /*
3167
     * AR 4 doesn't like fonts with empty FontBBox, which
3168
     * happens when the font contains only space characters.
3169
     * Small bbox causes AR 4 to display a hairline. So we use
3170
     * the full BBox.
3171
     */
3172
18.5k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3173
18.5k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3174
3175
18.5k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3176
18.5k
             pbox->p.x, pbox->p.y, x, y);
3177
18.5k
    return 0;
3178
18.5k
}
3179
3180
/* Write a FontBBox dictionary element using floats for the values. */
3181
int
3182
pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
3183
3.10k
{
3184
3.10k
    stream *s = pdev->strm;
3185
    /*
3186
     * AR 4 doesn't like fonts with empty FontBBox, which
3187
     * happens when the font contains only space characters.
3188
     * Small bbox causes AR 4 to display a hairline. So we use
3189
     * the full BBox.
3190
     */
3191
3.10k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3192
3.10k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3193
3194
3.10k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3195
3.10k
             pbox->p.x, pbox->p.y, x, y);
3196
3.10k
    return 0;
3197
3.10k
}