diff options
Diffstat (limited to 'devices/vector/gdevpdfc.c')
-rw-r--r-- | devices/vector/gdevpdfc.c | 1542 |
1 files changed, 1542 insertions, 0 deletions
diff --git a/devices/vector/gdevpdfc.c b/devices/vector/gdevpdfc.c new file mode 100644 index 000000000..c32ac5595 --- /dev/null +++ b/devices/vector/gdevpdfc.c @@ -0,0 +1,1542 @@ +/* Copyright (C) 2001-2012 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael, + CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Color space management and writing for pdfwrite driver */ +#include "math_.h" +#include "memory_.h" +#include "gx.h" +#include "gscspace.h" /* for gscie.h */ +#include "gscdevn.h" +#include "gscie.h" +#include "gscindex.h" +#include "gscsepr.h" +#include "stream.h" +#include "gsicc.h" +#include "gserrors.h" +#include "gsfunc.h" /* required for colour space function evaluation */ +#include "gsfunc3.h" /* Required to create a replacement linear interpolation function */ +#include "gsfunc0.h" /* Required to create a sampled function for DeviceN alternate replacement */ +#include "gdevpdfx.h" +#include "gdevpdfg.h" +#include "gdevpdfc.h" +#include "gdevpdfo.h" +#include "strimpl.h" +#include "sstring.h" +#include "gxcspace.h" +#include "gxcdevn.h" +#include "gscspace.h" +#include "gsicc_manage.h" +#include "gsicc_cache.h" + +/* + * PDF doesn't have general CIEBased color spaces. However, it provides + * two methods for handling general CIE spaces: + * + * - For PDF 1.2 and above, we note that the transformation from L*a*b* + * space to XYZ space is invertible, so we can handle any PostScript + * CIEBased space by transforming color values in that space to XYZ, + * then inverse-transforming them to L*a*b* and using a PDF Lab space + * with the same WhitePoint and BlackPoint and appropriate ranges for + * a and b. This approach has the drawback that Y values outside the + * range [0..1] can't be represented: we just clamp them. + * + * - For PDF 1.3 and above, we can create an ICCBased space. This is + * actually necessary, not just an option, because for shadings (also + * introduced in PDF 1.3), we want color interpolation to occur in the + * original space. + * + * The Lab approach is not currently implemented, because it requires + * transforming all the sample values of images. The ICCBased approach is + * implemented for color spaces whose ranges lie within [0..1], which are + * the only ranges supported by the ICC standard: we think that removing + * this limitation would also require transforming image sample values. + */ + +/* GC descriptors */ +public_st_pdf_color_space(); + +/* ------ CIE space testing ------ */ + +/* Test whether a cached CIE procedure is the identity function. */ +#define CIE_CACHE_IS_IDENTITY(pc)\ + ((pc)->floats.params.is_identity) +#define CIE_CACHE3_IS_IDENTITY(pca)\ + (CIE_CACHE_IS_IDENTITY(&(pca)[0]) &&\ + CIE_CACHE_IS_IDENTITY(&(pca)[1]) &&\ + CIE_CACHE_IS_IDENTITY(&(pca)[2])) + +/* + * Test whether a cached CIE procedure is an exponential. A cached + * procedure is exponential iff f(x) = k*(x^p). We make a very cursory + * check for this: we require that f(0) = 0, set k = f(1), set p = + * log[a](f(a)/k), and then require that f(b) = k*(b^p), where a and b are + * two arbitrarily chosen values between 0 and 1. Naturally all this is + * done with some slop. + */ +#define CC_INDEX_A (gx_cie_cache_size / 3) +#define CC_INDEX_B (gx_cie_cache_size * 2 / 3) +#define CC_INDEX_1 (gx_cie_cache_size - 1) +#define CC_KEY(i) ((i) / (double)CC_INDEX_1) +#define CC_KEY_A CC_KEY(CC_INDEX_A) +#define CC_KEY_B CC_KEY(CC_INDEX_B) + +static bool +cie_values_are_exponential(double v0, double va, double vb, double k, + float *pexpt) +{ + double p; + + if (fabs(v0) >= 0.001 || fabs(k) < 0.001) + return false; + if (va == 0 || (va > 0) != (k > 0)) + return false; + p = log(va / k) / log(CC_KEY_A); + if (fabs(vb - k * pow(CC_KEY_B, p)) >= 0.001) + return false; + *pexpt = p; + return true; +} + +static bool +cie_scalar_cache_is_exponential(const gx_cie_scalar_cache * pc, float *pexpt) +{ + return cie_values_are_exponential(pc->floats.values[0], + pc->floats.values[CC_INDEX_A], + pc->floats.values[CC_INDEX_B], + pc->floats.values[CC_INDEX_1], + pexpt); +} +#define CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pca, expts)\ + (cie_scalar_cache_is_exponential(&(pca)[0], &(expts).u) &&\ + cie_scalar_cache_is_exponential(&(pca)[1], &(expts).v) &&\ + cie_scalar_cache_is_exponential(&(pca)[2], &(expts).w)) + +static bool +cie_vector_cache_is_exponential(const gx_cie_vector_cache * pc, float *pexpt) +{ + return cie_values_are_exponential(pc->vecs.values[0].u, + pc->vecs.values[CC_INDEX_A].u, + pc->vecs.values[CC_INDEX_B].u, + pc->vecs.values[CC_INDEX_1].u, + pexpt); +} +#define CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pca, expts)\ + (cie_vector_cache_is_exponential(&(pca)[0], &(expts).u) &&\ + cie_vector_cache_is_exponential(&(pca)[1], &(expts).v) &&\ + cie_vector_cache_is_exponential(&(pca)[2], &(expts).w)) + +#undef CC_INDEX_A +#undef CC_INDEX_B +#undef CC_KEY_A +#undef CC_KEY_B + +/* + * Test whether a cached CIEBasedABC space consists only of a single + * Decode step followed by a single Matrix step. + */ +static cie_cache_one_step_t +cie_cached_abc_is_one_step(const gs_cie_abc *pcie, const gs_matrix3 **ppmat) +{ + /* The order of steps is, DecodeABC, MatrixABC, DecodeLMN, MatrixLMN. */ + + if (CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN)) { + if (pcie->MatrixABC.is_identity) { + *ppmat = &pcie->common.MatrixLMN; + return ONE_STEP_ABC; + } + if (pcie->common.MatrixLMN.is_identity) { + *ppmat = &pcie->MatrixABC; + return ONE_STEP_ABC; + } + } + if (CIE_CACHE3_IS_IDENTITY(pcie->caches.DecodeABC.caches)) { + if (pcie->MatrixABC.is_identity) { + *ppmat = &pcie->common.MatrixLMN; + return ONE_STEP_LMN; + } + } + return ONE_STEP_NOT; +} + +/* + * Test whether a cached CIEBasedABC space is a L*a*b* space. + */ +static bool +cie_scalar_cache_is_lab_lmn(const gs_cie_abc *pcie, int i) +{ + double k = CC_KEY(i); + double g = (k >= 6.0 / 29 ? k * k * k : + (k - 4.0 / 29) * (108.0 / 841)); + +#define CC_V(j,i) (pcie->common.caches.DecodeLMN[j].floats.values[i]) +#define CC_WP(uvw) (pcie->common.points.WhitePoint.uvw) + + return (fabs(CC_V(0, i) - g * CC_WP(u)) < 0.001 && + fabs(CC_V(1, i) - g * CC_WP(v)) < 0.001 && + fabs(CC_V(2, i) - g * CC_WP(w)) < 0.001 + ); + +#undef CC_V +#undef CC_WP +} +static bool +cie_vector_cache_is_lab_abc(const gx_cie_vector_cache3_t *pvc, int i) +{ + const gx_cie_vector_cache *const pc3 = pvc->caches; + double k = CC_KEY(i); + double l0 = pc3[0].vecs.params.base, + l = l0 + k * (pc3[0].vecs.params.limit - l0); + double a0 = pc3[1].vecs.params.base, + a = a0 + k * (pc3[1].vecs.params.limit - a0); + double b0 = pc3[2].vecs.params.base, + b = b0 + k * (pc3[2].vecs.params.limit - b0); + + return (fabs(cie_cached2float(pc3[0].vecs.values[i].u) - + (l + 16) / 116) < 0.001 && + fabs(cie_cached2float(pc3[1].vecs.values[i].u) - + a / 500) < 0.001 && + fabs(cie_cached2float(pc3[2].vecs.values[i].w) - + b / -200) < 0.001 + ); +} + +static bool +cie_is_lab(const gs_cie_abc *pcie) +{ + int i; + + /* Check MatrixABC and MatrixLMN. */ + if (!(pcie->MatrixABC.cu.u == 1 && pcie->MatrixABC.cu.v == 1 && + pcie->MatrixABC.cu.w == 1 && + pcie->MatrixABC.cv.u == 1 && pcie->MatrixABC.cv.v == 0 && + pcie->MatrixABC.cv.w == 0 && + pcie->MatrixABC.cw.u == 0 && pcie->MatrixABC.cw.v == 0 && + pcie->MatrixABC.cw.w == -1 && + pcie->common.MatrixLMN.is_identity + )) + return false; + + /* Check DecodeABC and DecodeLMN. */ + for (i = 0; i <= CC_INDEX_1; ++i) + if (!(cie_vector_cache_is_lab_abc(&pcie->caches.DecodeABC, i) && + cie_scalar_cache_is_lab_lmn(pcie, i) + )) + return false; + + return true; +} + +#undef CC_INDEX_1 +#undef CC_KEY + +/* Test whether one or more CIE-based ranges are [0..1]. */ +static bool +cie_ranges_are_0_1(const gs_range *prange, int n) +{ + int i; + + for (i = 0; i < n; ++i) + if (prange[i].rmin != 0 || prange[i].rmax != 1) + return false; + return true; +} + +/* ------ Utilities ------ */ + +/* Add a 3-element vector to a Cos array or dictionary. */ +static int +cos_array_add_vector3(cos_array_t *pca, const gs_vector3 *pvec) +{ + int code = cos_array_add_real(pca, pvec->u); + + if (code >= 0) + code = cos_array_add_real(pca, pvec->v); + if (code >= 0) + code = cos_array_add_real(pca, pvec->w); + return code; +} +static int +cos_dict_put_c_key_vector3(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, + const gs_vector3 *pvec) +{ + cos_array_t *pca = cos_array_alloc(pdev, "cos_array_from_vector3"); + int code; + + if (pca == 0) + return_error(gs_error_VMerror); + code = cos_array_add_vector3(pca, pvec); + if (code < 0) { + COS_FREE(pca, "cos_array_from_vector3"); + return code; + } + return cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca)); +} + +/* + * Finish creating a CIE-based color space (Calxxx or Lab.) + * This procedure is exported for gdevpdfk.c. + */ +int +pdf_finish_cie_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd, + const gs_cie_common *pciec) +{ + int code = cos_dict_put_c_key_vector3(pdev, pcd, "/WhitePoint", + &pciec->points.WhitePoint); + + if (code < 0) + return code; + if (pciec->points.BlackPoint.u != 0 || + pciec->points.BlackPoint.v != 0 || + pciec->points.BlackPoint.w != 0 + ) { + code = cos_dict_put_c_key_vector3(pdev, pcd, "/BlackPoint", + &pciec->points.BlackPoint); + if (code < 0) + return code; + } + return cos_array_add_object(pca, COS_OBJECT(pcd)); +} + +/* ------ Color space writing ------ */ + +/* Define standard and short color space names. */ +const pdf_color_space_names_t pdf_color_space_names = { + PDF_COLOR_SPACE_NAMES +}; +const pdf_color_space_names_t pdf_color_space_names_short = { + PDF_COLOR_SPACE_NAMES_SHORT +}; + +/* + * Create a local Device{Gray,RGB,CMYK} color space corresponding to the + * given number of components. + */ +int +pdf_cspace_init_Device(gs_memory_t *mem, gs_color_space **ppcs, + int num_components) +{ + switch (num_components) { + case 1: *ppcs = gs_cspace_new_DeviceGray(mem); break; + case 3: *ppcs = gs_cspace_new_DeviceRGB(mem); break; + case 4: *ppcs = gs_cspace_new_DeviceCMYK(mem); break; + default: return_error(gs_error_rangecheck); + } + return 0; +} + +int pdf_delete_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn) +{ + gs_function_Sd_params_t *params = (gs_function_Sd_params_t *)&pfn->params; + + gs_free_object(pdev->memory, (void *)params->Domain, "pdf_delete_function"); + gs_free_object(pdev->memory, (void *)params->Range, "pdf_delete_function"); + gs_free_string(pdev->memory, (void *)params->DataSource.data.str.data, params->DataSource.data.str.size, "pdf_dselete_function"); + gs_free_object(pdev->memory, (void *)pfn, "pdf_delete_function"); + return 0; +} + +int pdf_delete_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn) +{ + gs_function_ElIn_params_t *params = (gs_function_ElIn_params_t *)&pfn->params; + + gs_free_object(pdev->memory, (void *)params->Domain, "pdf_delete_function"); + gs_free_object(pdev->memory, (void *)params->Range, "pdf_delete_function"); + gs_free_object(pdev->memory, (void *)params->C0, "pdf_delete_function"); + gs_free_object(pdev->memory, (void *)params->C1, "pdf_delete_function"); + gs_free_object(pdev->memory, (void *)pfn, "pdf_delete_function"); + return 0; +} + +int pdf_make_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn, + int nSrcComp, int nDstComp, byte *data) +{ + gs_function_Sd_params_t params; + void *ptr1, *ptr2; + int i, code; + gs_const_string str; + + str.data = gs_alloc_string(pdev->memory, nDstComp * nSrcComp * 2 * sizeof(float), "pdf_DeviceN"); + str.size = nDstComp * (uint)pow(2, nSrcComp); + memcpy((void *)str.data, data, str.size); + + params.m = nSrcComp; + params.n = nDstComp; + params.Order = 1; + params.BitsPerSample = 8; + + ptr1 = gs_alloc_byte_array(pdev->memory, nSrcComp, sizeof(int), "pdf_make_function(Domain)"); + for (i=0;i<nSrcComp;i++) { + ((int *)ptr1)[i] = 2; + } + params.Size = (const int *)ptr1; + + ptr1 = (float *) + gs_alloc_byte_array(pdev->memory, 2 * nSrcComp, sizeof(float), "pdf_make_function(Domain)"); + if (ptr1 == 0) { + return gs_note_error(gs_error_VMerror); + } + ptr2 = (float *) + gs_alloc_byte_array(pdev->memory, 2 * nDstComp, sizeof(float), "pdf_make_function(Range)"); + if (ptr2 == 0) { + gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(Range)"); + return gs_note_error(gs_error_VMerror); + } + for (i=0;i<nSrcComp;i++) { + ((float *)ptr1)[i*2] = 0.0f; + ((float *)ptr1)[(i*2) + 1] = 1.0f; + } + for (i=0;i<nDstComp;i++) { + ((float *)ptr2)[i*2] = 0.0f; + ((float *)ptr2)[(i*2) + 1] = 1.0f; + } + params.Domain = ptr1; + params.Range = ptr2; + params.Encode = params.Decode = NULL; + data_source_init_string(¶ms.DataSource, str); + + code = gs_function_Sd_init(pfn, ¶ms, pdev->memory); + return code; +} + +int pdf_make_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn, + int ncomp, float *data_low, float *data_high) +{ + gs_function_ElIn_params_t params; + float *ptr1, *ptr2; + int i, code; + + ptr1 = (float *) + gs_alloc_byte_array(pdev->memory, 2, sizeof(float), "pdf_make_function(Domain)"); + if (ptr1 == 0) { + return gs_note_error(gs_error_VMerror); + } + ptr2 = (float *) + gs_alloc_byte_array(pdev->memory, 2 * ncomp, sizeof(float), "pdf_make_function(Range)"); + if (ptr2 == 0) { + gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(Range)"); + return gs_note_error(gs_error_VMerror); + } + params.m = 1; + params.n = ncomp; + params.N = 1.0f; + ptr1[0] = 0.0f; + ptr1[1] = 1.0f; + for (i=0;i<ncomp;i++) { + ptr2[i*2] = 0.0f; + ptr2[(i*2) + 1] = 1.0f; + } + params.Domain = ptr1; + params.Range = ptr2; + + ptr1 = (float *)gs_alloc_byte_array(pdev->memory, ncomp, sizeof(float), "pdf_make_function(C0)"); + if (ptr1 == 0) { + gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function(C0)"); + gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function(C0)"); + return gs_note_error(gs_error_VMerror); + } + ptr2 = (float *)gs_alloc_byte_array(pdev->memory, ncomp, sizeof(float), "pdf_make_function(C1)"); + if (ptr2 == 0) { + gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function(C1)"); + gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function(C1)"); + gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(C1)"); + return gs_note_error(gs_error_VMerror); + } + + for (i=0;i<ncomp;i++) { + ptr1[i] = data_low[i]; + ptr2[i] = data_high[i]; + } + params.C0 = ptr1; + params.C1 = ptr2; + code = gs_function_ElIn_init(pfn, ¶ms, pdev->memory); + if (code < 0) { + gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function"); + gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function"); + gs_free_object(pdev->memory, (void *)params.C0, "pdf_make_function"); + gs_free_object(pdev->memory, (void *)params.C1, "pdf_make_function"); + } + return code; +} + +static void pdf_SepRGB_ConvertToCMYK (float *in, float *out) +{ + float CMYK[4]; + int i; + + if (in[0] <= in[1] && in[0] <= in[2]) { + CMYK[3] = 1.0 - in[0]; + } else { + if (in[1]<= in[0] && in[1] <= in[2]) { + CMYK[3] = 1.0 - in[1]; + } else { + CMYK[3] = 1.0 - in[2]; + } + } + CMYK[0] = 1.0 - in[0] - CMYK[3]; + CMYK[1] = 1.0 - in[1] - CMYK[3]; + CMYK[2] = 1.0 - in[2] - CMYK[3]; + for (i=0;i<4;i++) + out[i] = CMYK[i]; +} + +static void pdf_SepCMYK_ConvertToRGB (float *in, float *out) +{ + float RGB[3]; + + RGB[0] = in[0] + in[3]; + RGB[1] = in[1] + in[3]; + RGB[2] = in[2] + in[3]; + + if (RGB[0] > 1) + out[0] = 0.0f; + else + out[0] = 1 - RGB[0]; + if (RGB[1] > 1) + out[1] = 0.0f; + else + out[1] = 1 - RGB[1]; + if (RGB[2] > 1) + out[2] = 0.0f; + else + out[2] = 1 - RGB[2]; +} + +/* Create a Separation or DeviceN color space (internal). */ +static int +pdf_separation_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, + cos_array_t *pca, const char *csname, + const cos_value_t *snames, + const gs_color_space *alt_space, + const gs_function_t *pfn, + const pdf_color_space_names_t *pcsn, + const cos_value_t *v_attributes) +{ + cos_value_t v; + const gs_range_t *ranges; + int code, csi; + + /* We need to think about the alternate space. If we are producing + * PDF/X or PDF/A we can't produce some device spaces, and the code in + * pdf_color_space_named always allows device spaces. We could alter + * that code, but by then we don't know its an Alternate space, and have + * lost the tin transform procedure. So instead we check here. + */ + csi = gs_color_space_get_index(alt_space); + /* Note that if csi is ICC, check to see if this was one of + the default substitutes that we introduced for DeviceGray, + DeviceRGB or DeviceCMYK. If it is, then just write + the default color. Depending upon the flavor of PDF, + or other options, we may want to actually have all + the colors defined by ICC profiles and not do the following + substituion of the Device space. */ + if (csi == gs_color_space_index_ICC) { + csi = gsicc_get_default_type(alt_space->cmm_icc_profile_data); + } + if (csi == gs_color_space_index_DeviceRGB && (pdev->PDFX || + (pdev->PDFA != 0 && (pdev->pcm_color_info_index == gs_color_space_index_DeviceCMYK)))) { + + /* We have a DeviceRGB alternate, but are producing either PDF/X or + * PDF/A with a DeviceCMYK process color model. So we need to convert + * the alternate space into CMYK. We do this by evaluating the function + * at each end of the Separation space (0 and 1), convert the resulting + * RGB colours into CMYK and create a new function which linearly + * interpolates between these points. + */ + gs_function_t *new_pfn = 0; + float in[1] = {0.0f}; + float out_low[4]; + float out_high[4]; + + code = gs_function_evaluate(pfn, in, out_low); + if (code < 0) + return code; + pdf_SepRGB_ConvertToCMYK((float *)&out_low, (float *)&out_low); + + in[0] = 1.0f; + code = gs_function_evaluate(pfn, in, out_high); + if (code < 0) + return code; + pdf_SepRGB_ConvertToCMYK((float *)&out_high, (float *)&out_high); + + code = pdf_make_base_space_function(pdev, &new_pfn, 4, out_low, out_high); + if (code < 0) + return code; + + code = cos_array_add(pca, cos_c_string_value(&v, csname)); + if (code >= 0) { + code = cos_array_add_no_copy(pca, snames); + if (code >= 0) { + cos_c_string_value(&v, (const char *)pcsn->DeviceCMYK); + code = cos_array_add(pca, &v); + if (code >= 0) { + code = pdf_function_scaled(pdev, new_pfn, 0x00, &v); + if (code >= 0) { + code = cos_array_add(pca, &v); + if (code >= 0 && v_attributes != NULL) + code = cos_array_add(pca, v_attributes); + } + } + } + } + pdf_delete_base_space_function(pdev, new_pfn); + return code; + } + if (csi == gs_color_space_index_DeviceCMYK && + (pdev->PDFA != 0 && (pdev->pcm_color_info_index == gs_color_space_index_DeviceRGB))) { + /* We have a DeviceCMYK alternate, but are producingPDF/A with a + * DeviceRGB process color model. See comment above re DviceRGB. + */ + gs_function_t *new_pfn = 0; + float in[1] = {0.0f}; + float out_low[4]; + float out_high[4]; + + code = gs_function_evaluate(pfn, in, out_low); + if (code < 0) + return code; + pdf_SepCMYK_ConvertToRGB((float *)&out_low, (float *)&out_low); + + in[0] = 1.0f; + code = gs_function_evaluate(pfn, in, out_high); + if (code < 0) + return code; + pdf_SepCMYK_ConvertToRGB((float *)&out_high, (float *)&out_high); + + code = pdf_make_base_space_function(pdev, &new_pfn, 3, out_low, out_high); + if (code < 0) + return code; + + code = cos_array_add(pca, cos_c_string_value(&v, csname)); + if (code >= 0) { + code = cos_array_add_no_copy(pca, snames); + if (code >= 0) { + cos_c_string_value(&v, pcsn->DeviceRGB); + code = cos_array_add(pca, &v); + if (code >= 0) { + code = pdf_function_scaled(pdev, new_pfn, 0x00, &v); + if (code >= 0) { + code = cos_array_add(pca, &v); + if (code >= 0 && v_attributes != NULL) + code = cos_array_add(pca, v_attributes); + } + } + } + } + pdf_delete_base_space_function(pdev, new_pfn); + return code; + } + + if ((code = cos_array_add(pca, cos_c_string_value(&v, csname))) < 0 || + (code = cos_array_add_no_copy(pca, snames)) < 0 || + (code = pdf_color_space_named(pdev, pis, &v, &ranges, alt_space, pcsn, false, NULL, 0, false)) < 0 || + (code = cos_array_add(pca, &v)) < 0 || + (code = pdf_function_scaled(pdev, pfn, ranges, &v)) < 0 || + (code = cos_array_add(pca, &v)) < 0 || + (v_attributes != NULL ? code = cos_array_add(pca, v_attributes) : 0) < 0 + ) + return code; + return 0; +} + +/* + * Create an Indexed color space. This is a single-use procedure, + * broken out only for readability. + */ +int +pdf_indexed_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, cos_value_t *pvalue, + const gs_color_space *pcs, cos_array_t *pca, cos_value_t *cos_base) +{ + const gs_indexed_params *pip = &pcs->params.indexed; + const gs_color_space *base_space = pcs->base_space; + int num_entries = pip->hival + 1; + int num_components = gs_color_space_num_components(base_space); + uint table_size = num_entries * num_components; + /* Guess at the extra space needed for PS string encoding. */ + uint string_size = 2 + table_size * 4; + uint string_used; + byte buf[100]; /* arbitrary */ + stream_AXE_state st; + stream s, es; + gs_memory_t *mem = pdev->pdf_memory; + byte *table; + byte *palette; + cos_value_t v; + int code; + + /* PDF doesn't support Indexed color spaces with more than 256 entries. */ + if (num_entries > 256) + return_error(gs_error_rangecheck); + if (pdev->CompatibilityLevel < 1.3 && !pdev->ForOPDFRead) { + switch (gs_color_space_get_index(pcs)) { + case gs_color_space_index_Pattern: + case gs_color_space_index_Separation: + case gs_color_space_index_Indexed: + case gs_color_space_index_DeviceN: + return_error(gs_error_rangecheck); + default: DO_NOTHING; + } + + } + table = gs_alloc_string(mem, string_size, "pdf_color_space(table)"); + palette = gs_alloc_string(mem, table_size, "pdf_color_space(palette)"); + if (table == 0 || palette == 0) { + gs_free_string(mem, palette, table_size, + "pdf_color_space(palette)"); + gs_free_string(mem, table, string_size, + "pdf_color_space(table)"); + return_error(gs_error_VMerror); + } + s_init(&s, mem); + swrite_string(&s, table, string_size); + s_init(&es, mem); + s_init_state((stream_state *)&st, &s_PSSE_template, NULL); + s_init_filter(&es, (stream_state *)&st, buf, sizeof(buf), &s); + sputc(&s, '('); + if (pcs->params.indexed.use_proc) { + gs_client_color cmin, cmax; + byte *pnext = palette; + int i, j; + + /* Find the legal range for the color components. */ + for (j = 0; j < num_components; ++j) + cmin.paint.values[j] = (float)min_long, + cmax.paint.values[j] = (float)max_long; + gs_color_space_restrict_color(&cmin, base_space); + gs_color_space_restrict_color(&cmax, base_space); + /* + * Compute the palette values, with the legal range for each + * one mapped to [0 .. 255]. + */ + for (i = 0; i < num_entries; ++i) { + gs_client_color cc; + + gs_cspace_indexed_lookup(pcs, i, &cc); + for (j = 0; j < num_components; ++j) { + float v = (cc.paint.values[j] - cmin.paint.values[j]) + * 255 / (cmax.paint.values[j] - cmin.paint.values[j]); + + *pnext++ = (v <= 0 ? 0 : v >= 255 ? 255 : (byte)v); + } + } + } else + memcpy(palette, pip->lookup.table.data, table_size); + if (gs_color_space_get_index(base_space) == + gs_color_space_index_DeviceRGB + ) { + /* Check for an all-gray palette3. */ + int i; + + for (i = table_size; (i -= 3) >= 0; ) + if (palette[i] != palette[i + 1] || + palette[i] != palette[i + 2] + ) + break; + if (i < 0) { + /* Change the color space to DeviceGray. */ + for (i = 0; i < num_entries; ++i) + palette[i] = palette[i * 3]; + table_size = num_entries; + base_space = gs_cspace_new_DeviceGray(mem); + } + } + stream_write(&es, palette, table_size); + gs_free_string(mem, palette, table_size, "pdf_color_space(palette)"); + sclose(&es); + sflush(&s); + string_used = (uint)stell(&s); + table = gs_resize_string(mem, table, string_size, string_used, + "pdf_color_space(table)"); + /* + * Since the array is always referenced by name as a resource + * rather than being written as a value, even for in-line images, + * always use the full name for the color space. + * + * We don't have to worry about the range of the base space: + * in PDF, unlike PostScript, the values from the lookup table are + * scaled automatically. + */ + if (pdev->UseOldColor || cos_base == NULL) { + if ((code = pdf_color_space_named(pdev, pis, pvalue, NULL, base_space, + &pdf_color_space_names, false, NULL, 0, false)) < 0 || + (code = cos_array_add(pca, + cos_c_string_value(&v, + pdf_color_space_names.Indexed + /*pcsn->Indexed*/))) < 0 || + (code = cos_array_add(pca, pvalue)) < 0 || + (code = cos_array_add_int(pca, pip->hival)) < 0 || + (code = cos_array_add_no_copy(pca, + cos_string_value(&v, table, + string_used))) < 0 + ) + return code; + } else { + code = cos_array_add(pca, cos_c_string_value(&v, pdf_color_space_names.Indexed)); + code = cos_array_add(pca, cos_base); + code = cos_array_add_int(pca, pip->hival); + code = cos_array_add_no_copy(pca, cos_string_value(&v, table, string_used)); + } + return 0; +} + +/* + * Find a color space resource by seriialized data. + */ +static pdf_resource_t * +pdf_find_cspace_resource(gx_device_pdf *pdev, const byte *serialized, uint serialized_size) +{ + pdf_resource_t **pchain = pdev->resources[resourceColorSpace].chains; + pdf_resource_t *pres; + int i; + + for (i = 0; i < NUM_RESOURCE_CHAINS; i++) { + for (pres = pchain[i]; pres != 0; pres = pres->next) { + const pdf_color_space_t *const ppcs = + (const pdf_color_space_t *)pres; + if (ppcs->serialized_size != serialized_size) + continue; + if (!memcmp(ppcs->serialized, serialized, ppcs->serialized_size)) + return pres; + } + } + return NULL; +} + +int pdf_convert_ICC(gx_device_pdf *pdev, + const gs_color_space *pcs, cos_value_t *pvalue, + const pdf_color_space_names_t *pcsn) +{ + gs_color_space_index csi; + int code; + + csi = gs_color_space_get_index(pcs); + if (csi == gs_color_space_index_ICC) { + csi = gsicc_get_default_type(pcs->cmm_icc_profile_data); + } + if (csi == gs_color_space_index_Indexed) { + pcs = pcs->base_space; + csi = gs_color_space_get_index(pcs); + } + if (csi == gs_color_space_index_ICC) { + if (pcs->cmm_icc_profile_data == NULL || + pdev->CompatibilityLevel < 1.3 + ) { + if (pcs->base_space != NULL) { + return 0; + } else { + int num_des_comps; + cmm_dev_profile_t *dev_profile; + + /* determine number of components in device space */ + code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile); + if (code < 0) + return code; + + num_des_comps = gsicc_get_device_profile_comps(dev_profile); + /* Set image color space to be device space */ + switch( num_des_comps ) { + case 1: + cos_c_string_value(pvalue, pcsn->DeviceGray); + /* negative return means we do conversion */ + return -1; + case 3: + cos_c_string_value(pvalue, pcsn->DeviceRGB); + return -1; + case 4: + cos_c_string_value(pvalue, pcsn->DeviceCMYK); + return -1; + default: + break; + } + } + } + } + return 0; +} + +/* + * Create a PDF color space corresponding to a PostScript color space. + * For parameterless color spaces, set *pvalue to a (literal) string with + * the color space name; for other color spaces, create a cos_array_t if + * necessary and set *pvalue to refer to it. In the latter case, if + * by_name is true, return a string /Rxxxx rather than a reference to + * the actual object. + * + * If ppranges is not NULL, then if the domain of the color space had + * to be scaled (to convert a CIEBased space to ICCBased), store a pointer + * to the ranges in *ppranges, otherwise set *ppranges to 0. + */ +int +pdf_color_space_named(gx_device_pdf *pdev, const gs_imager_state * pis, + cos_value_t *pvalue, + const gs_range_t **ppranges, + const gs_color_space *pcs_in, + const pdf_color_space_names_t *pcsn, + bool by_name, const byte *res_name, int name_length, bool keepICC) +{ + const gs_color_space *pcs; + gs_color_space_index csi; + cos_array_t *pca; + cos_dict_t *pcd; + cos_value_t v; + const gs_cie_common *pciec; + gs_function_t *pfn; + const gs_range_t *ranges = 0; + uint serialized_size = 0; + byte *serialized = NULL, serialized0[100]; + pdf_resource_t *pres = NULL; + int code; + bool is_lab = false; + + /* If color space is CIE based and we have compatibility then go ahead and use the ICC alternative */ + if ((pdev->CompatibilityLevel < 1.3) || !gs_color_space_is_PSCIE(pcs_in) ) { + pcs = pcs_in; + } else { + pcs = pcs_in; + /* The snippet below creates an ICC equivalent profile for the PS + color space. This is disabled until I add the capability to + specify the profile version to ensure compatability with + the PDF versions */ +#ifdef DEPRECATED_906 + if (pcs_in->icc_equivalent != NULL) { + pcs = pcs_in->icc_equivalent; + } else { + /* Need to create the equivalent object */ + gs_colorspace_set_icc_equivalent((gs_color_space *)pcs_in, &islab, pdev->memory); + pcs = pcs_in->icc_equivalent; + } +#endif + } + csi = gs_color_space_get_index(pcs); + /* Note that if csi is ICC, check to see if this was one of + the default substitutes that we introduced for DeviceGray, + DeviceRGB or DeviceCMYK. If it is, then just write + the default color. Depending upon the flavor of PDF, + or other options, we may want to actually have all + the colors defined by ICC profiles and not do the following + substituion of the Device space. */ + if (csi == gs_color_space_index_ICC && !keepICC) { + csi = gsicc_get_default_type(pcs->cmm_icc_profile_data); + } + if (ppranges) + *ppranges = 0; /* default */ + switch (csi) { + case gs_color_space_index_DeviceGray: + cos_c_string_value(pvalue, pcsn->DeviceGray); + return 0; + case gs_color_space_index_DeviceRGB: + cos_c_string_value(pvalue, pcsn->DeviceRGB); + return 0; + case gs_color_space_index_DeviceCMYK: + cos_c_string_value(pvalue, pcsn->DeviceCMYK); + return 0; + case gs_color_space_index_Pattern: + if (!pcs->params.pattern.has_base_space) { + cos_c_string_value(pvalue, "/Pattern"); + return 0; + } + break; + case gs_color_space_index_ICC: + /* + * Take a special early exit for unrecognized ICCBased color spaces, + * or for PDF 1.2 output (ICCBased color spaces date from PDF 1.3). + */ + + if (pcs->cmm_icc_profile_data == NULL || + pdev->CompatibilityLevel < 1.3 + ) { + if (res_name != NULL) + return 0; /* Ignore .includecolorspace */ + if (pcs->base_space != NULL) { + return pdf_color_space_named( pdev, pis, pvalue, ppranges, + pcs->base_space, + pcsn, by_name, NULL, 0, keepICC); + } else { + switch( cs_num_components(pcs) ) { + case 1: + cos_c_string_value(pvalue, pcsn->DeviceGray); + return 0; + case 3: + cos_c_string_value(pvalue, pcsn->DeviceRGB); + return 0; + case 4: + cos_c_string_value(pvalue, pcsn->DeviceCMYK); + return 0; + default: + break; + } + } + } + + break; + default: + break; + } + if (pdev->UseOldColor) { + if (pdev->params.ColorConversionStrategy == ccs_CMYK && + csi != gs_color_space_index_DeviceCMYK && + csi != gs_color_space_index_DeviceGray && + csi != gs_color_space_index_Pattern) { + emprintf(pdev->memory, + "\nUnable to convert color space to CMYK, reverting strategy to LeaveColorUnchanged.\n"); + pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged; + } + if (pdev->params.ColorConversionStrategy == ccs_sRGB && + csi != gs_color_space_index_DeviceRGB && + csi != gs_color_space_index_DeviceGray && + csi != gs_color_space_index_Pattern) { + emprintf(pdev->memory, + "\nUnable to convert color space to sRGB, reverting strategy to LeaveColorUnchanged.\n"); + pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged; + } + if (pdev->params.ColorConversionStrategy == ccs_Gray && + csi != gs_color_space_index_DeviceGray && + csi != gs_color_space_index_Pattern) { + emprintf(pdev->memory, + "\nUnable to convert color space to Gray, reverting strategy to LeaveColorUnchanged.\n"); + pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged; + } + } + /* Check whether we already have a PDF object for this color space. */ + if (pcs->id != gs_no_id) + pres = pdf_find_resource_by_gs_id(pdev, resourceColorSpace, pcs->id); + if (pres == NULL) { + stream s; + + s_init(&s, pdev->memory); + swrite_position_only(&s); + code = cs_serialize(pcs, &s); + if (code < 0) + return_error(gs_error_unregistered); /* Must not happen. */ + serialized_size = stell(&s); + sclose(&s); + if (serialized_size <= sizeof(serialized0)) + serialized = serialized0; + else { + serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space"); + if (serialized == NULL) + return_error(gs_error_VMerror); + } + swrite_string(&s, serialized, serialized_size); + code = cs_serialize(pcs, &s); + if (code < 0) + return_error(gs_error_unregistered); /* Must not happen. */ + if (stell(&s) != serialized_size) + return_error(gs_error_unregistered); /* Must not happen. */ + sclose(&s); + pres = pdf_find_cspace_resource(pdev, serialized, serialized_size); + if (pres != NULL) { + if (serialized != serialized0) + gs_free_object(pdev->pdf_memory, serialized, "pdf_color_space"); + serialized = NULL; + } + } + if (pres) { + const pdf_color_space_t *const ppcs = + (const pdf_color_space_t *)pres; + + if (ppranges != 0 && ppcs->ranges != 0) + *ppranges = ppcs->ranges; + pca = (cos_array_t *)pres->object; + goto ret; + } + + /* Space has parameters -- create an array. */ + pca = cos_array_alloc(pdev, "pdf_color_space"); + if (pca == 0) + return_error(gs_error_VMerror); + + switch (csi) { + + case gs_color_space_index_ICC: + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs, pca); + break; + + case gs_color_space_index_CIEA: { + /* Check that we can represent this as a CalGray space. */ + const gs_cie_a *pcie = pcs->params.a; + bool unitary = cie_ranges_are_0_1(&pcie->RangeA, 1); + bool identityA = (pcie->MatrixA.u == 1 && pcie->MatrixA.v == 1 && + pcie->MatrixA.w == 1); + gs_vector3 expts; + + pciec = (const gs_cie_common *)pcie; + if (!pcie->common.MatrixLMN.is_identity) { + if (!pdev->UseOldColor && !pdev->ForOPDFRead) { + if (pcs->icc_equivalent == 0) { + code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory); + if (code < 0) + return code; + } + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca); + if (pcs->params.a->RangeA.rmin < 0.0 || pcs->params.a->RangeA.rmax > 1.0) + ranges = &pcs->params.a->RangeA; + } else { + code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec, + &pcie->RangeA, ONE_STEP_NOT, NULL, + &ranges); + } + break; + } + if (unitary && identityA && + CIE_CACHE_IS_IDENTITY(&pcie->caches.DecodeA) && + CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts) && + expts.v == expts.u && expts.w == expts.u + ) { + DO_NOTHING; + } else if (unitary && identityA && + CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN) && + cie_vector_cache_is_exponential(&pcie->caches.DecodeA, &expts.u) + ) { + DO_NOTHING; + } else { + if (!pdev->UseOldColor && !pdev->ForOPDFRead) { + if (pcs->icc_equivalent == 0) { + code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory); + if (code < 0) + return code; + } + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca); + if (pcs->params.a->RangeA.rmin < 0.0 || pcs->params.a->RangeA.rmax > 1.0) + ranges = &pcs->params.a->RangeA; + } else { + code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec, + &pcie->RangeA, ONE_STEP_NOT, NULL, + &ranges); + } + break; + } + code = cos_array_add(pca, cos_c_string_value(&v, "/CalGray")); + if (code < 0) + return code; + pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)"); + if (pcd == 0) + return_error(gs_error_VMerror); + if (expts.u != 1) { + code = cos_dict_put_c_key_real(pcd, "/Gamma", expts.u); + if (code < 0) + return code; + } + } + cal: + /* Finish handling a CIE-based color space (Calxxx or Lab). */ + if (code < 0) + return code; + code = pdf_finish_cie_space(pdev, pca, pcd, pciec); + break; + + case gs_color_space_index_CIEABC: { + /* Check that we can represent this as a CalRGB space. */ + const gs_cie_abc *pcie = pcs->params.abc; + bool unitary = cie_ranges_are_0_1(pcie->RangeABC.ranges, 3); + gs_vector3 expts; + const gs_matrix3 *pmat = NULL; + cie_cache_one_step_t one_step = + cie_cached_abc_is_one_step(pcie, &pmat); + + pciec = (const gs_cie_common *)pcie; + if (unitary) { + switch (one_step) { + case ONE_STEP_ABC: + if (CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pcie->caches.DecodeABC.caches, expts)) + goto calrgb; + break; + case ONE_STEP_LMN: + if (CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts)) + goto calrgb; + default: + break; + } + } + if (cie_is_lab(pcie)) { + /* Represent this as a Lab space. */ + pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)"); + if (pcd == 0) + return_error(gs_error_VMerror); + code = pdf_put_lab_color_space(pdev, pca, pcd, pcie->RangeABC.ranges); + goto cal; + } else { + if (!pdev->UseOldColor && !pdev->ForOPDFRead) { + int i; + + if (pcs->icc_equivalent == 0) { + code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory); + if (code < 0) + return code; + } + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca); + for (i = 0; i < 3; ++i) { + double rmin = pcs->params.abc->RangeABC.ranges[i].rmin, rmax = pcs->params.abc->RangeABC.ranges[i].rmax; + + if (rmin < 0.0 || rmax > 1.0) + ranges = pcs->params.abc->RangeABC.ranges; + } + } else { + code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ", pciec, + pcie->RangeABC.ranges, + one_step, pmat, &ranges); + } + break; + } + calrgb: + code = cos_array_add(pca, cos_c_string_value(&v, "/CalRGB")); + if (code < 0) + return code; + pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)"); + if (pcd == 0) + return_error(gs_error_VMerror); + if (expts.u != 1 || expts.v != 1 || expts.w != 1) { + code = cos_dict_put_c_key_vector3(pdev, pcd, "/Gamma", &expts); + if (code < 0) + return code; + } + if (!pmat->is_identity) { + cos_array_t *pcma = + cos_array_alloc(pdev, "pdf_color_space(Matrix)"); + + if (pcma == 0) + return_error(gs_error_VMerror); + if ((code = cos_array_add_vector3(pcma, &pmat->cu)) < 0 || + (code = cos_array_add_vector3(pcma, &pmat->cv)) < 0 || + (code = cos_array_add_vector3(pcma, &pmat->cw)) < 0 || + (code = cos_dict_put(pcd, (const byte *)"/Matrix", 7, + COS_OBJECT_VALUE(&v, pcma))) < 0 + ) + return code; + } + } + goto cal; + + case gs_color_space_index_CIEDEF: + if (!pdev->UseOldColor && !pdev->ForOPDFRead) { + int i; + if (pcs->icc_equivalent == 0) { + code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory); + if (code < 0) + return code; + } + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca); + for (i = 0; i < 3; ++i) { + double rmin = pcs->params.def->RangeDEF.ranges[i].rmin, rmax = pcs->params.def->RangeDEF.ranges[i].rmax; + + if (rmin < 0.0 || rmax > 1.0) + ranges = pcs->params.def->RangeDEF.ranges; + } + } else { + code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ", + (const gs_cie_common *)pcs->params.def, + pcs->params.def->RangeDEF.ranges, + ONE_STEP_NOT, NULL, &ranges); + } + break; + + case gs_color_space_index_CIEDEFG: + if (!pdev->UseOldColor && !pdev->ForOPDFRead) { + int i; + if (pcs->icc_equivalent == 0) { + code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory); + if (code < 0) + return code; + } + code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca); + for (i = 0; i < 4; ++i) { + double rmin = pcs->params.defg->RangeDEFG.ranges[i].rmin, rmax = pcs->params.defg->RangeDEFG.ranges[i].rmax; + + if (rmin < 0.0 || rmax > 1.0) + ranges = pcs->params.defg->RangeDEFG.ranges; + } + } else { + code = pdf_convert_cie_space(pdev, pca, pcs, "CMYK", + (const gs_cie_common *)pcs->params.defg, + pcs->params.defg->RangeDEFG.ranges, + ONE_STEP_NOT, NULL, &ranges); + } + break; + + case gs_color_space_index_Indexed: + code = pdf_indexed_color_space(pdev, pis, pvalue, pcs, pca, NULL); + break; + + case gs_color_space_index_DeviceN: + if (!pdev->PreserveDeviceN) + return_error(gs_error_rangecheck); + if (pdev->CompatibilityLevel < 1.3) + return_error(gs_error_rangecheck); + pfn = gs_cspace_get_devn_function(pcs); + /****** CURRENTLY WE ONLY HANDLE Functions ******/ + if (pfn == 0) + return_error(gs_error_rangecheck); + { + cos_array_t *psna = + cos_array_alloc(pdev, "pdf_color_space(DeviceN)"); + int i; + byte *name_string; + uint name_string_length; + cos_value_t v_attriburtes, *va = NULL; + + if (psna == 0) + return_error(gs_error_VMerror); + for (i = 0; i < pcs->params.device_n.num_components; ++i) { + if ((code = pcs->params.device_n.get_colorname_string( + pdev->memory, + pcs->params.device_n.names[i], &name_string, + &name_string_length)) < 0 || + (code = pdf_string_to_cos_name(pdev, name_string, + name_string_length, &v)) < 0 || + (code = cos_array_add_no_copy(psna, &v)) < 0) + return code; + } + COS_OBJECT_VALUE(&v, psna); + if (pcs->params.device_n.colorants != NULL) { + cos_dict_t *colorants = cos_dict_alloc(pdev, "pdf_color_space(DeviceN)"); + cos_value_t v_colorants, v_separation, v_colorant_name; + const gs_device_n_attributes *csa; + pdf_resource_t *pres_attributes; + + if (colorants == NULL) + return_error(gs_error_VMerror); + code = pdf_alloc_resource(pdev, resourceOther, 0, &pres_attributes, -1); + if (code < 0) + return code; + cos_become(pres_attributes->object, cos_type_dict); + COS_OBJECT_VALUE(&v_colorants, colorants); + code = cos_dict_put((cos_dict_t *)pres_attributes->object, + (const byte *)"/Colorants", 10, &v_colorants); + if (code < 0) + return code; + for (csa = pcs->params.device_n.colorants; csa != NULL; csa = csa->next) { + code = pcs->params.device_n.get_colorname_string(pdev->memory, + csa->colorant_name, &name_string, &name_string_length); + if (code < 0) + return code; + code = pdf_color_space_named(pdev, pis, &v_separation, NULL, csa->cspace, pcsn, false, NULL, 0, keepICC); + if (code < 0) + return code; + code = pdf_string_to_cos_name(pdev, name_string, name_string_length, &v_colorant_name); + if (code < 0) + return code; + code = cos_dict_put(colorants, v_colorant_name.contents.chars.data, + v_colorant_name.contents.chars.size, &v_separation); + if (code < 0) + return code; + } + code = pdf_substitute_resource(pdev, &pres_attributes, resourceOther, NULL, true); + if (code < 0) + return code; + pres_attributes->where_used |= pdev->used_mask; + va = &v_attriburtes; + COS_OBJECT_VALUE(va, pres_attributes->object); + } + if ((code = pdf_separation_color_space(pdev, pis, pca, "/DeviceN", &v, + pcs->base_space, + pfn, &pdf_color_space_names, va)) < 0) + return code; + } + break; + + case gs_color_space_index_Separation: + if (!pdev->PreserveSeparation) + return_error(gs_error_rangecheck); + pfn = gs_cspace_get_sepr_function(pcs); + /****** CURRENTLY WE ONLY HANDLE Functions ******/ + if (pfn == 0) + return_error(gs_error_rangecheck); + { + byte *name_string; + uint name_string_length; + if ((code = pcs->params.separation.get_colorname_string( + pdev->memory, + pcs->params.separation.sep_name, &name_string, + &name_string_length)) < 0 || + (code = pdf_string_to_cos_name(pdev, name_string, + name_string_length, &v)) < 0 || + (code = pdf_separation_color_space(pdev, pis, pca, "/Separation", &v, + pcs->base_space, + pfn, &pdf_color_space_names, NULL)) < 0) + return code; + } + break; + + case gs_color_space_index_Pattern: + if ((code = pdf_color_space_named(pdev, pis, pvalue, ppranges, + pcs->base_space, + &pdf_color_space_names, false, NULL, 0, false)) < 0 || + (code = cos_array_add(pca, + cos_c_string_value(&v, "/Pattern"))) < 0 || + (code = cos_array_add(pca, pvalue)) < 0 + ) + return code; + break; + + default: + return_error(gs_error_rangecheck); + } + + /* + * Register the color space as a resource, since it must be referenced + * by name rather than directly. + */ + { + pdf_color_space_t *ppcs; + + if (code < 0 || + (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id, + &pres, -1)) < 0 + ) { + COS_FREE(pca, "pdf_color_space"); + return code; + } + pdf_reserve_object_id(pdev, pres, 0); + if (res_name != NULL) { + int l = min(name_length, sizeof(pres->rname) - 1); + + memcpy(pres->rname, res_name, l); + pres->rname[l] = 0; + } + ppcs = (pdf_color_space_t *)pres; + if (serialized == serialized0) { + serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space"); + if (serialized == NULL) + return_error(gs_error_VMerror); + memcpy(serialized, serialized0, serialized_size); + } + ppcs->serialized = serialized; + ppcs->serialized_size = serialized_size; + if (ranges) { + int num_comp = gs_color_space_num_components(pcs); + gs_range_t *copy_ranges = (gs_range_t *) + gs_alloc_byte_array(pdev->pdf_memory, num_comp, + sizeof(gs_range_t), "pdf_color_space"); + + if (copy_ranges == 0) { + COS_FREE(pca, "pdf_color_space"); + return_error(gs_error_VMerror); + } + memcpy(copy_ranges, ranges, num_comp * sizeof(gs_range_t)); + ppcs->ranges = copy_ranges; + if (ppranges) + *ppranges = copy_ranges; + } else + ppcs->ranges = 0; + pca->id = pres->object->id; + COS_FREE(pres->object, "pdf_color_space"); + pres->object = (cos_object_t *)pca; + cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace); + } + ret: + if (by_name) { + /* Return a resource name rather than an object reference. */ + discard(COS_RESOURCE_VALUE(pvalue, pca)); + } else + discard(COS_OBJECT_VALUE(pvalue, pca)); + if (pres != NULL) { + pres->where_used |= pdev->used_mask; + code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres); + if (code < 0) + return code; + } + return 0; +} + +int free_color_space(gx_device_pdf *pdev, pdf_resource_t *pres) +{ + pdf_color_space_t *ppcs = (pdf_color_space_t *)pres; + + if (ppcs->serialized) + gs_free_object(pdev->pdf_memory, ppcs->serialized, "free serialized colour space"); + if (pres->object) { + cos_release(pres->object, "release ColorSpace object"); + gs_free_object(pdev->pdf_memory, pres->object, "free ColorSpace object"); + pres->object = 0; + } + return 0; +} + +/* ---------------- Miscellaneous ---------------- */ + +/* Create colored and uncolored Pattern color spaces. */ +static int +pdf_pattern_space(gx_device_pdf *pdev, cos_value_t *pvalue, + pdf_resource_t **ppres, const char *cs_name) +{ + int code; + + if (!*ppres) { + int code = pdf_begin_resource_body(pdev, resourceColorSpace, gs_no_id, + ppres); + + if (code < 0) + return code; + pprints1(pdev->strm, "%s\n", cs_name); + pdf_end_resource(pdev, resourceColorSpace); + (*ppres)->object->written = true; /* don't write at end */ + ((pdf_color_space_t *)*ppres)->ranges = 0; + ((pdf_color_space_t *)*ppres)->serialized = 0; + } + code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", *ppres); + if (code < 0) + return code; + cos_resource_value(pvalue, (*ppres)->object); + return 0; +} +int +pdf_cs_Pattern_colored(gx_device_pdf *pdev, cos_value_t *pvalue) +{ + return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[0], + "[/Pattern]"); +} +int +pdf_cs_Pattern_uncolored(gx_device_pdf *pdev, cos_value_t *pvalue) +{ + /* Only for process colors. */ + int ncomp = pdev->color_info.num_components; + static const char *const pcs_names[5] = { + 0, "[/Pattern /DeviceGray]", 0, "[/Pattern /DeviceRGB]", + "[/Pattern /DeviceCMYK]" + }; + + return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[ncomp], + pcs_names[ncomp]); +} +int +pdf_cs_Pattern_uncolored_hl(gx_device_pdf *pdev, + const gs_color_space *pcs, cos_value_t *pvalue, const gs_imager_state * pis) +{ + /* Only for high level colors. */ + return pdf_color_space_named(pdev, pis, pvalue, NULL, pcs, &pdf_color_space_names, true, NULL, 0, false); +} + +/* Set the ProcSets bits corresponding to an image color space. */ +void +pdf_color_space_procsets(gx_device_pdf *pdev, const gs_color_space *pcs) +{ + const gs_color_space *pbcs = pcs; + + csw: + switch (gs_color_space_get_index(pbcs)) { + case gs_color_space_index_DeviceGray: + case gs_color_space_index_CIEA: + /* We only handle CIEBasedA spaces that map to CalGray. */ + pdev->procsets |= ImageB; + break; + case gs_color_space_index_Indexed: + pdev->procsets |= ImageI; + pbcs = pcs->base_space; + goto csw; + default: + pdev->procsets |= ImageC; + break; + } +} |