Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/devices/vector/gdevpdfk.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* Lab and ICCBased color space writing */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "gx.h"
21
#include "gxcspace.h"
22
#include "stream.h"
23
#include "gsicc.h"
24
#include "gserrors.h"
25
#include "gxcie.h"
26
#include "gdevpdfx.h"
27
#include "gdevpdfg.h"
28
#include "gdevpdfc.h"
29
#include "gdevpdfo.h"
30
#include "strimpl.h"
31
#include "gsicc_create.h"
32
#include "gsicc_manage.h"
33
34
/* ------ CIE space synthesis ------ */
35
36
/* Add a /Range entry to a CIE-based color space dictionary. */
37
static int
38
pdf_cie_add_ranges(gx_device_pdf *pdev, cos_dict_t *pcd, const gs_range *prange, int n, bool clamp)
39
0
{
40
0
    cos_array_t *pca = cos_array_alloc(pdev, "pdf_cie_add_ranges");
41
0
    int code = 0, i;
42
43
0
    if (pca == 0)
44
0
        return_error(gs_error_VMerror);
45
0
    for (i = 0; i < n; ++i) {
46
0
        double rmin = prange[i].rmin, rmax = prange[i].rmax;
47
48
0
        if (clamp) {
49
0
            if (rmin < 0) rmin = 0;
50
0
            if (rmax > 1) rmax = 1;
51
0
        }
52
0
        if ((code = cos_array_add_real(pca, rmin)) < 0 ||
53
0
            (code = cos_array_add_real(pca, rmax)) < 0
54
0
            )
55
0
            break;
56
0
    }
57
0
    if (code >= 0)
58
0
        code = cos_dict_put_c_key_object(pcd, "/Range", COS_OBJECT(pca));
59
0
    if (code < 0)
60
0
        COS_FREE(pca, "pdf_cie_add_ranges");
61
0
    return code;
62
0
}
63
64
/* Transform a CIEBased color to XYZ. */
65
static int
66
cie_to_xyz(const double *in, double out[3], const gs_color_space *pcs,
67
           const gs_gstate *pgs, const gs_cie_common *pciec)
68
0
{
69
0
    gs_client_color cc;
70
0
    frac xyz[3];
71
0
    int ncomp = gs_color_space_num_components(pcs);
72
0
    int i;
73
74
0
    gs_color_space_index cs_index;
75
0
    const gs_vector3 *const pWhitePoint = &pciec->points.WhitePoint;
76
0
    float xyz_float[3];
77
78
0
    cs_index = gs_color_space_get_index(pcs);
79
80
0
    for (i = 0; i < ncomp; ++i)
81
0
        cc.paint.values[i] = in[i];
82
83
    /* The standard concretization makes use of the equivalent ICC profile
84
       to ensure that all color management is handled by the CMM.
85
       Unfortunately, we can't do that here since we have no access to the
86
       icc manager.  Also the PDF write outputs have restrictions on the
87
       ICC profiles that can be embedded so we must use this older form.
88
       Need to add an ICC version number into the icc creator to enable
89
       creation to and from various versions */
90
91
0
    switch (cs_index) {
92
0
        case gs_color_space_index_CIEA:
93
0
            gx_psconcretize_CIEA(&cc, pcs, xyz, xyz_float, pgs);
94
0
            break;
95
0
        case gs_color_space_index_CIEABC:
96
0
            gx_psconcretize_CIEABC(&cc, pcs, xyz, xyz_float, pgs);
97
0
            break;
98
0
        case gs_color_space_index_CIEDEF:
99
0
            gx_psconcretize_CIEDEF(&cc, pcs, xyz, xyz_float, pgs);
100
0
            break;
101
0
        case gs_color_space_index_CIEDEFG:
102
0
           gx_psconcretize_CIEDEFG(&cc, pcs, xyz, xyz_float, pgs);
103
0
           break;
104
0
        default:
105
            /* Only to silence a Coverity uninitialised variable warning */
106
0
            memset(&xyz_float, 0x00, sizeof(xyz_float));
107
0
            break;
108
0
    }
109
0
    if (cs_index == gs_color_space_index_CIEA) {
110
        /* AR forces this case to always be achromatic.  We will
111
        do the same even though it does not match the PS
112
        specification */
113
        /* Use the resulting Y value to scale the wp Illumination.
114
        note that we scale to the whitepoint here.  Matrix out
115
        handles mapping to CIE D50.  This forces an achromatic result */
116
0
        xyz_float[0] = pWhitePoint->u * xyz_float[1];
117
0
        xyz_float[2] = pWhitePoint->w * xyz_float[1];
118
0
    }
119
120
    /* Do wp mapping to D50 in XYZ for now.  We should do bradford correction.
121
       Will add that in next release */
122
0
    out[0] = xyz_float[0]*0.9642/pWhitePoint->u;
123
0
    out[1] = xyz_float[1];
124
0
    out[2] = xyz_float[2]*0.8249/pWhitePoint->w;
125
0
    return 0;
126
0
}
127
128
/* ------ Lab space writing and synthesis ------ */
129
130
/* Transform XYZ values to Lab. */
131
static double
132
lab_g_inverse(double v)
133
0
{
134
0
    if (v >= (6.0 * 6.0 * 6.0) / (29 * 29 * 29))
135
0
        return pow(v, 1.0 / 3); /* use cbrt if available? */
136
0
    else
137
0
        return (v * (841.0 / 108) + 4.0 / 29);
138
0
}
139
static void
140
xyz_to_lab(const double xyz[3], double lab[3], const gs_cie_common *pciec)
141
0
{
142
0
    const gs_vector3 *const pWhitePoint = &pciec->points.WhitePoint;
143
0
    double L, lunit;
144
0
145
0
    /* Calculate L* first. */
146
0
    L = lab_g_inverse(xyz[1] / pWhitePoint->v) * 116 - 16;
147
0
    /* Clamp L* to the PDF range [0..100]. */
148
0
    if (L < 0)
149
0
        L = 0;
150
0
    else if (L > 100)
151
0
        L = 100;
152
0
    lab[1] = L;
153
0
    lunit = (L + 16) / 116;
154
0
155
0
    /* Calculate a* and b*. */
156
0
    lab[0] = (lab_g_inverse(xyz[0] / pWhitePoint->u) - lunit) * 500;
157
0
    lab[2] = (lab_g_inverse(xyz[2] / pWhitePoint->w) - lunit) * -200;
158
0
}
159
160
/* Create a PDF Lab color space corresponding to a CIEBased color space. */
161
static int
162
lab_range(gs_range range_out[3] /* only [1] and [2] used */,
163
          const gs_color_space *pcs, const gs_cie_common *pciec,
164
          const gs_range *ranges, gs_memory_t *mem)
165
0
{
166
0
    /*
167
0
     * Determine the range of a* and b* by evaluating the color space
168
0
     * mapping at all of its extrema.
169
0
     */
170
0
    int ncomp = gs_color_space_num_components(pcs);
171
0
    gs_gstate *pgs;
172
0
    int code = gx_cie_to_xyz_alloc(&pgs, pcs, mem);
173
0
    int i, j;
174
0
175
0
    if (code < 0)
176
0
        return code;
177
0
    for (j = 1; j < 3; ++j)
178
0
        range_out[j].rmin = 1000.0, range_out[j].rmax = -1000.0;
179
0
    for (i = 0; i < 1 << ncomp; ++i) {
180
0
        double in[4], xyz[3];
181
0
182
0
        for (j = 0; j < ncomp; ++j)
183
0
            in[j] = (i & (1 << j) ? ranges[j].rmax : ranges[j].rmin);
184
0
        if (cie_to_xyz(in, xyz, pcs, pgs, pciec) >= 0) {
185
0
            double lab[3];
186
0
187
0
            xyz_to_lab(xyz, lab, pciec);
188
0
            for (j = 1; j < 3; ++j) {
189
0
                range_out[j].rmin = min(range_out[j].rmin, lab[j]);
190
0
                range_out[j].rmax = max(range_out[j].rmax, lab[j]);
191
0
            }
192
0
        }
193
0
    }
194
0
    gx_cie_to_xyz_free(pgs);
195
0
    return 0;
196
0
}
197
/*
198
 * Create a Lab color space object.
199
 * This procedure is exported for Lab color spaces in gdevpdfc.c.
200
 */
201
int
202
pdf_put_lab_color_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd,
203
                        const gs_range ranges[3] /* only [1] and [2] used */)
204
0
{
205
0
    int code;
206
0
    cos_value_t v;
207
208
0
    if ((code = cos_array_add(pca, cos_c_string_value(&v, "/Lab"))) >= 0)
209
0
        code = pdf_cie_add_ranges(pdev, pcd, ranges + 1, 2, false);
210
0
    return code;
211
0
}
212
213
/*
214
 * Create a Lab color space for a CIEBased space that can't be represented
215
 * directly as a Calxxx or Lab space.
216
 */
217
static int
218
pdf_convert_cie_to_lab(gx_device_pdf *pdev, cos_array_t *pca,
219
                       const gs_color_space *pcs,
220
                       const gs_cie_common *pciec, const gs_range *prange)
221
0
{
222
0
    cos_dict_t *pcd;
223
0
    gs_range ranges[3];
224
0
    int code;
225
226
    /****** NOT IMPLEMENTED YET, REQUIRES TRANSFORMING VALUES ******/
227
0
    if (1) return_error(gs_error_rangecheck);
228
0
    pcd = cos_dict_alloc(pdev, "pdf_convert_cie_to_lab(dict)");
229
0
    if (pcd == 0)
230
0
        return_error(gs_error_VMerror);
231
0
    if ((code = lab_range(ranges, pcs, pciec, prange, pdev->pdf_memory)) < 0 ||
232
0
        (code = pdf_put_lab_color_space(pdev, pca, pcd, ranges)) < 0 ||
233
0
        (code = pdf_finish_cie_space(pdev, pca, pcd, pciec)) < 0
234
0
        )
235
0
        COS_FREE(pcd, "pdf_convert_cie_to_lab(dict)");
236
0
    return code;
237
0
}
238
239
/* ------ ICCBased space writing and synthesis ------ */
240
241
/* Define standard and short color space names. */
242
const pdf_color_space_names_t base_names = {
243
    PDF_COLOR_SPACE_NAMES
244
};
245
246
static int put_calgray_color_space(gx_device_pdf *pdev, const gs_gstate * pgs, const gs_color_space *pcs, cos_array_t *pca)
247
0
{
248
0
    int code;
249
0
    cos_value_t v;
250
0
    cos_dict_t *pcd;
251
0
    cos_array_t *WP = NULL, *BP = NULL;
252
253
0
    pcd = cos_dict_alloc(pdev, "write_calgray_color_space");
254
0
    if (pcd == NULL)
255
0
        return_error(gs_error_VMerror);
256
257
0
    WP = cos_array_from_floats(pdev, pcs->params.calgray.WhitePoint, 3,
258
0
                                             "write_calgray_color_space");
259
0
    if (WP == NULL) {
260
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
261
0
        return_error(gs_error_VMerror);
262
0
    }
263
0
    BP = cos_array_from_floats(pdev, pcs->params.calgray.BlackPoint, 3,
264
0
                                             "write_calgray_color_space");
265
0
    if (BP == NULL) {
266
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
267
0
        cos_free((cos_object_t *)WP, "write_calgray_color_space");
268
0
        return_error(gs_error_VMerror);
269
0
    }
270
271
0
    code = cos_dict_put_c_key(pcd, "/BlackPoint", COS_OBJECT_VALUE(&v, BP));
272
0
    if (code < 0)
273
0
        goto error;
274
275
0
    code = cos_dict_put_c_key(pcd, "/WhitePoint",  COS_OBJECT_VALUE(&v, WP));
276
0
    if (code < 0)
277
0
        goto error;
278
279
0
    code = cos_dict_put_c_key_real(pcd, "/Gamma", pcs->params.calgray.Gamma);
280
281
0
    code = cos_array_add_c_string(pca, "/CalGray");
282
0
    if (code < 0)
283
0
        goto error;
284
285
0
    code = cos_array_add(pca,  COS_OBJECT_VALUE(&v, pcd));
286
0
    if (code < 0)
287
0
        goto error;
288
289
0
    return 0;
290
291
0
error:
292
0
    cos_free((cos_object_t *)pcd, "write_calgray_color_space");
293
0
    cos_free((cos_object_t *)WP, "write_calgray_color_space");
294
0
    cos_free((cos_object_t *)BP, "write_calgray_color_space");
295
0
    return code;
296
0
}
297
298
static int put_calrgb_color_space(gx_device_pdf *pdev, const gs_gstate * pgs, const gs_color_space *pcs, cos_array_t *pca)
299
0
{
300
0
    int code;
301
0
    cos_value_t v;
302
0
    cos_dict_t *pcd = NULL;
303
0
    cos_array_t *WP = NULL, *BP = NULL, *Gamma = NULL, *Matrix;
304
305
0
    pcd = cos_dict_alloc(pdev, "write_calrgb_color_space");
306
0
    if (pcd == NULL)
307
0
        return_error(gs_error_VMerror);
308
309
0
    WP = cos_array_from_floats(pdev, pcs->params.calrgb.WhitePoint, 3,
310
0
                                             "write_calrgb_color_space");
311
0
    if (WP == NULL) {
312
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
313
0
        cos_free((cos_object_t *)pcd, "write_calrgb_color_space");
314
0
        return_error(gs_error_VMerror);
315
0
    }
316
0
    BP = cos_array_from_floats(pdev, pcs->params.calrgb.BlackPoint, 3,
317
0
                                             "write_calrgb_color_space");
318
0
    if (BP == NULL) {
319
0
        cos_free((cos_object_t *)pcd, "write_calrgb_color_space");
320
0
        cos_free((cos_object_t *)WP, "write_calrgb_color_space");
321
0
        return_error(gs_error_VMerror);
322
0
    }
323
324
0
    Gamma = cos_array_from_floats(pdev, pcs->params.calrgb.Gamma, 3,
325
0
                                             "write_calrgb_color_space");
326
0
    if (Gamma == NULL) {
327
0
        cos_free((cos_object_t *)BP, "write_calrgb_color_space");
328
0
        cos_free((cos_object_t *)pcd, "write_calrgb_color_space");
329
0
        cos_free((cos_object_t *)WP, "write_calrgb_color_space");
330
0
        return_error(gs_error_VMerror);
331
0
    }
332
333
0
    Matrix = cos_array_from_floats(pdev, pcs->params.calrgb.Matrix, 9,
334
0
                                             "write_calrgb_color_space");
335
0
    if (Matrix == NULL) {
336
0
        cos_free((cos_object_t *)Gamma, "write_calrgb_color_space");
337
0
        cos_free((cos_object_t *)BP, "write_calrgb_color_space");
338
0
        cos_free((cos_object_t *)pcd, "write_calrgb_color_space");
339
0
        cos_free((cos_object_t *)WP, "write_calrgb_color_space");
340
0
        return_error(gs_error_VMerror);
341
0
    }
342
343
0
    code = cos_dict_put_c_key(pcd, "/BlackPoint", COS_OBJECT_VALUE(&v, BP));
344
0
    if (code < 0)
345
0
        goto error;
346
347
0
    code = cos_dict_put_c_key(pcd, "/WhitePoint",  COS_OBJECT_VALUE(&v, WP));
348
0
    if (code < 0)
349
0
        goto error;
350
351
0
    code = cos_dict_put_c_key(pcd, "/Gamma", COS_OBJECT_VALUE(&v, Gamma));
352
0
    if (code < 0)
353
0
        goto error;
354
355
0
    code = cos_dict_put_c_key(pcd, "/Matrix", COS_OBJECT_VALUE(&v, Matrix));
356
0
    if (code < 0)
357
0
        goto error;
358
359
0
    code = cos_array_add_c_string(pca, "/CalRGB");
360
0
    if (code < 0)
361
0
        goto error;
362
363
0
    code = cos_array_add(pca,  COS_OBJECT_VALUE(&v, pcd));
364
0
    if (code < 0)
365
0
        goto error;
366
367
368
0
    return 0;
369
370
0
error:
371
0
    cos_free((cos_object_t *)pcd, "write_calrgb_color_space");
372
0
    cos_free((cos_object_t *)WP, "write_calrgb_color_space");
373
0
    cos_free((cos_object_t *)BP, "write_calrgb_color_space");
374
0
    cos_free((cos_object_t *)Gamma, "write_calrgb_color_space");
375
0
    cos_free((cos_object_t *)Matrix, "write_calrgb_color_space");
376
0
    return code;
377
0
}
378
379
static int put_lab_color_space(gx_device_pdf *pdev, const gs_gstate * pgs, const gs_color_space *pcs, cos_array_t *pca)
380
0
{
381
0
    int code, i;
382
0
    cos_value_t v;
383
0
    cos_dict_t *pcd;
384
0
    cos_array_t *WP = NULL, *BP = NULL, *range = NULL;
385
386
0
    pcd = cos_dict_alloc(pdev, "write_lab_color_space");
387
0
    if (pcd == NULL)
388
0
        return_error(gs_error_VMerror);
389
390
0
    range = cos_array_alloc(pdev, "write_lab_color_space");
391
0
    if (range == NULL){
392
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
393
0
        return_error(gs_error_VMerror);
394
0
    }
395
396
0
    WP = cos_array_from_floats(pdev, pcs->params.lab.WhitePoint, 3,
397
0
                                             "write_lab_color_space");
398
0
    if (WP == NULL) {
399
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
400
0
        cos_free((cos_object_t *)range, "write_lab_color_space");
401
0
        return_error(gs_error_VMerror);
402
0
    }
403
0
    BP = cos_array_from_floats(pdev, pcs->params.lab.BlackPoint, 3,
404
0
                                             "write_lab_color_space");
405
0
    if (BP == NULL) {
406
0
        cos_free((cos_object_t *)pcd, "write_calgray_color_space");
407
0
        cos_free((cos_object_t *)range, "write_lab_color_space");
408
0
        cos_free((cos_object_t *)WP, "write_lab_color_space");
409
0
        return_error(gs_error_VMerror);
410
0
    }
411
412
0
    for (i = 0;i < 4;i++) {
413
0
        code = cos_array_add_real(range, pcs->params.lab.Range[i]);
414
0
        if (code < 0)
415
0
            goto error;
416
0
    }
417
418
0
    code = cos_dict_put_c_key(pcd, "/BlackPoint", COS_OBJECT_VALUE(&v, BP));
419
0
    if (code < 0)
420
0
        goto error;
421
422
0
    code = cos_dict_put_c_key(pcd, "/WhitePoint",  COS_OBJECT_VALUE(&v, WP));
423
0
    if (code < 0)
424
0
        goto error;
425
426
0
    code = cos_dict_put_c_key(pcd, "/Range",  COS_OBJECT_VALUE(&v, range));
427
0
    if (code < 0)
428
0
        goto error;
429
430
0
    code = cos_array_add_c_string(pca, "/Lab");
431
0
    if (code < 0)
432
0
        goto error;
433
434
0
    code = cos_array_add(pca,  COS_OBJECT_VALUE(&v, pcd));
435
0
    if (code < 0)
436
0
        goto error;
437
438
0
    return 0;
439
440
0
error:
441
0
    cos_free((cos_object_t *)pcd, "write_calgray_color_space");
442
0
    cos_free((cos_object_t *)range, "write_lab_color_space");
443
0
    cos_free((cos_object_t *)WP, "write_lab_color_space");
444
0
    cos_free((cos_object_t *)BP, "write_lab_color_space");
445
0
    return code;
446
0
}
447
448
/*
449
 * Create an ICCBased color space object (internal).  The client must write
450
 * the profile data on *ppcstrm.
451
 */
452
static int
453
pdf_make_iccbased(gx_device_pdf *pdev, const gs_gstate * pgs,
454
                  cos_array_t *pca, int ncomps,
455
                  const gs_range *prange /*[4]*/,
456
                  const gs_color_space *pcs,
457
                  cos_stream_t **ppcstrm,
458
                  const gs_range_t **pprange /* if scaling is needed */)
459
460
1.47k
{
461
1.47k
    cos_value_t v;
462
1.47k
    int code;
463
1.47k
    cos_stream_t * pcstrm = 0;
464
1.47k
    cos_array_t * prngca = 0;
465
466
    /* Range values are a bit tricky to check.
467
       For example, CIELAB ICC profiles have
468
       a unique range.  I am not convinced
469
       that a check is needed in the new
470
       color architecture as I am carefull
471
       to get them properly set during
472
       creation of the ICC profile data. */
473
474
    /* ICCBased color spaces are essentially copied to the output. */
475
1.47k
    if ((code = cos_array_add(pca, cos_c_string_value(&v, "/ICCBased"))) < 0)
476
0
        return code;
477
478
    /* Create a stream for the output. */
479
1.47k
    if ((pcstrm = cos_stream_alloc(pdev, "pdf_make_iccbased(stream)")) == 0) {
480
0
        code = gs_note_error(gs_error_VMerror);
481
0
        goto fail;
482
0
    }
483
484
    /* Indicate the number of components. */
485
1.47k
    code = cos_dict_put_c_key_int(cos_stream_dict(pcstrm), "/N", ncomps);
486
1.47k
    if (code < 0)
487
0
        goto fail;
488
489
    /* In the new design there may not be a specified alternate color space */
490
1.47k
    if (pcs->base_space != NULL){
491
492
        /* Output the alternate color space, if necessary. */
493
0
        switch (gs_color_space_get_index(pcs->base_space)) {
494
0
        case gs_color_space_index_DeviceGray:
495
0
        case gs_color_space_index_DeviceRGB:
496
0
        case gs_color_space_index_DeviceCMYK:
497
0
            break;     /* implicit (default) */
498
0
        default:
499
0
            if ((code = pdf_color_space_named(pdev, pgs, &v, NULL, pcs->base_space,
500
0
                                        &pdf_color_space_names, false, NULL, 0, true)) < 0 ||
501
0
                (code = cos_dict_put_c_key(cos_stream_dict(pcstrm), "/Alternate",
502
0
                                           &v)) < 0
503
0
                )
504
0
                goto fail;
505
0
        }
506
507
1.47k
    } else {
508
1.47k
        cos_value_t alt_v;
509
510
1.47k
        if (pcs->ICC_Alternate_space != gs_ICC_Alternate_None) {
511
1.16k
            switch(pcs->ICC_Alternate_space) {
512
436
                case gs_ICC_Alternate_DeviceGray:
513
436
                    cos_c_string_value(&alt_v, base_names.DeviceGray);
514
436
                    break;
515
729
                case gs_ICC_Alternate_DeviceRGB:
516
729
                    cos_c_string_value(&alt_v, base_names.DeviceRGB);
517
729
                    break;
518
0
                case gs_ICC_Alternate_DeviceCMYK:
519
0
                    cos_c_string_value(&alt_v, base_names.DeviceCMYK);
520
0
                    break;
521
0
                case gs_ICC_Alternate_CalGray:
522
0
                    {
523
0
                        pdf_resource_t *pres;
524
0
                        cos_array_t *pca1;
525
526
0
                        code = pdf_alloc_resource(pdev, resourceColorSpace, gs_no_id, &pres, -1);
527
0
                        if (code < 0)
528
0
                            goto fail;
529
0
                        cos_become(pres->object, cos_type_array);
530
0
                        pca1 = (cos_array_t *)pres->object;
531
532
0
                        code = put_calgray_color_space(pdev, pgs, pcs, pca1);
533
0
                        if (code < 0)
534
0
                            goto fail;
535
536
0
                        code = pdf_substitute_resource(pdev, &pres, resourcePattern, NULL, false);
537
0
                        if (code < 0)
538
0
                            return code;
539
0
                        pres->where_used |= pdev->used_mask;
540
0
                        cos_object_value(&alt_v, pres->object);
541
0
                    }
542
0
                    break;
543
0
                case gs_ICC_Alternate_CalRGB:
544
0
                    {
545
0
                        pdf_resource_t *pres;
546
0
                        cos_array_t *pca1;
547
548
0
                        code = pdf_alloc_resource(pdev, resourceColorSpace, gs_no_id, &pres, -1);
549
0
                        if (code < 0)
550
0
                            goto fail;
551
0
                        cos_become(pres->object, cos_type_array);
552
0
                        pca1 = (cos_array_t *)pres->object;
553
554
0
                        code = put_calrgb_color_space(pdev, pgs, pcs, pca1);
555
0
                        if (code < 0)
556
0
                            goto fail;
557
558
0
                        code = pdf_substitute_resource(pdev, &pres, resourceColorSpace, NULL, false);
559
0
                        if (code < 0)
560
0
                            return code;
561
0
                        pres->where_used |= pdev->used_mask;
562
0
                        cos_object_value(&alt_v, pres->object);
563
0
                    }
564
0
                    break;
565
0
                case gs_ICC_Alternate_Lab:
566
0
                    {
567
0
                        pdf_resource_t *pres;
568
0
                        cos_array_t *pca1;
569
570
0
                        code = pdf_alloc_resource(pdev, resourceColorSpace, gs_no_id, &pres, -1);
571
0
                        if (code < 0)
572
0
                            goto fail;
573
0
                        cos_become(pres->object, cos_type_array);
574
0
                        pca1 = (cos_array_t *)pres->object;
575
576
0
                        code = put_lab_color_space(pdev, pgs, pcs, pca1);
577
0
                        if (code < 0)
578
0
                            goto fail;
579
580
0
                        code = pdf_substitute_resource(pdev, &pres, resourceColorSpace, NULL, false);
581
0
                        if (code < 0)
582
0
                            return code;
583
0
                        pres->where_used |= pdev->used_mask;
584
0
                        cos_object_value(&alt_v, pres->object);
585
0
                    }
586
0
                    break;
587
0
                default:
588
0
                    code = gs_error_rangecheck;
589
0
                    goto fail;
590
0
                    break;
591
1.16k
            }
592
1.16k
            code = cos_dict_put_c_key(cos_stream_dict(pcstrm), "/Alternate", &alt_v);
593
1.16k
            if (code < 0)
594
0
                goto fail;
595
1.16k
        } else {
596
308
            if (ncomps != 1 && ncomps != 3 && ncomps != 4) {
597
                /* We can only use a default for Gray, RGB or CMYK. For anything else we need
598
                 * to convert to the base space, we can't legally preserve the ICC profile.
599
                 */
600
0
                code = gs_error_rangecheck;
601
0
                goto fail;
602
0
            }
603
308
        }
604
1.47k
    }
605
606
    /* Wrap up. */
607
1.47k
    if ((code = cos_array_add_object(pca, COS_OBJECT(pcstrm))) < 0)
608
0
        goto fail;
609
1.47k
    *ppcstrm = pcstrm;
610
1.47k
    return code;
611
0
 fail:
612
0
    if (prngca)
613
0
        COS_FREE(prngca, "pdf_make_iccbased(Range)");
614
0
    if (pcstrm)
615
0
        COS_FREE(pcstrm, "pdf_make_iccbased(stream)");
616
0
    return code;
617
1.47k
}
618
/*
619
 * Finish writing the data stream for an ICCBased color space object.
620
 */
621
static int
622
pdf_finish_iccbased(gx_device_pdf *pdev, cos_stream_t *pcstrm)
623
1.47k
{
624
    /*
625
     * The stream must be an indirect object.  Assign an ID, and write the
626
     * object out now.
627
     */
628
1.47k
    pcstrm->id = pdf_obj_ref(pdev);
629
1.47k
    return cos_write_object(COS_OBJECT(pcstrm), pdev, resourceICC);
630
1.47k
}
631
632
/*
633
 * Create an ICCBased color space for a CIEBased space that can't be
634
 * represented directly as a Calxxx or Lab space.
635
 */
636
637
typedef struct profile_table_s profile_table_t;
638
struct profile_table_s {
639
    const char *tag;
640
    const byte *data;
641
    uint length;
642
    uint data_length;   /* may be < length if write != 0 */
643
    int (*write)(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
644
                 const gs_cie_common *pciec);
645
    const void *write_data;
646
    const gs_range_t *ranges;
647
};
648
static profile_table_t *
649
add_table(profile_table_t **ppnt, const char *tag, const byte *data,
650
          uint length)
651
0
{
652
0
    profile_table_t *pnt = (*ppnt)++;
653
654
0
    pnt->tag = tag, pnt->data = data, pnt->length = length;
655
0
    pnt->data_length = length;
656
0
    pnt->write = NULL;
657
    /* write_data not set */
658
0
    pnt->ranges = NULL;
659
0
    return pnt;
660
0
}
661
static void
662
set_uint32(byte bytes[4], uint value)
663
0
{
664
0
    bytes[0] = (byte)(value >> 24);
665
0
    bytes[1] = (byte)(value >> 16);
666
0
    bytes[2] = (byte)(value >> 8);
667
0
    bytes[3] = (byte)value;
668
0
}
669
static void
670
set_XYZ(byte bytes[4], double value)
671
0
{
672
0
    set_uint32(bytes, (uint)(int)(value * 65536));
673
0
}
674
static void
675
add_table_xyz3(profile_table_t **ppnt, const char *tag, byte bytes[20],
676
               const gs_vector3 *pv)
677
0
{
678
0
    memcpy(bytes, "XYZ \000\000\000\000", 8);
679
0
    set_XYZ(bytes + 8, pv->u);
680
0
    set_XYZ(bytes + 12, pv->v);
681
0
    set_XYZ(bytes + 16, pv->w);
682
0
    DISCARD(add_table(ppnt, tag, bytes, 20));
683
0
}
684
static void
685
set_sample16(byte *p, double v)
686
0
{
687
0
    int value = (int)(v * 65535);
688
689
0
    if (value < 0)
690
0
        value = 0;
691
0
    else if (value > 65535)
692
0
        value = 65535;
693
0
    p[0] = (byte)(value >> 8);
694
0
    p[1] = (byte)value;
695
0
}
696
/* Create and write a TRC curve table. */
697
static int write_trc_abc(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
698
static int write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
699
static profile_table_t *
700
add_trc(gx_device_pdf *pdev, profile_table_t **ppnt, const char *tag, byte bytes[12],
701
        const gs_cie_common *pciec, cie_cache_one_step_t one_step)
702
0
{
703
0
    const int count = gx_cie_cache_size;
704
0
    profile_table_t *pnt;
705
706
0
    memcpy(bytes, "curv\000\000\000\000", 8);
707
0
    set_uint32(bytes + 8, count);
708
0
    pnt = add_table(ppnt, tag, bytes, 12);
709
0
    pnt->length += count * 2;
710
0
    pnt->write = (one_step == ONE_STEP_ABC ? write_trc_abc : write_trc_lmn);
711
0
    pnt->write_data = (const gs_cie_abc *)pciec;
712
0
    return pnt;
713
0
}
714
static int
715
rgb_to_index(const profile_table_t *pnt)
716
0
{
717
0
    switch (pnt->tag[0]) {
718
0
    case 'r': return 0;
719
0
    case 'g': return 1;
720
0
    case 'b': default: /* others can't happen */ return 2;
721
0
    }
722
0
}
723
static double
724
cache_arg(int i, int denom, const gs_range_t *range)
725
0
{
726
0
    double arg = i / (double)denom;
727
728
0
    if (range) {
729
        /* Sample over the range [range->rmin .. range->rmax]. */
730
0
        arg = arg * (range->rmax - range->rmin) + range->rmin;
731
0
    }
732
0
    return arg;
733
0
}
734
735
static int
736
write_trc_abc(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
737
              gs_memory_t *ignore_mem, const gs_cie_common *unused)
738
0
{
739
    /* Write the curve table from DecodeABC. */
740
0
    const gs_cie_abc *pabc = pnt->write_data;
741
0
    int ci = rgb_to_index(pnt);
742
0
    gs_cie_abc_proc proc = pabc->DecodeABC.procs[ci];
743
0
    byte samples[gx_cie_cache_size * 2];
744
0
    byte *p = samples;
745
0
    int i;
746
747
0
    for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
748
0
        set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
749
0
                             pabc));
750
0
    return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
751
0
}
752
static int
753
write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
754
              gs_memory_t *ignore_mem, const gs_cie_common *unused)
755
0
{
756
0
    const gs_cie_common *pciec = pnt->write_data;
757
0
    int ci = rgb_to_index(pnt);
758
0
    gs_cie_common_proc proc = pciec->DecodeLMN.procs[ci];
759
0
    byte samples[gx_cie_cache_size * 2];
760
0
    byte *p = samples;
761
0
    int i;
762
763
    /* Write the curve table from DecodeLMN. */
764
0
    for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
765
0
        set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
766
0
                             pciec));
767
0
    return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
768
0
}
769
/* Create and write an a2b0 lookup table. */
770
0
#define NUM_IN_ENTRIES 2  /* assume linear interpolation */
771
0
#define NUM_OUT_ENTRIES 2  /* ibid. */
772
0
#define MAX_CLUT_ENTRIES 2500  /* enough for 7^4 */
773
typedef struct icc_a2b0_s {
774
    byte header[52];
775
    const gs_color_space *pcs;
776
    int num_points;   /* on each axis of LUT */
777
    int count;      /* total # of entries in LUT */
778
} icc_a2b0_t;
779
static int write_a2b0(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
780
                      const gs_cie_common *pciec);
781
static profile_table_t *
782
add_a2b0(profile_table_t **ppnt, icc_a2b0_t *pa2b, int ncomps,
783
         const gs_color_space *pcs)
784
0
{
785
0
    static const byte a2b0_data[sizeof(pa2b->header)] = {
786
0
        'm', 'f', 't', '2',   /* type signature */
787
0
        0, 0, 0, 0,     /* reserved, 0 */
788
0
        0,        /* # of input channels **VARIABLE** */
789
0
        3,        /* # of output channels */
790
0
        0,        /* # of CLUT points **VARIABLE** */
791
0
        0,        /* reserved, padding */
792
0
        0, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /* matrix column 0 */
793
0
        0, 0, 0, 0,  0, 1, 0, 0,  0, 0, 0, 0, /* matrix column 1 */
794
0
        0, 0, 0, 0,  0, 0, 0, 0,  0, 1, 0, 0, /* matrix column 2 */
795
0
        0, NUM_IN_ENTRIES,   /* # of input table entries */
796
0
        0, NUM_OUT_ENTRIES    /* # of output table entries */
797
0
    };
798
0
    int num_points = (int)floor(pow(MAX_CLUT_ENTRIES, 1.0 / ncomps));
799
0
    profile_table_t *pnt;
800
801
0
    num_points = min(num_points, 255);
802
0
    memcpy(pa2b->header, a2b0_data, sizeof(a2b0_data));
803
0
    pa2b->header[8] = ncomps;
804
0
    pa2b->header[10] = num_points;
805
0
    pa2b->pcs = pcs;
806
0
    pa2b->num_points = num_points;
807
0
    pa2b->count = (int)pow(num_points, ncomps);
808
0
    pnt = add_table(ppnt, "A2B0", pa2b->header,
809
0
                    sizeof(pa2b->header) +
810
0
                    ncomps * 2 * NUM_IN_ENTRIES + /* in */
811
0
                    pa2b->count * (3 * 2) + /* clut: XYZ, 16-bit values */
812
0
                    3 * 2 * NUM_OUT_ENTRIES /* out */
813
0
                    );
814
0
    pnt->data_length = sizeof(pa2b->header); /* only write fixed part */
815
0
    pnt->write = write_a2b0;
816
0
    pnt->write_data = pa2b;
817
0
    return pnt;
818
0
}
819
static int
820
write_a2b0(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
821
           gs_memory_t *mem, const gs_cie_common *pciec)
822
0
{
823
0
    const icc_a2b0_t *pa2b = pnt->write_data;
824
0
    const gs_color_space *pcs = pa2b->pcs;
825
0
    int ncomps = pa2b->header[8];
826
0
    int num_points = pa2b->num_points;
827
0
    int i;
828
0
#define MAX_NCOMPS 4    /* CIEBasedDEFG */
829
0
    static const byte v01[MAX_NCOMPS * 2 * 2] = {
830
0
        0,0, 255,255,   0,0, 255,255,   0,0, 255,255,   0,0, 255,255
831
0
    };
832
0
    gs_gstate *pgs;
833
0
    int code;
834
835
    /* Write the input table. */
836
837
0
    if ((code = cos_stream_add_bytes(pdev, pcstrm, v01, ncomps * 4)) < 0
838
0
        )
839
0
        return code;
840
841
    /* Write the lookup table. */
842
843
0
    code = gx_cie_to_xyz_alloc(&pgs, pcs, mem);
844
0
    if (code < 0)
845
0
        return code;
846
0
    for (i = 0; i < pa2b->count; ++i) {
847
0
        double in[MAX_NCOMPS], xyz[3];
848
0
        byte entry[3 * 2];
849
0
        byte *p = entry;
850
0
        int n, j;
851
852
0
        for (n = i, j = ncomps - 1; j >= 0; --j, n /= num_points)
853
0
            in[j] = cache_arg(n % num_points, num_points - 1,
854
0
                              (pnt->ranges ? pnt->ranges + j : NULL));
855
0
        cie_to_xyz(in, xyz, pcs, pgs, pciec);
856
        /*
857
         * NOTE: Due to an obscure provision of the ICC Profile
858
         * specification, values in a2b0 lookup tables do *not* represent
859
         * the range [0 .. 1], but rather the range [0
860
         * .. MAX_ICC_XYZ_VALUE].  This caused us a lot of grief before we
861
         * figured it out!
862
         */
863
0
#define MAX_ICC_XYZ_VALUE (1 + 32767.0/32768)
864
0
        for (j = 0; j < 3; ++j, p += 2)
865
0
            set_sample16(p, xyz[j] / MAX_ICC_XYZ_VALUE);
866
0
#undef MAX_ICC_XYZ_VALUE
867
0
        if ((code = cos_stream_add_bytes(pdev, pcstrm, entry, sizeof(entry))) < 0)
868
0
            break;
869
0
    }
870
0
    gx_cie_to_xyz_free(pgs);
871
0
    if (code < 0)
872
0
        return code;
873
874
    /* Write the output table. */
875
876
0
    return cos_stream_add_bytes(pdev, pcstrm, v01, 3 * 4);
877
0
}
878
879
/* XYZ wp mapping for now.  Will replace later with Bradford or other */
880
static void
881
adjust_wp(const gs_vector3 *color_in, const gs_vector3 *wp_in,
882
          gs_vector3 *color_out, const gs_vector3 *wp_out)
883
0
{
884
0
    color_out->u = color_in->u * wp_out->u / wp_in->u;
885
0
    color_out->v = color_in->v * wp_out->v / wp_in->v;
886
0
    color_out->w = color_in->w * wp_out->w / wp_in->w;
887
0
}
888
889
static int
890
pdf_convert_cie_to_iccbased(gx_device_pdf *pdev, cos_array_t *pca,
891
                            gs_color_space *pcs, const char *dcsname,
892
                            const gs_cie_common *pciec, const gs_range *prange,
893
                            cie_cache_one_step_t one_step,
894
                            const gs_matrix3 *pmat, const gs_range_t **pprange)
895
0
{
896
    /*
897
     * We have two options for creating an ICCBased color space to represent
898
     * a CIEBased space.  For CIEBasedABC spaces using only a single
899
     * Decode step followed by a single Matrix step, we can use [rgb]TRC
900
     * and [rgb]XYZ; for CIEBasedA spaces using only DecodeA, we could use
901
     * kTRC (but don't); otherwise, we must use a mft2 LUT.
902
     */
903
0
    int code;
904
0
    int ncomps = gs_color_space_num_components(pcs);
905
0
    gs_color_space *alt_space;
906
0
    cos_stream_t *pcstrm;
907
0
    gs_vector3 white_d50;
908
0
    gs_vector3 temp_xyz;
909
    /*
910
     * because it requires random access to the output stream
911
     * we construct the ICC profile by hand.
912
     */
913
    /* Header */
914
0
    byte header[128];
915
0
    static const byte header_data[] = {
916
0
        0, 0, 0, 0,     /* profile size **VARIABLE** */
917
0
        0, 0, 0, 0,     /* CMM type signature */
918
0
        0x02, 0x20, 0, 0,   /* profile version number */
919
0
        's', 'c', 'n', 'r',   /* profile class signature */
920
0
        0, 0, 0, 0,     /* data color space **VARIABLE** */
921
0
        'X', 'Y', 'Z', ' ',   /* connection color space */
922
0
        2002 / 256, 2002 % 256, 0, 1, 0, 1, /* date (1/1/2002) */
923
0
        0, 0, 0, 0, 0, 0,   /* time */
924
0
        'a', 'c', 's', 'p',   /* profile file signature */
925
0
        0, 0, 0, 0,     /* primary platform signature */
926
0
        0, 0, 0, 3,     /* profile flags (embedded use only) */
927
0
        0, 0, 0, 0, 0, 0, 0, 0,   /* device manufacturer */
928
0
        0, 0, 0, 0,     /* device model */
929
0
        0, 0, 0, 0, 0, 0, 0, 2    /* device attributes */
930
        /* Remaining fields are zero or variable. */
931
        /* [4] */     /* rendering intent */
932
        /* 3 * [4] */     /* illuminant */
933
0
    };
934
    /* Description */
935
0
#define DESC_LENGTH 5    /* "adhoc" */
936
0
    byte desc[12 + DESC_LENGTH + 1 + 11 + 67];
937
0
    static const byte desc_data[] = {
938
0
        'd', 'e', 's', 'c',   /* type signature */
939
0
        0, 0, 0, 0,     /* reserved, 0 */
940
0
        0, 0, 0, DESC_LENGTH + 1, /* ASCII description length */
941
0
        'a', 'd', 'h', 'o', 'c', 0, /* ASCII description */
942
        /* Remaining fields are zero. */
943
0
    };
944
    /* White point */
945
0
    byte wtpt[20];
946
    /* Copyright (useless, but required by icclib) */
947
0
    static const byte cprt_data[] = {
948
0
        't', 'e', 'x', 't', /* type signature */
949
0
        0, 0, 0, 0,   /* reserved, 0 */
950
0
        'n', 'o', 'n', 'e', 0 /* must be null-terminated (!) */
951
0
    };
952
    /* Lookup table */
953
0
    icc_a2b0_t a2b0;
954
    /* [rgb]TRC */
955
0
    byte rTRC[12], gTRC[12], bTRC[12];
956
    /* [rgb]XYZ */
957
0
    byte rXYZ[20], gXYZ[20], bXYZ[20];
958
    /* Table structures */
959
0
#define MAX_NUM_TABLES 9  /* desc, [rgb]TRC, [rgb]xYZ, wtpt, cprt */
960
0
    profile_table_t tables[MAX_NUM_TABLES];
961
0
    profile_table_t *next_table = tables;
962
963
    /* White point must be D50 */
964
0
    white_d50.u = 0.9642f;
965
0
    white_d50.v = 1.0f;
966
0
    white_d50.w = 0.8249f;
967
968
0
    pdf_cspace_init_Device(pdev->memory, &alt_space, ncomps); /* can't fail */
969
0
    pcs->base_space = alt_space;
970
0
    code = pdf_make_iccbased(pdev, NULL, pca, ncomps, prange, pcs,
971
0
                             &pcstrm, pprange);
972
0
    rc_decrement_cs(alt_space, "pdf_convert_cie_to_iccbased");
973
0
    if (code < 0)
974
0
        return code;
975
976
    /* Fill in most of the header, except for the total size. */
977
978
0
    memset(header, 0, sizeof(header));
979
0
    memcpy(header, header_data, sizeof(header_data));
980
0
    memcpy(header + 16, dcsname, 4);
981
982
    /* Construct the tables. */
983
984
    /* desc */
985
0
    memset(desc, 0, sizeof(desc));
986
0
    memcpy(desc, desc_data, sizeof(desc_data));
987
0
    DISCARD(add_table(&next_table, "desc", desc, sizeof(desc)));
988
989
    /* wtpt. must be D50 */
990
0
    add_table_xyz3(&next_table, "wtpt", wtpt, &white_d50);
991
0
    memcpy(header + 68, wtpt + 8, 12); /* illuminant = white point */
992
993
    /* cprt */
994
    /* (We have no use for this tag, but icclib requires it.) */
995
0
    DISCARD(add_table(&next_table, "cprt", cprt_data, sizeof(cprt_data)));
996
997
    /* Use TRC + XYZ if possible, otherwise AToB. */
998
0
    if ((one_step == ONE_STEP_ABC || one_step == ONE_STEP_LMN) && pmat != 0) {
999
        /* Use TRC + XYZ. */
1000
0
        profile_table_t *tr =
1001
0
            add_trc(pdev, &next_table, "rTRC", rTRC, pciec, one_step);
1002
0
        profile_table_t *tg =
1003
0
            add_trc(pdev, &next_table, "gTRC", gTRC, pciec, one_step);
1004
0
        profile_table_t *tb =
1005
0
            add_trc(pdev, &next_table, "bTRC", bTRC, pciec, one_step);
1006
1007
0
        if (*pprange) {
1008
0
            tr->ranges = *pprange;
1009
0
            tg->ranges = *pprange + 1;
1010
0
            tb->ranges = *pprange + 2;
1011
0
        }
1012
        /* These values need to be adjusted to D50.  Again
1013
           use XYZ wp mapping for now.  Later we will add in
1014
           the bradford stuff */
1015
0
        adjust_wp(&(pmat->cu), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
1016
0
        add_table_xyz3(&next_table, "rXYZ", rXYZ, &temp_xyz);
1017
0
        adjust_wp(&(pmat->cv), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
1018
0
        add_table_xyz3(&next_table, "gXYZ", gXYZ, &temp_xyz);
1019
0
        adjust_wp(&(pmat->cw), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
1020
0
        add_table_xyz3(&next_table, "bXYZ", bXYZ, &temp_xyz);
1021
0
    } else {
1022
        /* General case, use a lookup table. */
1023
        /* AToB (mft2) */
1024
0
        profile_table_t *pnt = add_a2b0(&next_table, &a2b0, ncomps, pcs);
1025
1026
0
        pnt->ranges = *pprange;
1027
0
    }
1028
1029
    /* Write the profile. */
1030
0
    {
1031
0
        byte bytes[4 + MAX_NUM_TABLES * 12];
1032
0
        int num_tables = next_table - tables;
1033
0
        int i;
1034
0
        byte *p;
1035
0
        uint table_size = 4 + num_tables * 12;
1036
0
        uint offset = sizeof(header) + table_size;
1037
1038
0
        set_uint32(bytes, next_table - tables);
1039
0
        for (i = 0, p = bytes + 4; i < num_tables; ++i, p += 12) {
1040
0
            memcpy(p, tables[i].tag, 4);
1041
0
            set_uint32(p + 4, offset);
1042
0
            set_uint32(p + 8, tables[i].length);
1043
0
            offset += round_up(tables[i].length, 4);
1044
0
        }
1045
0
        set_uint32(header, offset);
1046
0
        if ((code = cos_stream_add_bytes(pdev, pcstrm, header, sizeof(header))) < 0 ||
1047
0
            (code = cos_stream_add_bytes(pdev, pcstrm, bytes, table_size)) < 0
1048
0
            )
1049
0
            return code;
1050
0
        for (i = 0; i < num_tables; ++i) {
1051
0
            uint len = tables[i].data_length;
1052
0
            static const byte pad[3] = {0, 0, 0};
1053
1054
0
            if ((code = cos_stream_add_bytes(pdev, pcstrm, tables[i].data, len)) < 0 ||
1055
0
                (tables[i].write != 0 &&
1056
0
                 (code = tables[i].write(pdev, pcstrm, &tables[i], pdev->pdf_memory, pciec)) < 0) ||
1057
0
                (code = cos_stream_add_bytes(pdev, pcstrm, pad,
1058
0
                        -(int)(tables[i].length) & 3)) < 0
1059
0
                )
1060
0
                return code;
1061
0
        }
1062
0
    }
1063
1064
0
    return pdf_finish_iccbased(pdev, pcstrm);
1065
0
}
1066
1067
/* ------ Entry points (from gdevpdfc.c) ------ */
1068
1069
/*
1070
 * Create an ICCBased color space.  This is a single-use procedure,
1071
 * broken out only for readability.
1072
 */
1073
int
1074
pdf_iccbased_color_space(gx_device_pdf *pdev, const gs_gstate * pgs, cos_value_t *pvalue,
1075
                         const gs_color_space *pcs, cos_array_t *pca)
1076
1.47k
{
1077
1.47k
    cos_stream_t * pcstrm;
1078
1.47k
    int code = 0, code1 = 0;
1079
1.47k
    unsigned char major = 0, minor = 0;
1080
1.47k
    bool downgrade_icc = false;
1081
1.47k
    pdf_resource_t *pres = NULL;
1082
1083
    /*
1084
     * This would arise only in a pdf ==> pdf translation, but we
1085
     * should allow for it anyway.
1086
     */
1087
    /* Not all ICC profile types are valid for embedding in a PDF file.
1088
     * The code here duplicates a check in zicc.c, .numicc_components()
1089
     * where we check to see if an embedded profile is valid. Because
1090
     * we could be getting input from other sources, we need to do the same
1091
     * check here. If the profile can't be embedded in PDF, then we
1092
     * return gs_error_rangecheck which will cause pdfwrtie to fall back
1093
     * to the device space. At least the PDF file will be valid and have
1094
     * 'correct' colours.
1095
     */
1096
1.47k
    switch (pcs->cmm_icc_profile_data->data_cs) {
1097
0
        case gsCIEXYZ:
1098
6
        case gsCIELAB:
1099
820
        case gsRGB:
1100
1.47k
        case gsGRAY:
1101
1.47k
        case gsCMYK:
1102
1.47k
            break;
1103
0
        case gsUNDEFINED:
1104
0
        case gsNCHANNEL:
1105
0
        case gsNAMED:
1106
0
            emprintf(pdev->memory, "\n An ICC profile which is not suitable for use in PDF has been identified.\n All colours using this profile will be converted into device space\n instead and the profile will not be used.\n");
1107
0
            return gs_error_rangecheck;
1108
0
            break;
1109
1.47k
    }
1110
1111
1.47k
    code =
1112
1.47k
        pdf_make_iccbased(pdev, pgs, pca, pcs->cmm_icc_profile_data->num_comps,
1113
1.47k
                          pcs->cmm_icc_profile_data->Range.ranges,
1114
1.47k
                          pcs,
1115
1.47k
                          &pcstrm, NULL);
1116
1117
1.47k
    if (code < 0)
1118
0
        return code;
1119
1120
    /* Transfer the buffer data  */
1121
1122
1.47k
    (void)gsicc_getprofilevers(pcs->cmm_icc_profile_data, &major, &minor);
1123
1.47k
    minor = minor >> 4;
1124
1125
    /* Determine whether we need to get the CMS to give us an earlier ICC version
1126
     * of the profile.
1127
     */
1128
1.47k
    if (pdev->CompatibilityLevel < 1.3) {
1129
0
        return_error(gs_error_rangecheck);
1130
1.47k
    } else {
1131
1.47k
        if (pdev->CompatibilityLevel < 1.5) {
1132
0
            if (major > 2)
1133
0
                downgrade_icc = true;
1134
1.47k
        } else {
1135
1.47k
            if (pdev->CompatibilityLevel == 1.5) {
1136
0
                if (major > 4 || minor > 0)
1137
0
                    downgrade_icc = true;
1138
1.47k
            } else {
1139
1.47k
                if (pdev->CompatibilityLevel == 1.6) {
1140
0
                    if (major > 4 || minor > 1)
1141
0
                        downgrade_icc = true;
1142
1.47k
                } else {
1143
1.47k
                    if (major > 4 || minor > 2)
1144
0
                        downgrade_icc = true;
1145
1.47k
                }
1146
1.47k
            }
1147
1.47k
        }
1148
1.47k
    }
1149
1150
1.47k
    if (downgrade_icc) {
1151
0
        byte *v2_buffer;
1152
0
        int size;
1153
1154
0
        if (pgs == NULL)
1155
0
            return (gs_error_undefined);
1156
0
        if (pcs->cmm_icc_profile_data->profile_handle == NULL)
1157
0
            gsicc_initialize_default_profile(pcs->cmm_icc_profile_data);
1158
0
        v2_buffer = gsicc_create_getv2buffer(pgs, pcs->cmm_icc_profile_data, &size);
1159
0
        code = cos_stream_add_bytes(pdev, pcstrm, v2_buffer, size);
1160
1.47k
    }else{
1161
1.47k
        code = cos_stream_add_bytes(pdev, pcstrm, pcs->cmm_icc_profile_data->buffer,
1162
1.47k
        pcs->cmm_icc_profile_data->buffer_size);
1163
1.47k
    }
1164
1165
    /*
1166
     * The stream has been added to the array: However because the stream cos object
1167
     * has an id (it has to be an indirect object), freeing the colour space won't
1168
     * free the ICC profile stream. In order to have the stream freed we must add it to
1169
     * a resource chain; we don't have a resource chain for ICC profiles, so add it to
1170
     * resourceOther instead. This means it will be among the last objects released.
1171
     */
1172
1.47k
    code1 = pdf_alloc_resource(pdev, resourceOther, pcstrm->id, &pres, -1);
1173
1.47k
    if (code1 >= 0) {
1174
1.47k
        COS_FREE(pres->object, "pdf_iccbased_color_space");
1175
1.47k
        pres->object = (cos_object_t *)pcstrm;
1176
1.47k
    }
1177
1178
1.47k
    if (code >= 0)
1179
1.47k
        code = pdf_finish_iccbased(pdev, pcstrm);
1180
1181
1.47k
    return code;
1182
1.47k
}
1183
1184
/* Convert a CIEBased space to Lab or ICCBased. */
1185
int
1186
pdf_convert_cie_space(gx_device_pdf *pdev, cos_array_t *pca,
1187
                      const gs_color_space *pcs, const char *dcsname,
1188
                      const gs_cie_common *pciec, const gs_range *prange,
1189
                      cie_cache_one_step_t one_step, const gs_matrix3 *pmat,
1190
                      const gs_range_t **pprange)
1191
0
{
1192
0
    return (pdev->CompatibilityLevel < 1.3 ?
1193
            /* PDF 1.2 or earlier, use a Lab space. */
1194
0
            pdf_convert_cie_to_lab(pdev, pca, pcs, pciec, prange) :
1195
            /* PDF 1.3 or later, use an ICCBased space. */
1196
0
            pdf_convert_cie_to_iccbased(pdev, pca, (gs_color_space *)pcs, dcsname, pciec, prange,
1197
0
                                        one_step, pmat, pprange)
1198
0
            );
1199
0
}