Coverage Report

Created: 2026-10-28 08:05

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