Coverage Report

Created: 2026-04-09 07:06

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