Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/devices/vector/gdevpdfk.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, 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
/*
242
 * Create an ICCBased color space object (internal).  The client must write
243
 * the profile data on *ppcstrm.
244
 */
245
static int
246
pdf_make_iccbased(gx_device_pdf *pdev, const gs_gstate * pgs,
247
                  cos_array_t *pca, int ncomps,
248
                  const gs_range *prange /*[4]*/,
249
                  const gs_color_space *pcs_alt,
250
                  cos_stream_t **ppcstrm,
251
                  const gs_range_t **pprange /* if scaling is needed */)
252
253
3.25k
{
254
3.25k
    cos_value_t v;
255
3.25k
    int code;
256
3.25k
    cos_stream_t * pcstrm = 0;
257
3.25k
    cos_array_t * prngca = 0;
258
259
    /* Range values are a bit tricky to check.
260
       For example, CIELAB ICC profiles have
261
       a unique range.  I am not convinced
262
       that a check is needed in the new
263
       color architecture as I am carefull
264
       to get them properly set during
265
       creation of the ICC profile data. */
266
267
    /* ICCBased color spaces are essentially copied to the output. */
268
3.25k
    if ((code = cos_array_add(pca, cos_c_string_value(&v, "/ICCBased"))) < 0)
269
0
        return code;
270
271
    /* Create a stream for the output. */
272
3.25k
    if ((pcstrm = cos_stream_alloc(pdev, "pdf_make_iccbased(stream)")) == 0) {
273
0
        code = gs_note_error(gs_error_VMerror);
274
0
        goto fail;
275
0
    }
276
277
    /* Indicate the number of components. */
278
3.25k
    code = cos_dict_put_c_key_int(cos_stream_dict(pcstrm), "/N", ncomps);
279
3.25k
    if (code < 0)
280
0
        goto fail;
281
282
    /* In the new design there may not be a specified alternate color space */
283
3.25k
    if (pcs_alt != NULL){
284
285
        /* Output the alternate color space, if necessary. */
286
0
        switch (gs_color_space_get_index(pcs_alt)) {
287
0
        case gs_color_space_index_DeviceGray:
288
0
        case gs_color_space_index_DeviceRGB:
289
0
        case gs_color_space_index_DeviceCMYK:
290
0
            break;     /* implicit (default) */
291
0
        default:
292
0
            if ((code = pdf_color_space_named(pdev, pgs, &v, NULL, pcs_alt,
293
0
                                        &pdf_color_space_names, false, NULL, 0, true)) < 0 ||
294
0
                (code = cos_dict_put_c_key(cos_stream_dict(pcstrm), "/Alternate",
295
0
                                           &v)) < 0
296
0
                )
297
0
                goto fail;
298
0
        }
299
300
3.25k
    } else {
301
3.25k
        if (ncomps != 1 && ncomps != 3 && ncomps != 4) {
302
            /* We can only use a default for Gray, RGB or CMYK. For anything else we need
303
             * to convert to the base space, we can't legally preserve the ICC profile.
304
             */
305
0
            code = gs_error_rangecheck;
306
0
            goto fail;
307
0
        }
308
3.25k
    }
309
310
    /* Wrap up. */
311
3.25k
    if ((code = cos_array_add_object(pca, COS_OBJECT(pcstrm))) < 0)
312
0
        goto fail;
313
3.25k
    *ppcstrm = pcstrm;
314
3.25k
    return code;
315
0
 fail:
316
0
    if (prngca)
317
0
        COS_FREE(prngca, "pdf_make_iccbased(Range)");
318
0
    if (pcstrm)
319
0
        COS_FREE(pcstrm, "pdf_make_iccbased(stream)");
320
0
    return code;
321
3.25k
}
322
/*
323
 * Finish writing the data stream for an ICCBased color space object.
324
 */
325
static int
326
pdf_finish_iccbased(gx_device_pdf *pdev, cos_stream_t *pcstrm)
327
3.25k
{
328
    /*
329
     * The stream must be an indirect object.  Assign an ID, and write the
330
     * object out now.
331
     */
332
3.25k
    pcstrm->id = pdf_obj_ref(pdev);
333
3.25k
    return cos_write_object(COS_OBJECT(pcstrm), pdev, resourceICC);
334
3.25k
}
335
336
/*
337
 * Create an ICCBased color space for a CIEBased space that can't be
338
 * represented directly as a Calxxx or Lab space.
339
 */
340
341
typedef struct profile_table_s profile_table_t;
342
struct profile_table_s {
343
    const char *tag;
344
    const byte *data;
345
    uint length;
346
    uint data_length;   /* may be < length if write != 0 */
347
    int (*write)(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
348
                 const gs_cie_common *pciec);
349
    const void *write_data;
350
    const gs_range_t *ranges;
351
};
352
static profile_table_t *
353
add_table(profile_table_t **ppnt, const char *tag, const byte *data,
354
          uint length)
355
0
{
356
0
    profile_table_t *pnt = (*ppnt)++;
357
358
0
    pnt->tag = tag, pnt->data = data, pnt->length = length;
359
0
    pnt->data_length = length;
360
0
    pnt->write = NULL;
361
    /* write_data not set */
362
0
    pnt->ranges = NULL;
363
0
    return pnt;
364
0
}
365
static void
366
set_uint32(byte bytes[4], uint value)
367
0
{
368
0
    bytes[0] = (byte)(value >> 24);
369
0
    bytes[1] = (byte)(value >> 16);
370
0
    bytes[2] = (byte)(value >> 8);
371
0
    bytes[3] = (byte)value;
372
0
}
373
static void
374
set_XYZ(byte bytes[4], double value)
375
0
{
376
0
    set_uint32(bytes, (uint)(int)(value * 65536));
377
0
}
378
static void
379
add_table_xyz3(profile_table_t **ppnt, const char *tag, byte bytes[20],
380
               const gs_vector3 *pv)
381
0
{
382
0
    memcpy(bytes, "XYZ \000\000\000\000", 8);
383
0
    set_XYZ(bytes + 8, pv->u);
384
0
    set_XYZ(bytes + 12, pv->v);
385
0
    set_XYZ(bytes + 16, pv->w);
386
0
    DISCARD(add_table(ppnt, tag, bytes, 20));
387
0
}
388
static void
389
set_sample16(byte *p, double v)
390
0
{
391
0
    int value = (int)(v * 65535);
392
393
0
    if (value < 0)
394
0
        value = 0;
395
0
    else if (value > 65535)
396
0
        value = 65535;
397
0
    p[0] = (byte)(value >> 8);
398
0
    p[1] = (byte)value;
399
0
}
400
/* Create and write a TRC curve table. */
401
static int write_trc_abc(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
402
static int write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
403
static profile_table_t *
404
add_trc(gx_device_pdf *pdev, profile_table_t **ppnt, const char *tag, byte bytes[12],
405
        const gs_cie_common *pciec, cie_cache_one_step_t one_step)
406
0
{
407
0
    const int count = gx_cie_cache_size;
408
0
    profile_table_t *pnt;
409
410
0
    memcpy(bytes, "curv\000\000\000\000", 8);
411
0
    set_uint32(bytes + 8, count);
412
0
    pnt = add_table(ppnt, tag, bytes, 12);
413
0
    pnt->length += count * 2;
414
0
    pnt->write = (one_step == ONE_STEP_ABC ? write_trc_abc : write_trc_lmn);
415
0
    pnt->write_data = (const gs_cie_abc *)pciec;
416
0
    return pnt;
417
0
}
418
static int
419
rgb_to_index(const profile_table_t *pnt)
420
0
{
421
0
    switch (pnt->tag[0]) {
422
0
    case 'r': return 0;
423
0
    case 'g': return 1;
424
0
    case 'b': default: /* others can't happen */ return 2;
425
0
    }
426
0
}
427
static double
428
cache_arg(int i, int denom, const gs_range_t *range)
429
0
{
430
0
    double arg = i / (double)denom;
431
432
0
    if (range) {
433
        /* Sample over the range [range->rmin .. range->rmax]. */
434
0
        arg = arg * (range->rmax - range->rmin) + range->rmin;
435
0
    }
436
0
    return arg;
437
0
}
438
439
static int
440
write_trc_abc(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
441
              gs_memory_t *ignore_mem, const gs_cie_common *unused)
442
0
{
443
    /* Write the curve table from DecodeABC. */
444
0
    const gs_cie_abc *pabc = pnt->write_data;
445
0
    int ci = rgb_to_index(pnt);
446
0
    gs_cie_abc_proc proc = pabc->DecodeABC.procs[ci];
447
0
    byte samples[gx_cie_cache_size * 2];
448
0
    byte *p = samples;
449
0
    int i;
450
451
0
    for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
452
0
        set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
453
0
                             pabc));
454
0
    return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
455
0
}
456
static int
457
write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
458
              gs_memory_t *ignore_mem, const gs_cie_common *unused)
459
0
{
460
0
    const gs_cie_common *pciec = pnt->write_data;
461
0
    int ci = rgb_to_index(pnt);
462
0
    gs_cie_common_proc proc = pciec->DecodeLMN.procs[ci];
463
0
    byte samples[gx_cie_cache_size * 2];
464
0
    byte *p = samples;
465
0
    int i;
466
467
    /* Write the curve table from DecodeLMN. */
468
0
    for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
469
0
        set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
470
0
                             pciec));
471
0
    return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
472
0
}
473
/* Create and write an a2b0 lookup table. */
474
0
#define NUM_IN_ENTRIES 2  /* assume linear interpolation */
475
0
#define NUM_OUT_ENTRIES 2  /* ibid. */
476
0
#define MAX_CLUT_ENTRIES 2500  /* enough for 7^4 */
477
typedef struct icc_a2b0_s {
478
    byte header[52];
479
    const gs_color_space *pcs;
480
    int num_points;   /* on each axis of LUT */
481
    int count;      /* total # of entries in LUT */
482
} icc_a2b0_t;
483
static int write_a2b0(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
484
                      const gs_cie_common *pciec);
485
static profile_table_t *
486
add_a2b0(profile_table_t **ppnt, icc_a2b0_t *pa2b, int ncomps,
487
         const gs_color_space *pcs)
488
0
{
489
0
    static const byte a2b0_data[sizeof(pa2b->header)] = {
490
0
        'm', 'f', 't', '2',   /* type signature */
491
0
        0, 0, 0, 0,     /* reserved, 0 */
492
0
        0,        /* # of input channels **VARIABLE** */
493
0
        3,        /* # of output channels */
494
0
        0,        /* # of CLUT points **VARIABLE** */
495
0
        0,        /* reserved, padding */
496
0
        0, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /* matrix column 0 */
497
0
        0, 0, 0, 0,  0, 1, 0, 0,  0, 0, 0, 0, /* matrix column 1 */
498
0
        0, 0, 0, 0,  0, 0, 0, 0,  0, 1, 0, 0, /* matrix column 2 */
499
0
        0, NUM_IN_ENTRIES,   /* # of input table entries */
500
        0, NUM_OUT_ENTRIES    /* # of output table entries */
501
0
    };
502
0
    int num_points = (int)floor(pow(MAX_CLUT_ENTRIES, 1.0 / ncomps));
503
0
    profile_table_t *pnt;
504
505
0
    num_points = min(num_points, 255);
506
0
    memcpy(pa2b->header, a2b0_data, sizeof(a2b0_data));
507
0
    pa2b->header[8] = ncomps;
508
0
    pa2b->header[10] = num_points;
509
0
    pa2b->pcs = pcs;
510
0
    pa2b->num_points = num_points;
511
0
    pa2b->count = (int)pow(num_points, ncomps);
512
0
    pnt = add_table(ppnt, "A2B0", pa2b->header,
513
0
                    sizeof(pa2b->header) +
514
0
                    ncomps * 2 * NUM_IN_ENTRIES + /* in */
515
0
                    pa2b->count * (3 * 2) + /* clut: XYZ, 16-bit values */
516
                    3 * 2 * NUM_OUT_ENTRIES /* out */
517
0
                    );
518
0
    pnt->data_length = sizeof(pa2b->header); /* only write fixed part */
519
0
    pnt->write = write_a2b0;
520
0
    pnt->write_data = pa2b;
521
0
    return pnt;
522
0
}
523
static int
524
write_a2b0(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
525
           gs_memory_t *mem, const gs_cie_common *pciec)
526
0
{
527
0
    const icc_a2b0_t *pa2b = pnt->write_data;
528
0
    const gs_color_space *pcs = pa2b->pcs;
529
0
    int ncomps = pa2b->header[8];
530
0
    int num_points = pa2b->num_points;
531
0
    int i;
532
0
#define MAX_NCOMPS 4    /* CIEBasedDEFG */
533
0
    static const byte v01[MAX_NCOMPS * 2 * 2] = {
534
0
        0,0, 255,255,   0,0, 255,255,   0,0, 255,255,   0,0, 255,255
535
0
    };
536
0
    gs_gstate *pgs;
537
0
    int code;
538
539
    /* Write the input table. */
540
541
0
    if ((code = cos_stream_add_bytes(pdev, pcstrm, v01, ncomps * 4)) < 0
542
0
        )
543
0
        return code;
544
545
    /* Write the lookup table. */
546
547
0
    code = gx_cie_to_xyz_alloc(&pgs, pcs, mem);
548
0
    if (code < 0)
549
0
        return code;
550
0
    for (i = 0; i < pa2b->count; ++i) {
551
0
        double in[MAX_NCOMPS], xyz[3];
552
0
        byte entry[3 * 2];
553
0
        byte *p = entry;
554
0
        int n, j;
555
556
0
        for (n = i, j = ncomps - 1; j >= 0; --j, n /= num_points)
557
0
            in[j] = cache_arg(n % num_points, num_points - 1,
558
0
                              (pnt->ranges ? pnt->ranges + j : NULL));
559
0
        cie_to_xyz(in, xyz, pcs, pgs, pciec);
560
        /*
561
         * NOTE: Due to an obscure provision of the ICC Profile
562
         * specification, values in a2b0 lookup tables do *not* represent
563
         * the range [0 .. 1], but rather the range [0
564
         * .. MAX_ICC_XYZ_VALUE].  This caused us a lot of grief before we
565
         * figured it out!
566
         */
567
0
#define MAX_ICC_XYZ_VALUE (1 + 32767.0/32768)
568
0
        for (j = 0; j < 3; ++j, p += 2)
569
0
            set_sample16(p, xyz[j] / MAX_ICC_XYZ_VALUE);
570
0
#undef MAX_ICC_XYZ_VALUE
571
0
        if ((code = cos_stream_add_bytes(pdev, pcstrm, entry, sizeof(entry))) < 0)
572
0
            break;
573
0
    }
574
0
    gx_cie_to_xyz_free(pgs);
575
0
    if (code < 0)
576
0
        return code;
577
578
    /* Write the output table. */
579
580
0
    return cos_stream_add_bytes(pdev, pcstrm, v01, 3 * 4);
581
0
}
582
583
/* XYZ wp mapping for now.  Will replace later with Bradford or other */
584
static void
585
adjust_wp(const gs_vector3 *color_in, const gs_vector3 *wp_in,
586
          gs_vector3 *color_out, const gs_vector3 *wp_out)
587
0
{
588
0
    color_out->u = color_in->u * wp_out->u / wp_in->u;
589
0
    color_out->v = color_in->v * wp_out->v / wp_in->v;
590
0
    color_out->w = color_in->w * wp_out->w / wp_in->w;
591
0
}
592
593
static int
594
pdf_convert_cie_to_iccbased(gx_device_pdf *pdev, cos_array_t *pca,
595
                            const gs_color_space *pcs, const char *dcsname,
596
                            const gs_cie_common *pciec, const gs_range *prange,
597
                            cie_cache_one_step_t one_step,
598
                            const gs_matrix3 *pmat, const gs_range_t **pprange)
599
0
{
600
    /*
601
     * We have two options for creating an ICCBased color space to represent
602
     * a CIEBased space.  For CIEBasedABC spaces using only a single
603
     * Decode step followed by a single Matrix step, we can use [rgb]TRC
604
     * and [rgb]XYZ; for CIEBasedA spaces using only DecodeA, we could use
605
     * kTRC (but don't); otherwise, we must use a mft2 LUT.
606
     */
607
0
    int code;
608
0
    int ncomps = gs_color_space_num_components(pcs);
609
0
    gs_color_space *alt_space;
610
0
    cos_stream_t *pcstrm;
611
0
    gs_vector3 white_d50;
612
0
    gs_vector3 temp_xyz;
613
    /*
614
     * because it requires random access to the output stream
615
     * we construct the ICC profile by hand.
616
     */
617
    /* Header */
618
0
    byte header[128];
619
0
    static const byte header_data[] = {
620
0
        0, 0, 0, 0,     /* profile size **VARIABLE** */
621
0
        0, 0, 0, 0,     /* CMM type signature */
622
0
        0x02, 0x20, 0, 0,   /* profile version number */
623
0
        's', 'c', 'n', 'r',   /* profile class signature */
624
0
        0, 0, 0, 0,     /* data color space **VARIABLE** */
625
0
        'X', 'Y', 'Z', ' ',   /* connection color space */
626
0
        2002 / 256, 2002 % 256, 0, 1, 0, 1, /* date (1/1/2002) */
627
0
        0, 0, 0, 0, 0, 0,   /* time */
628
0
        'a', 'c', 's', 'p',   /* profile file signature */
629
0
        0, 0, 0, 0,     /* primary platform signature */
630
0
        0, 0, 0, 3,     /* profile flags (embedded use only) */
631
0
        0, 0, 0, 0, 0, 0, 0, 0,   /* device manufacturer */
632
0
        0, 0, 0, 0,     /* device model */
633
0
        0, 0, 0, 0, 0, 0, 0, 2    /* device attributes */
634
        /* Remaining fields are zero or variable. */
635
        /* [4] */     /* rendering intent */
636
        /* 3 * [4] */     /* illuminant */
637
0
    };
638
    /* Description */
639
0
#define DESC_LENGTH 5    /* "adhoc" */
640
0
    byte desc[12 + DESC_LENGTH + 1 + 11 + 67];
641
0
    static const byte desc_data[] = {
642
0
        'd', 'e', 's', 'c',   /* type signature */
643
0
        0, 0, 0, 0,     /* reserved, 0 */
644
0
        0, 0, 0, DESC_LENGTH + 1, /* ASCII description length */
645
0
        'a', 'd', 'h', 'o', 'c', 0, /* ASCII description */
646
        /* Remaining fields are zero. */
647
0
    };
648
    /* White point */
649
0
    byte wtpt[20];
650
    /* Copyright (useless, but required by icclib) */
651
0
    static const byte cprt_data[] = {
652
0
        't', 'e', 'x', 't', /* type signature */
653
0
        0, 0, 0, 0,   /* reserved, 0 */
654
0
        'n', 'o', 'n', 'e', 0 /* must be null-terminated (!) */
655
0
    };
656
    /* Lookup table */
657
0
    icc_a2b0_t a2b0;
658
    /* [rgb]TRC */
659
0
    byte rTRC[12], gTRC[12], bTRC[12];
660
    /* [rgb]XYZ */
661
0
    byte rXYZ[20], gXYZ[20], bXYZ[20];
662
    /* Table structures */
663
0
#define MAX_NUM_TABLES 9  /* desc, [rgb]TRC, [rgb]xYZ, wtpt, cprt */
664
0
    profile_table_t tables[MAX_NUM_TABLES];
665
0
    profile_table_t *next_table = tables;
666
667
    /* White point must be D50 */
668
0
    white_d50.u = 0.9642f;
669
0
    white_d50.v = 1.0f;
670
0
    white_d50.w = 0.8249f;
671
672
0
    pdf_cspace_init_Device(pdev->memory, &alt_space, ncomps); /* can't fail */
673
0
    code = pdf_make_iccbased(pdev, NULL, pca, ncomps, prange, alt_space,
674
0
                             &pcstrm, pprange);
675
0
    rc_decrement_cs(alt_space, "pdf_convert_cie_to_iccbased");
676
0
    if (code < 0)
677
0
        return code;
678
679
    /* Fill in most of the header, except for the total size. */
680
681
0
    memset(header, 0, sizeof(header));
682
0
    memcpy(header, header_data, sizeof(header_data));
683
0
    memcpy(header + 16, dcsname, 4);
684
685
    /* Construct the tables. */
686
687
    /* desc */
688
0
    memset(desc, 0, sizeof(desc));
689
0
    memcpy(desc, desc_data, sizeof(desc_data));
690
0
    DISCARD(add_table(&next_table, "desc", desc, sizeof(desc)));
691
692
    /* wtpt. must be D50 */
693
0
    add_table_xyz3(&next_table, "wtpt", wtpt, &white_d50);
694
0
    memcpy(header + 68, wtpt + 8, 12); /* illuminant = white point */
695
696
    /* cprt */
697
    /* (We have no use for this tag, but icclib requires it.) */
698
0
    DISCARD(add_table(&next_table, "cprt", cprt_data, sizeof(cprt_data)));
699
700
    /* Use TRC + XYZ if possible, otherwise AToB. */
701
0
    if ((one_step == ONE_STEP_ABC || one_step == ONE_STEP_LMN) && pmat != 0) {
702
        /* Use TRC + XYZ. */
703
0
        profile_table_t *tr =
704
0
            add_trc(pdev, &next_table, "rTRC", rTRC, pciec, one_step);
705
0
        profile_table_t *tg =
706
0
            add_trc(pdev, &next_table, "gTRC", gTRC, pciec, one_step);
707
0
        profile_table_t *tb =
708
0
            add_trc(pdev, &next_table, "bTRC", bTRC, pciec, one_step);
709
710
0
        if (*pprange) {
711
0
            tr->ranges = *pprange;
712
0
            tg->ranges = *pprange + 1;
713
0
            tb->ranges = *pprange + 2;
714
0
        }
715
        /* These values need to be adjusted to D50.  Again
716
           use XYZ wp mapping for now.  Later we will add in
717
           the bradford stuff */
718
0
        adjust_wp(&(pmat->cu), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
719
0
        add_table_xyz3(&next_table, "rXYZ", rXYZ, &temp_xyz);
720
0
        adjust_wp(&(pmat->cv), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
721
0
        add_table_xyz3(&next_table, "gXYZ", gXYZ, &temp_xyz);
722
0
        adjust_wp(&(pmat->cw), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
723
0
        add_table_xyz3(&next_table, "bXYZ", bXYZ, &temp_xyz);
724
0
    } else {
725
        /* General case, use a lookup table. */
726
        /* AToB (mft2) */
727
0
        profile_table_t *pnt = add_a2b0(&next_table, &a2b0, ncomps, pcs);
728
729
0
        pnt->ranges = *pprange;
730
0
    }
731
732
    /* Write the profile. */
733
0
    {
734
0
        byte bytes[4 + MAX_NUM_TABLES * 12];
735
0
        int num_tables = next_table - tables;
736
0
        int i;
737
0
        byte *p;
738
0
        uint table_size = 4 + num_tables * 12;
739
0
        uint offset = sizeof(header) + table_size;
740
741
0
        set_uint32(bytes, next_table - tables);
742
0
        for (i = 0, p = bytes + 4; i < num_tables; ++i, p += 12) {
743
0
            memcpy(p, tables[i].tag, 4);
744
0
            set_uint32(p + 4, offset);
745
0
            set_uint32(p + 8, tables[i].length);
746
0
            offset += round_up(tables[i].length, 4);
747
0
        }
748
0
        set_uint32(header, offset);
749
0
        if ((code = cos_stream_add_bytes(pdev, pcstrm, header, sizeof(header))) < 0 ||
750
0
            (code = cos_stream_add_bytes(pdev, pcstrm, bytes, table_size)) < 0
751
0
            )
752
0
            return code;
753
0
        for (i = 0; i < num_tables; ++i) {
754
0
            uint len = tables[i].data_length;
755
0
            static const byte pad[3] = {0, 0, 0};
756
757
0
            if ((code = cos_stream_add_bytes(pdev, pcstrm, tables[i].data, len)) < 0 ||
758
0
                (tables[i].write != 0 &&
759
0
                 (code = tables[i].write(pdev, pcstrm, &tables[i], pdev->pdf_memory, pciec)) < 0) ||
760
0
                (code = cos_stream_add_bytes(pdev, pcstrm, pad,
761
0
                        -(int)(tables[i].length) & 3)) < 0
762
0
                )
763
0
                return code;
764
0
        }
765
0
    }
766
767
0
    return pdf_finish_iccbased(pdev, pcstrm);
768
0
}
769
770
/* ------ Entry points (from gdevpdfc.c) ------ */
771
772
/*
773
 * Create an ICCBased color space.  This is a single-use procedure,
774
 * broken out only for readability.
775
 */
776
int
777
pdf_iccbased_color_space(gx_device_pdf *pdev, const gs_gstate * pgs, cos_value_t *pvalue,
778
                         const gs_color_space *pcs, cos_array_t *pca)
779
3.25k
{
780
3.25k
    cos_stream_t * pcstrm;
781
3.25k
    int code = 0, code1 = 0;
782
3.25k
    unsigned char major = 0, minor = 0;
783
3.25k
    bool downgrade_icc = false;
784
3.25k
    pdf_resource_t *pres = NULL;
785
786
    /*
787
     * This would arise only in a pdf ==> pdf translation, but we
788
     * should allow for it anyway.
789
     */
790
    /* Not all ICC profile types are valid for embedding in a PDF file.
791
     * The code here duplicates a check in zicc.c, .numicc_components()
792
     * where we check to see if an embedded profile is valid. Because
793
     * we could be getting input from other sources, we need to do the same
794
     * check here. If the profile can't be embedded in PDF, then we
795
     * return gs_error_rangecheck which will cause pdfwrtie to fall back
796
     * to the device space. At least the PDF file will be valid and have
797
     * 'correct' colours.
798
     */
799
3.25k
    switch (pcs->cmm_icc_profile_data->data_cs) {
800
0
        case gsCIEXYZ:
801
154
        case gsCIELAB:
802
2.28k
        case gsRGB:
803
3.25k
        case gsGRAY:
804
3.25k
        case gsCMYK:
805
3.25k
            break;
806
0
        case gsUNDEFINED:
807
0
        case gsNCHANNEL:
808
0
        case gsNAMED:
809
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");
810
0
            return gs_error_rangecheck;
811
0
            break;
812
3.25k
    }
813
814
3.25k
    code =
815
3.25k
        pdf_make_iccbased(pdev, pgs, pca, pcs->cmm_icc_profile_data->num_comps,
816
3.25k
                          pcs->cmm_icc_profile_data->Range.ranges,
817
3.25k
                          pcs->base_space,
818
3.25k
                          &pcstrm, NULL);
819
820
3.25k
    if (code < 0)
821
0
        return code;
822
823
    /* Transfer the buffer data  */
824
825
3.25k
    (void)gsicc_getprofilevers(pcs->cmm_icc_profile_data, &major, &minor);
826
3.25k
    minor = minor >> 4;
827
828
    /* Determine whether we need to get the CMS to give us an earlier ICC version
829
     * of the profile.
830
     */
831
3.25k
    if (pdev->CompatibilityLevel < 1.3) {
832
0
        return_error(gs_error_rangecheck);
833
3.25k
    } else {
834
3.25k
        if (pdev->CompatibilityLevel < 1.5) {
835
0
            if (major > 2)
836
0
                downgrade_icc = true;
837
3.25k
        } else {
838
3.25k
            if (pdev->CompatibilityLevel == 1.5) {
839
0
                if (major > 4 || minor > 0)
840
0
                    downgrade_icc = true;
841
3.25k
            } else {
842
3.25k
                if (pdev->CompatibilityLevel == 1.6) {
843
0
                    if (major > 4 || minor > 1)
844
0
                        downgrade_icc = true;
845
3.25k
                } else {
846
3.25k
                    if (major > 4 || minor > 2)
847
1
                        downgrade_icc = true;
848
3.25k
                }
849
3.25k
            }
850
3.25k
        }
851
3.25k
    }
852
853
3.25k
    if (downgrade_icc) {
854
1
        byte *v2_buffer;
855
1
        int size;
856
857
1
        if (pgs == NULL)
858
0
            return (gs_error_undefined);
859
1
        if (pcs->cmm_icc_profile_data->profile_handle == NULL)
860
0
            gsicc_initialize_default_profile(pcs->cmm_icc_profile_data);
861
1
        v2_buffer = gsicc_create_getv2buffer(pgs, pcs->cmm_icc_profile_data, &size);
862
1
        code = cos_stream_add_bytes(pdev, pcstrm, v2_buffer, size);
863
3.25k
    }else{
864
3.25k
        code = cos_stream_add_bytes(pdev, pcstrm, pcs->cmm_icc_profile_data->buffer,
865
3.25k
        pcs->cmm_icc_profile_data->buffer_size);
866
3.25k
    }
867
868
    /*
869
     * The stream has been added to the array: However because the stream cos object
870
     * has an id (it has to be an indirect object), freeing the colour space won't
871
     * free the ICC profile stream. In order to have the stream freed we must add it to
872
     * a resource chain; we don't have a resource chain for ICC profiles, so add it to
873
     * resourceOther instead. This means it will be among the last objects released.
874
     */
875
3.25k
    code1 = pdf_alloc_resource(pdev, resourceOther, pcstrm->id, &pres, -1);
876
3.25k
    if (code1 >= 0) {
877
3.25k
        COS_FREE(pres->object, "pdf_iccbased_color_space");
878
3.25k
        pres->object = (cos_object_t *)pcstrm;
879
3.25k
    }
880
881
3.25k
    if (code >= 0)
882
3.25k
        code = pdf_finish_iccbased(pdev, pcstrm);
883
884
3.25k
    return code;
885
3.25k
}
886
887
/* Convert a CIEBased space to Lab or ICCBased. */
888
int
889
pdf_convert_cie_space(gx_device_pdf *pdev, cos_array_t *pca,
890
                      const gs_color_space *pcs, const char *dcsname,
891
                      const gs_cie_common *pciec, const gs_range *prange,
892
                      cie_cache_one_step_t one_step, const gs_matrix3 *pmat,
893
                      const gs_range_t **pprange)
894
0
{
895
0
    return (pdev->CompatibilityLevel < 1.3 ?
896
            /* PDF 1.2 or earlier, use a Lab space. */
897
0
            pdf_convert_cie_to_lab(pdev, pca, pcs, pciec, prange) :
898
            /* PDF 1.3 or later, use an ICCBased space. */
899
0
            pdf_convert_cie_to_iccbased(pdev, pca, pcs, dcsname, pciec, prange,
900
0
                                        one_step, pmat, pprange)
901
0
            );
902
0
}