Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/vector/gdevpdfj.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Image-writing utilities for pdfwrite driver */
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gdevpdfx.h"
22
#include "gdevpdfg.h"
23
#include "gdevpdfo.h"
24
#include "gxcspace.h"
25
#include "gsiparm4.h"
26
#include "gdevpsds.h"
27
#include "spngpx.h"
28
#include <stdlib.h> /* for atoi */
29
30
#define CHECK(expr)\
31
2.67M
  BEGIN if ((code = (expr)) < 0) return code; END
32
33
/* GC descriptors */
34
public_st_pdf_image_writer();
35
0
static ENUM_PTRS_WITH(pdf_image_writer_enum_ptrs, pdf_image_writer *piw)
36
0
     index -= 4;
37
0
     if (index < psdf_binary_writer_max_ptrs * piw->alt_writer_count) {
38
0
         gs_ptr_type_t ret =
39
0
             ENUM_USING(st_psdf_binary_writer, &piw->binary[index / psdf_binary_writer_max_ptrs],
40
0
                        sizeof(psdf_binary_writer), index % psdf_binary_writer_max_ptrs);
41
42
0
         if (ret == 0)   /* don't stop early */
43
0
             ENUM_RETURN(0);
44
0
         return ret;
45
0
    }
46
0
    return 0;
47
0
case 0: ENUM_RETURN(piw->pres);
48
0
case 1: ENUM_RETURN(piw->data);
49
0
case 2: ENUM_RETURN(piw->named);
50
0
case 3: ENUM_RETURN(piw->pres_mask);
51
0
ENUM_PTRS_END
52
0
static RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs, pdf_image_writer *piw)
53
0
{
54
0
    int i;
55
56
0
    for (i = 0; i < piw->alt_writer_count; ++i)
57
0
        RELOC_USING(st_psdf_binary_writer, &piw->binary[i],
58
0
                    sizeof(psdf_binary_writer));
59
0
    RELOC_VAR(piw->pres);
60
0
    RELOC_VAR(piw->data);
61
0
    RELOC_VAR(piw->named);
62
0
    RELOC_VAR(piw->pres_mask);
63
0
}
64
0
RELOC_PTRS_END
65
66
/* ---------------- Image stream dictionaries ---------------- */
67
68
const pdf_image_names_t pdf_image_names_full = {
69
    { PDF_COLOR_SPACE_NAMES },
70
    { PDF_FILTER_NAMES },
71
    PDF_IMAGE_PARAM_NAMES
72
};
73
const pdf_image_names_t pdf_image_names_short = {
74
    { PDF_COLOR_SPACE_NAMES_SHORT },
75
    { PDF_FILTER_NAMES_SHORT },
76
    PDF_IMAGE_PARAM_NAMES_SHORT
77
};
78
79
/* Store the values of image parameters other than filters. */
80
/* pdev is used only for updating procsets. */
81
/* pcsvalue is not used for masks. */
82
static int
83
pdf_put_pixel_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
84
                           const gs_pixel_image_t *pim,
85
                           const gs_color_space *pcs,
86
                           const pdf_image_names_t *pin,
87
                           const cos_value_t *pcsvalue)
88
222k
{
89
222k
    int num_components;
90
222k
    float indexed_decode[2];
91
222k
    const float *default_decode = NULL;
92
222k
    int code;
93
94
222k
    if (pcs) {
95
45.4k
        CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue));
96
45.4k
        pdf_color_space_procsets(pdev, pcs);
97
45.4k
        num_components = gs_color_space_num_components(pcs);
98
45.4k
        if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
99
2.63k
            indexed_decode[0] = 0;
100
2.63k
            indexed_decode[1] = (float)((1 << pim->BitsPerComponent) - 1);
101
2.63k
            default_decode = indexed_decode;
102
2.63k
        }
103
45.4k
    } else
104
177k
        num_components = 1;
105
222k
    CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width));
106
222k
    CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height));
107
222k
    CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent,
108
222k
                                 pim->BitsPerComponent));
109
222k
    {
110
222k
        int i;
111
112
436k
        for (i = 0; i < num_components * 2; ++i) {
113
386k
            if (pim->Decode[i] !=
114
386k
                (default_decode ? default_decode[i] : i & 1)
115
386k
                )
116
172k
                break;
117
386k
        }
118
222k
        if (i < num_components * 2) {
119
172k
            cos_array_t *pca =
120
172k
                cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)");
121
122
172k
            if (pca == 0)
123
0
                return_error(gs_error_VMerror);
124
172k
            if (pcs == NULL) {
125
                /* 269-01.ps sets /Decode[0 100] with a mask image. */
126
516k
                for (i = 0; i < num_components * 2; ++i)
127
344k
                    CHECK(cos_array_add_real(pca, min(pim->Decode[i], 1)));
128
172k
            } else {
129
943
                for (i = 0; i < num_components * 2; ++i)
130
774
                    CHECK(cos_array_add_real(pca, pim->Decode[i]));
131
169
            }
132
172k
            CHECK(cos_dict_put_c_key_object(pcd, pin->Decode,
133
172k
                                            COS_OBJECT(pca)));
134
172k
        }
135
222k
    }
136
222k
    if (pim->Interpolate) {
137
20.8k
        if (pdev->PDFA != 0)
138
0
            emprintf(pdev->memory,
139
20.8k
                     "PDFA doesn't allow images with Interpolate true.\n");
140
20.8k
        else
141
20.8k
            CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true"));
142
20.8k
    }
143
222k
    return 0;
144
222k
}
145
int
146
pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
147
                     const gs_pixel_image_t *pic,
148
                     const pdf_image_names_t *pin,
149
                     const cos_value_t *pcsvalue)
150
222k
{
151
222k
    const gs_color_space *pcs = pic->ColorSpace;
152
222k
    int code;
153
154
222k
    switch (pic->type->index) {
155
222k
    case 1: {
156
222k
        const gs_image1_t *pim = (const gs_image1_t *)pic;
157
158
222k
        if (pim->ImageMask) {
159
177k
            CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true"));
160
177k
            pdev->procsets |= ImageB;
161
177k
            pcs = NULL;
162
177k
        }
163
222k
    }
164
222k
        break;
165
222k
    case 3: {
166
        /*
167
         * Clients must treat this as a special case: they must call
168
         * pdf_put_image_values for the MaskDict separately, and must
169
         * add the Mask entry to the main image stream (dictionary).
170
         */
171
        /*const gs_image3_t *pim = (const gs_image3_t *)pic;*/
172
173
        /* Masked images are only supported starting in PDF 1.3. */
174
0
        if (pdev->CompatibilityLevel < 1.3)
175
0
            return_error(gs_error_rangecheck);
176
0
    }
177
0
        break;
178
6
    case 4: {
179
6
        const gs_image4_t *pim = (const gs_image4_t *)pic;
180
6
        int num_components = gs_color_space_num_components(pcs);
181
6
        cos_array_t *pca;
182
6
        int i;
183
184
        /* Masked images are only supported starting in PDF 1.3. */
185
6
        if (pdev->CompatibilityLevel < 1.3)
186
0
            break; /* Will convert into an imagemask with a pattern color. */
187
6
        pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)");
188
6
        if (pca == 0)
189
0
            return_error(gs_error_VMerror);
190
24
        for (i = 0; i < num_components; ++i) {
191
18
            int lo, hi;
192
193
18
            if (pim->MaskColor_is_range)
194
18
                lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1];
195
0
            else
196
0
                lo = hi = pim->MaskColor[i];
197
18
            CHECK(cos_array_add_int(pca, lo));
198
18
            CHECK(cos_array_add_int(pca, hi));
199
18
        }
200
6
        CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca)));
201
6
    }
202
6
        break;
203
6
    default:
204
0
        return_error(gs_error_rangecheck);
205
222k
    }
206
222k
    return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue);
207
222k
}
208
209
/* Store filters for an image. */
210
/* Currently this only saves parameters for CCITTFaxDecode. */
211
int
212
pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
213
                      const psdf_binary_writer * pbw,
214
                      const pdf_image_names_t *pin)
215
222k
{
216
222k
    return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names);
217
222k
}
218
219
/* ---------------- Image writing ---------------- */
220
221
/*
222
 * Fill in the image parameters for a device space bitmap.
223
 * PDF images are always specified top-to-bottom.
224
 * data_h is the actual number of data rows, which may be less than h.
225
 */
226
void
227
pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
228
                       int h_actual)
229
1.76M
{
230
1.76M
    pmat->xx = (float)w;
231
1.76M
    pmat->xy = 0;
232
1.76M
    pmat->yx = 0;
233
1.76M
    pmat->yy = (float)(-h_actual);
234
1.76M
    pmat->tx = (float)x;
235
1.76M
    pmat->ty = (float)(y + h);
236
1.76M
}
237
238
/*
239
 * Put out the gsave and matrix for an image.  y_scale adjusts the matrix
240
 * for images that end prematurely.
241
 */
242
void
243
pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
244
                     double y_scale)
245
81.1k
{
246
81.1k
    gs_matrix imat = {1, 0, 0, 1, 0 ,0};
247
248
81.1k
    gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat);
249
81.1k
    gs_matrix_scale(&imat, 1.0, y_scale, &imat);
250
81.1k
    pdf_put_matrix(pdev, "q ", &imat, "cm\n");
251
81.1k
}
252
253
/* Put out a reference to an image resource. */
254
int
255
pdf_do_image_by_id(gx_device_pdf * pdev, double scale,
256
             const gs_matrix * pimat, bool in_contents, gs_id id)
257
11.9k
{
258
    /* fixme : in_contents is always true (there are no calls with false). */
259
11.9k
    if (in_contents) {
260
11.9k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
261
262
11.9k
        if (code < 0)
263
0
            return code;
264
11.9k
    }
265
11.9k
    if (pimat)
266
11.9k
        pdf_put_image_matrix(pdev, pimat, scale);
267
11.9k
    pprintld1(pdev->strm, "/R%ld Do\nQ\n", id);
268
11.9k
    return 0;
269
11.9k
}
270
int
271
pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
272
             const gs_matrix * pimat, bool in_contents)
273
11.9k
{
274
    /* fixme : call pdf_do_image_by_id when pimam == NULL. */
275
11.9k
    double scale = 1;
276
277
11.9k
    if (pimat) {
278
        /* Adjust the matrix to account for short images. */
279
11.9k
        const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
280
11.9k
        scale = (double)pxo->data_height / pxo->height;
281
11.9k
    }
282
11.9k
    return pdf_do_image_by_id(pdev, scale, pimat, in_contents, pdf_resource_id(pres));
283
11.9k
}
284
285
/* ------ Begin / finish ------ */
286
287
/* Initialize image writer. */
288
void
289
pdf_image_writer_init(pdf_image_writer * piw)
290
209k
{
291
209k
    memset(piw, 0, sizeof(*piw));
292
209k
    piw->alt_writer_count = 1; /* Default. */
293
209k
}
294
295
/*
296
 * Begin writing an image, creating the resource if not in-line, and setting
297
 * up the binary writer.  If pnamed != 0, it is a stream object created by a
298
 * NI pdfmark.
299
 */
300
int
301
pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
302
                      gx_bitmap_id id, int w, int h, cos_dict_t *named,
303
                      bool in_line)
304
209k
{
305
    /* Patch pdev->strm so the right stream gets into the writer. */
306
209k
    stream *save_strm = pdev->strm;
307
209k
    cos_stream_t *data;
308
209k
    bool mask = (piw->data != NULL);
309
209k
    int alt_stream_index = (!mask ? 0 : piw->alt_writer_count);
310
209k
    int code;
311
312
209k
    if (in_line) {
313
189k
        piw->pres = 0;
314
189k
        piw->pin = &pdf_image_names_short;
315
189k
        data = cos_stream_alloc(pdev, "pdf_begin_image_data");
316
189k
        if (data == 0)
317
0
            return_error(gs_error_VMerror);
318
189k
        piw->end_string = " Q";
319
189k
        piw->named = 0;   /* must have named == 0 */
320
189k
    } else {
321
20.4k
        pdf_x_object_t *pxo;
322
20.4k
        cos_stream_t *pcos;
323
20.4k
        pdf_resource_t *pres;
324
325
        /*
326
         * Note that if named != 0, there are two objects with the same id
327
         * while the image is being accumulated: named, and pres->object.
328
         */
329
20.4k
        code = pdf_alloc_resource(pdev, resourceXObject, id, &pres,
330
20.4k
                                  (named ? named->id : -1L));
331
20.4k
        if (code < 0)
332
0
            return code;
333
20.4k
        *(mask ? &piw->pres_mask : &piw->pres) = pres;
334
20.4k
        cos_become(pres->object, cos_type_stream);
335
20.4k
        pres->rid = id;
336
20.4k
        piw->pin = &pdf_image_names_full;
337
20.4k
        pxo = (pdf_x_object_t *)pres;
338
20.4k
        pcos = (cos_stream_t *)pxo->object;
339
20.4k
        CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype",
340
20.4k
                                     "/Image"));
341
20.4k
        pxo->width = w;
342
20.4k
        pxo->height = h;
343
        /* Initialize data_height for the benefit of copy_{mono,color}. */
344
20.4k
        pxo->data_height = h;
345
20.4k
        data = pcos;
346
20.4k
        if (!mask)
347
20.4k
            piw->named = named;
348
20.4k
    }
349
209k
    pdev->strm = pdev->streams.strm;
350
209k
    pdev->strm = cos_write_stream_alloc(data, pdev, "pdf_begin_write_image");
351
209k
    if (pdev->strm == 0) {
352
0
        pdev->strm = save_strm;
353
0
        return_error(gs_error_VMerror);
354
0
    }
355
209k
    if (!mask)
356
209k
        piw->data = data;
357
209k
    piw->height = h;
358
209k
    code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary[alt_stream_index]);
359
209k
    piw->binary[alt_stream_index].target = NULL; /* We don't need target with cos_write_stream. */
360
209k
    pdev->strm = save_strm;
361
209k
    return code;
362
209k
}
363
364
/*
365
 *  Make alternative stream for image compression choice.
366
 */
367
int
368
pdf_make_alt_stream(gx_device_pdf * pdev, psdf_binary_writer * pbw)
369
13.1k
{
370
13.1k
    stream *save_strm = pdev->strm;
371
13.1k
    cos_stream_t *pcos = cos_stream_alloc(pdev, "pdf_make_alt_stream");
372
13.1k
    int code;
373
374
13.1k
    if (pcos == 0)
375
0
        return_error(gs_error_VMerror);
376
13.1k
    pcos->id = 0;
377
13.1k
    CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype", "/Image"));
378
13.1k
    pbw->strm = cos_write_stream_alloc(pcos, pdev, "pdf_make_alt_stream");
379
13.1k
    if (pbw->strm == 0)
380
0
        return_error(gs_error_VMerror);
381
13.1k
    pbw->dev = (gx_device_psdf *)pdev;
382
13.1k
    pbw->memory = pdev->pdf_memory;
383
13.1k
    pdev->strm = pbw->strm;
384
13.1k
    code = psdf_begin_binary((gx_device_psdf *) pdev, pbw);
385
13.1k
    pdev->strm = save_strm;
386
13.1k
    pbw->target = NULL; /* We don't need target with cos_write_stream. */
387
13.1k
    return code;
388
13.1k
}
389
390
/* Begin writing the image data, setting up the dictionary and filters. */
391
int
392
pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
393
                     const gs_pixel_image_t * pim, const cos_value_t *pcsvalue,
394
                     int alt_writer_index)
395
222k
{
396
397
222k
    cos_stream_t *s;
398
222k
    cos_dict_t *pcd;
399
222k
    int code;
400
401
222k
    s = cos_stream_from_pipeline(piw->binary[alt_writer_index].strm);
402
222k
    if (s == 0L)
403
0
        return gs_note_error(gs_error_ioerror);
404
405
222k
    pcd = cos_stream_dict(s);
406
222k
    code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue);
407
222k
    if (code >= 0)
408
222k
        code = pdf_put_image_filters(pcd, pdev, &piw->binary[alt_writer_index], piw->pin);
409
222k
    if (code < 0) {
410
0
        if (!piw->pres)
411
0
            COS_FREE(piw->data, "pdf_begin_image_data");
412
0
        piw->data = 0;
413
0
    }
414
222k
    if (pdev->JPEG_PassThrough) {
415
4.38k
        CHECK(cos_dict_put_c_strings(pcd, "/Filter", "/DCTDecode"));
416
4.38k
    }
417
222k
    if (pdev->JPX_PassThrough) {
418
1.05k
        CHECK(cos_dict_put_c_strings(pcd, "/Filter", "/JPXDecode"));
419
1.05k
    }
420
222k
    if (pdev->PendingOC != 0) {
421
19
        char str[256];
422
19
        gs_param_string param;
423
19
        cos_object_t *pco = NULL;
424
425
19
        param.data = (const byte *)pdev->PendingOC;
426
19
        param.size = strlen(pdev->PendingOC);
427
19
        code = pdf_refer_named(pdev, &param, &pco);
428
19
        if(code < 0)
429
0
            return code;
430
431
19
        gs_snprintf(str, sizeof(str), "%"PRId64" 0 R", pco->id);
432
19
        if (piw->pres != NULL && piw->pres->object != NULL)
433
19
            code = cos_dict_put_string_copy((cos_dict_t *)piw->pres->object, "/OC", str);
434
435
19
        gs_free_object(pdev->memory->non_gc_memory, pdev->PendingOC, "");
436
19
        pdev->PendingOC = NULL;
437
19
        if(code < 0)
438
0
            return code;
439
19
    }
440
222k
    return code;
441
222k
}
442
443
/* Complete image data. */
444
int
445
pdf_complete_image_data(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h,
446
                        int width, int bits_per_pixel)
447
17.8k
{
448
17.8k
    if (data_h != piw->height) {
449
2.14k
        if (piw->binary[0].strm->procs.process == s_DCTE_template.process ||
450
2.12k
            piw->binary[0].strm->procs.process == s_PNGPE_template.process ) {
451
            /*  Since DCTE and PNGPE can't safely close with incomplete data,
452
                we add stub data to complete the stream.
453
            */
454
2.02k
            int bytes_per_line = (width * bits_per_pixel + 7) / 8;
455
2.02k
            int lines_left = piw->height - data_h;
456
2.02k
            byte buf[256];
457
2.02k
            const uint lb = sizeof(buf);
458
2.02k
            int i, l;
459
2.02k
            uint ignore;
460
461
2.02k
            memset(buf, 128, lb);
462
620k
            for (; lines_left; lines_left--)
463
2.44M
                for (i = 0; i < piw->alt_writer_count; i++) {
464
5.71M
                    for (l = bytes_per_line; l > 0; l -= lb)
465
3.89M
                        if ((sputs(piw->binary[i].strm, buf, min(l, lb),
466
3.89M
                                            &ignore)) < 0)
467
0
                            return_error(gs_error_ioerror);
468
1.82M
                }
469
2.02k
        }
470
2.14k
    }
471
17.8k
    return 0;
472
17.8k
}
473
474
/* Finish writing the binary image data. */
475
int
476
pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h)
477
209k
{
478
209k
    int code, code1 = 0;
479
480
209k
    if (piw->alt_writer_count > 2)
481
12.9k
        code = pdf_choose_compression(piw, true);
482
196k
    else
483
196k
        code = psdf_end_binary(&piw->binary[0]);
484
    /* If the image ended prematurely, update the Height. */
485
209k
    if (data_h != piw->height) {
486
7.22k
        char data[256];
487
7.22k
        int OutHeight;
488
7.22k
        cos_value_t *value;
489
7.22k
        value = (cos_value_t *)cos_dict_find(cos_stream_dict(piw->data),
490
7.22k
                                      (const byte *)piw->pin->Height, strlen(piw->pin->Height));
491
7.22k
        if (!value || value->contents.chars.size > 255)
492
0
            return(gs_error_rangecheck);
493
7.22k
        strncpy((char *)&data, (const char *)value->contents.chars.data, value->contents.chars.size);
494
7.22k
        data[value->contents.chars.size] = 0x00;
495
7.22k
        OutHeight = atoi(data);
496
7.22k
        if (OutHeight != piw->height) {
497
            /* Looks like we are downsampling, so we can't use the number
498
             * of rows of data actually received, we must divide those by
499
             * the sampling factor.
500
             */
501
0
            float factor = (float)OutHeight / piw->height;
502
0
            OutHeight = (int)(factor * data_h);
503
0
            code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
504
0
                                      piw->pin->Height, OutHeight);
505
7.22k
        } else {
506
507
7.22k
            code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
508
7.22k
                                      piw->pin->Height, data_h);
509
7.22k
        }
510
7.22k
    }
511
209k
    return code < 0 ? code : code1;
512
209k
}
513
514
/* When writing out an image, we check to see if its a duplicate of an existing image, and if so we simply
515
 * use the existing image. There is one potential problem here; if we have an image with a SMask, the SMask is
516
 * itself animage, and the SMask image and the image which uses it are identical. Because we don't add the SMask
517
 * entry to the image dictionary until we have closed the image, its possible that the image can detect the alredy
518
 * stored SMask image as being identical and attempt to use the SMask image instead. This leads to an image with
519
 * an SMask entry referencing itself.
520
 * We detect this here simply by checking if the detected image resource ID is the same as any current SMask. Worst
521
 * case is we fail to detect a duplicate, which is better than detecting an incorrect duplicate.
522
 * This check function is only used in pdf_end_write_image() below as an argument to pdf_substitute_resource().
523
 */
524
static int
525
smask_image_check(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
526
11.4k
{
527
11.4k
    cos_value_t *v = NULL;
528
529
    /* image_mask_id is non-zero if we have a pending SMask image */
530
11.4k
    if (pdev->image_mask_id != 0) {
531
3.79k
        if (pres0->object->id == pdev->image_mask_id || pres1->object->id == pdev->image_mask_id)
532
0
            return 0;
533
3.79k
        if (pdev->image_mask_is_SMask)
534
3.79k
            v = (cos_value_t *)cos_dict_find_c_key((const cos_dict_t *)pres1->object, "/SMask");
535
0
        else
536
0
            v = (cos_value_t *)cos_dict_find_c_key((const cos_dict_t *)pres1->object, "/Mask");
537
3.79k
        if (v == 0)
538
0
            return 0;
539
3.79k
        if (v != 0) {
540
3.79k
            const byte *p = v->contents.chars.data;
541
3.79k
            int ix = 0;
542
543
12.8k
            while (*p != 0x20) {
544
9.08k
                if (p > v->contents.chars.data + v->contents.chars.size)
545
0
                    return 0;
546
9.08k
                ix *= 10;
547
9.08k
                ix += (*p++) - 0x30;
548
9.08k
            }
549
3.79k
            if (ix != pdev->image_mask_id)
550
0
                return 0;
551
3.79k
        }
552
3.79k
    }
553
11.4k
    return 1;
554
11.4k
}
555
556
557
/* Abort an image without writing it.
558
 * Frees any associated memory.
559
 */
560
int
561
pdf_end_abort_image(gx_device_pdf * pdev, pdf_image_writer * piw)
562
4.45k
{
563
4.45k
    pdf_resource_t *pres = piw->pres;
564
565
4.45k
    if (!pres) {
566
1.78k
        COS_FREE(piw->data, "pdf_end_write_image");
567
1.78k
    }
568
4.45k
    return 0;
569
4.45k
}
570
571
/*
572
 * Finish writing an image.  If in-line, write the BI/dict/ID/data/EI and
573
 * return 1; if a resource, write the resource definition and return 0.
574
 */
575
int
576
pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw)
577
205k
{
578
205k
    pdf_resource_t *pres = piw->pres;
579
580
205k
    if (pres) {     /* image resource */
581
17.8k
        cos_object_t *const pco = pres->object;
582
17.8k
        cos_stream_t *const pcs = (cos_stream_t *)pco;
583
17.8k
        cos_dict_t *named = piw->named;
584
17.8k
        int code;
585
586
17.8k
        if (named) {
587
6
            if (pdev->ForOPDFRead) {
588
6
                code = cos_dict_put_c_key_bool(named, "/.Global", true);
589
6
                if (code < 0)
590
0
                    return code;
591
6
            }
592
            /*
593
             * This image was named by NI.  Copy any dictionary elements
594
             * from the named dictionary to the image stream, and then
595
             * associate the name with the stream.
596
             */
597
6
            code = cos_dict_move_all(cos_stream_dict(pcs), named);
598
6
            if (code < 0)
599
0
                return code;
600
6
            pres->named = true;
601
            /*
602
             * We need to make the entry in the name dictionary point to
603
             * the stream (pcs) rather than the object created by NI (named).
604
             * Unfortunately, we no longer know what dictionary to use.
605
             * Instead, overwrite the latter with the former's contents,
606
             * and change the only relevant pointer.
607
             */
608
6
            *(cos_object_t *)named = *pco;
609
6
            pres->object = COS_OBJECT(named);
610
17.8k
        } else if (!pres->named) { /* named objects are written at the end */
611
17.8k
            if (pdev->DetectDuplicateImages) {
612
17.8k
                pdf_x_object_t *pxo = (pdf_x_object_t *)piw->pres;
613
17.8k
                int height = pxo->height, width = pxo->width;
614
615
17.8k
                code = pdf_substitute_resource(pdev, &piw->pres, resourceXObject, smask_image_check, false);
616
17.8k
                if (code < 0)
617
0
                    return code;
618
619
                /* These values are related to the image matrix and should *not* be
620
                 * substituted if we found a duplicate image, or the matrix calculation
621
                 * will be incorrect! This only seems to matter for the PCL interpreter.
622
                 */
623
17.8k
                pxo = (pdf_x_object_t *)piw->pres;
624
17.8k
                pxo->height = height;
625
17.8k
                pxo->width = width;
626
17.8k
            } else {
627
0
                pdf_reserve_object_id(pdev, piw->pres, gs_no_id);
628
0
            }
629
            /*  Warning : If the substituted image used alternate streams,
630
                its space in the pdev->streams.strm file won't be released. */
631
17.8k
            piw->pres->where_used |= pdev->used_mask;
632
17.8k
        }
633
17.8k
        code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", piw->pres);
634
17.8k
        if (code < 0)
635
0
            return code;
636
17.8k
        return 0;
637
187k
    } else {     /* in-line image */
638
187k
        stream *s = pdev->strm;
639
187k
        uint KeyLength = pdev->KeyLength;
640
641
187k
        stream_puts(s, "BI\n");
642
187k
        cos_stream_elements_write(piw->data, pdev);
643
187k
        stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n"));
644
187k
        pdev->KeyLength = 0; /* Disable encryption for the inline image. */
645
187k
        cos_stream_contents_write(piw->data, pdev);
646
187k
        pdev->KeyLength = KeyLength;
647
187k
        pprints1(s, "\nEI%s\n", piw->end_string);
648
187k
        COS_FREE(piw->data, "pdf_end_write_image");
649
187k
        return 1;
650
187k
    }
651
205k
}
652
653
/* ------ Copy data ------ */
654
655
/* Copy the data for a mask or monobit bitmap. */
656
int
657
pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster,
658
                   int w, int h, byte invert)
659
121k
{
660
121k
    int yi;
661
662
2.97M
    for (yi = 0; yi < h; ++yi) {
663
2.84M
        const byte *data = base + yi * raster + (sourcex >> 3);
664
2.84M
        int sbit = sourcex & 7;
665
666
2.84M
        if (sbit == 0) {
667
2.84M
            int nbytes = (w + 7) >> 3;
668
2.84M
            int i;
669
670
24.8M
            for (i = 0; i < nbytes; ++data, ++i)
671
21.9M
                sputc(s, (byte)(*data ^ invert));
672
2.84M
        } else {
673
0
            int wleft = w;
674
0
            int rbit = 8 - sbit;
675
676
0
            for (; wleft + sbit > 8; ++data, wleft -= 8)
677
0
                sputc(s, (byte)(((*data << sbit) + (data[1] >> rbit)) ^ invert));
678
0
            if (wleft > 0)
679
0
                sputc(s, (byte)(((*data << sbit) ^ invert) &
680
0
                      (byte) (0xff00 >> wleft)));
681
0
        }
682
2.84M
    }
683
121k
    return 0;
684
121k
}
685
686
/* Copy the data for a colored image (device pixels). */
687
int
688
pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster,
689
                    int w, int h, int bytes_per_pixel)
690
1.39k
{
691
1.39k
    int yi;
692
693
155k
    for (yi = 0; yi < h; ++yi) {
694
153k
        uint ignore;
695
696
153k
        sputs(s, base + sourcex * bytes_per_pixel + yi * raster,
697
153k
              w * bytes_per_pixel, &ignore);
698
153k
    }
699
1.39k
    return 0;
700
1.39k
}
701
702
/* Choose image compression - auxiliary procs */
703
static inline bool much_bigger__DL(long l1, long l2)
704
2.18M
{
705
2.18M
    return l1 > 1024*1024 && (l2 > 0 && l2 < l1 / 3);
706
2.18M
}
707
static void
708
pdf_choose_compression_cos(pdf_image_writer *piw, cos_stream_t *s[2], bool force)
709
1.10M
{   /*  Assume s[0] is Flate, s[1] is DCT, s[2] is chooser. */
710
1.10M
    long l0, l1;
711
1.10M
    int k0, k1;
712
713
1.10M
    l0 = cos_stream_length(s[0]);
714
1.10M
    l1 = cos_stream_length(s[1]);
715
716
1.10M
    if ((force && l0 <= l1) || l1 == -1)
717
10.9k
        k0 = 1; /* Use Flate if it is not longer. Or if the DCT failed */
718
1.09M
    else {
719
1.09M
        k0 = s_compr_chooser__get_choice(
720
1.09M
            (stream_compr_chooser_state *)piw->binary[2].strm->state, force);
721
1.09M
        if (k0 && l0 > 0 && l1 > 0)
722
1.78k
            k0--;
723
1.09M
        else if (much_bigger__DL(l0, l1))
724
0
            k0 = 0;
725
1.09M
        else if (much_bigger__DL(l1, l0) || force)
726
352
            k0 = 1;
727
1.09M
        else
728
1.09M
           return;
729
1.09M
    }
730
13.1k
    k1 = 1 - k0;
731
13.1k
    s_close_filters(&piw->binary[k0].strm, piw->binary[k0].target);
732
13.1k
    s[k0]->cos_procs->release((cos_object_t *)s[k0], "pdf_image_choose_filter");
733
13.1k
    s[k0]->written = 1;
734
13.1k
    piw->binary[0].strm = piw->binary[k1].strm;
735
13.1k
    s_close_filters(&piw->binary[2].strm, piw->binary[2].target);
736
13.1k
    piw->binary[1].strm = piw->binary[2].strm = 0; /* for GC */
737
13.1k
    piw->binary[1].target = piw->binary[2].target = 0;
738
13.1k
    s[k1]->id = piw->pres->object->id;
739
13.1k
    piw->pres->object = (cos_object_t *)s[k1];
740
13.1k
    piw->data = s[k1];
741
13.1k
    if (piw->alt_writer_count > 3) {
742
0
        piw->binary[1] = piw->binary[3];
743
0
        piw->binary[3].strm = 0; /* for GC */
744
0
        piw->binary[3].target = 0;
745
0
    }
746
13.1k
    piw->alt_writer_count -= 2;
747
13.1k
}
748
749
/* End binary with choosing image compression. */
750
int
751
pdf_choose_compression(pdf_image_writer * piw, bool end_binary)
752
1.10M
{
753
1.10M
    cos_stream_t *s[2];
754
1.10M
    int status;
755
756
1.10M
    s[0] = cos_stream_from_pipeline(piw->binary[0].strm);
757
1.10M
    s[1] = cos_stream_from_pipeline(piw->binary[1].strm);
758
759
1.10M
    if (s[0] == 0L) {
760
0
        return_error(gs_error_ioerror);
761
0
    }
762
1.10M
    if (s[1] == 0L) {
763
0
        s_close_filters(&piw->binary[0].strm, piw->binary[0].target);
764
0
        return_error(gs_error_ioerror);
765
0
    }
766
1.10M
    if (end_binary) {
767
12.9k
        status = s_close_filters(&piw->binary[0].strm, piw->binary[0].target);
768
12.9k
        if (status < 0)
769
0
            return_error(gs_error_ioerror);
770
12.9k
        status = s_close_filters(&piw->binary[1].strm, piw->binary[1].target);
771
12.9k
        if (status < 0)
772
908
            s[1]->length = -1;
773
12.9k
    }
774
1.10M
    pdf_choose_compression_cos(piw, s, end_binary);
775
1.10M
    return 0;
776
1.10M
}