xref: /plan9-contrib/sys/src/cmd/gs/src/gdevpdfc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
13ff48bf5SDavid du Colombier /* Copyright (C) 1999, 2000, 2001 Aladdin Enterprises.  All rights reserved.
23ff48bf5SDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
53ff48bf5SDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
93ff48bf5SDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
153ff48bf5SDavid du Colombier */
163ff48bf5SDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gdevpdfc.c,v 1.54 2005/10/18 09:05:58 leonardo Exp $ */
183ff48bf5SDavid du Colombier /* Color space management and writing for pdfwrite driver */
193ff48bf5SDavid du Colombier #include "math_.h"
203ff48bf5SDavid du Colombier #include "memory_.h"
213ff48bf5SDavid du Colombier #include "gx.h"
223ff48bf5SDavid du Colombier #include "gscspace.h"		/* for gscie.h */
233ff48bf5SDavid du Colombier #include "gscdevn.h"
243ff48bf5SDavid du Colombier #include "gscie.h"
253ff48bf5SDavid du Colombier #include "gscindex.h"
263ff48bf5SDavid du Colombier #include "gscsepr.h"
273ff48bf5SDavid du Colombier #include "stream.h"
283ff48bf5SDavid du Colombier #include "gsicc.h"
293ff48bf5SDavid du Colombier #include "gserrors.h"
303ff48bf5SDavid du Colombier #include "gdevpdfx.h"
313ff48bf5SDavid du Colombier #include "gdevpdfg.h"
32*593dc095SDavid du Colombier #include "gdevpdfc.h"
333ff48bf5SDavid du Colombier #include "gdevpdfo.h"
343ff48bf5SDavid du Colombier #include "strimpl.h"
353ff48bf5SDavid du Colombier #include "sstring.h"
36*593dc095SDavid du Colombier #include "gxcspace.h"
37*593dc095SDavid du Colombier #include <assert.h>
38*593dc095SDavid du Colombier 
39*593dc095SDavid du Colombier /*
40*593dc095SDavid du Colombier  * PDF doesn't have general CIEBased color spaces.  However, it provides
41*593dc095SDavid du Colombier  * two methods for handling general CIE spaces:
42*593dc095SDavid du Colombier  *
43*593dc095SDavid du Colombier  *	- For PDF 1.2 and above, we note that the transformation from L*a*b*
44*593dc095SDavid du Colombier  *	space to XYZ space is invertible, so we can handle any PostScript
45*593dc095SDavid du Colombier  *	CIEBased space by transforming color values in that space to XYZ,
46*593dc095SDavid du Colombier  *	then inverse-transforming them to L*a*b* and using a PDF Lab space
47*593dc095SDavid du Colombier  *	with the same WhitePoint and BlackPoint and appropriate ranges for
48*593dc095SDavid du Colombier  *	a and b.  This approach has the drawback that Y values outside the
49*593dc095SDavid du Colombier  *	range [0..1] can't be represented: we just clamp them.
50*593dc095SDavid du Colombier  *
51*593dc095SDavid du Colombier  *	- For PDF 1.3 and above, we can create an ICCBased space.  This is
52*593dc095SDavid du Colombier  *	actually necessary, not just an option, because for shadings (also
53*593dc095SDavid du Colombier  *	introduced in PDF 1.3), we want color interpolation to occur in the
54*593dc095SDavid du Colombier  *	original space.
55*593dc095SDavid du Colombier  *
56*593dc095SDavid du Colombier  * The Lab approach is not currently implemented, because it requires
57*593dc095SDavid du Colombier  * transforming all the sample values of images.  The ICCBased approach is
58*593dc095SDavid du Colombier  * implemented for color spaces whose ranges lie within [0..1], which are
59*593dc095SDavid du Colombier  * the only ranges supported by the ICC standard: we think that removing
60*593dc095SDavid du Colombier  * this limitation would also require transforming image sample values.
61*593dc095SDavid du Colombier  */
62*593dc095SDavid du Colombier 
63*593dc095SDavid du Colombier /* GC descriptors */
64*593dc095SDavid du Colombier public_st_pdf_color_space();
653ff48bf5SDavid du Colombier 
663ff48bf5SDavid du Colombier /* ------ CIE space testing ------ */
673ff48bf5SDavid du Colombier 
683ff48bf5SDavid du Colombier /* Test whether a cached CIE procedure is the identity function. */
693ff48bf5SDavid du Colombier #define CIE_CACHE_IS_IDENTITY(pc)\
703ff48bf5SDavid du Colombier   ((pc)->floats.params.is_identity)
713ff48bf5SDavid du Colombier #define CIE_CACHE3_IS_IDENTITY(pca)\
723ff48bf5SDavid du Colombier   (CIE_CACHE_IS_IDENTITY(&(pca)[0]) &&\
733ff48bf5SDavid du Colombier    CIE_CACHE_IS_IDENTITY(&(pca)[1]) &&\
743ff48bf5SDavid du Colombier    CIE_CACHE_IS_IDENTITY(&(pca)[2]))
753ff48bf5SDavid du Colombier 
763ff48bf5SDavid du Colombier /*
773ff48bf5SDavid du Colombier  * Test whether a cached CIE procedure is an exponential.  A cached
783ff48bf5SDavid du Colombier  * procedure is exponential iff f(x) = k*(x^p).  We make a very cursory
793ff48bf5SDavid du Colombier  * check for this: we require that f(0) = 0, set k = f(1), set p =
803ff48bf5SDavid du Colombier  * log[a](f(a)/k), and then require that f(b) = k*(b^p), where a and b are
813ff48bf5SDavid du Colombier  * two arbitrarily chosen values between 0 and 1.  Naturally all this is
823ff48bf5SDavid du Colombier  * done with some slop.
833ff48bf5SDavid du Colombier  */
843ff48bf5SDavid du Colombier #define CC_INDEX_A (gx_cie_cache_size / 3)
853ff48bf5SDavid du Colombier #define CC_INDEX_B (gx_cie_cache_size * 2 / 3)
86*593dc095SDavid du Colombier #define CC_INDEX_1 (gx_cie_cache_size - 1)
87*593dc095SDavid du Colombier #define CC_KEY(i) ((i) / (double)CC_INDEX_1)
88*593dc095SDavid du Colombier #define CC_KEY_A CC_KEY(CC_INDEX_A)
89*593dc095SDavid du Colombier #define CC_KEY_B CC_KEY(CC_INDEX_B)
903ff48bf5SDavid du Colombier 
913ff48bf5SDavid du Colombier private bool
cie_values_are_exponential(floatp v0,floatp va,floatp vb,floatp k,float * pexpt)92*593dc095SDavid du Colombier cie_values_are_exponential(floatp v0, floatp va, floatp vb, floatp k,
93*593dc095SDavid du Colombier 			   float *pexpt)
943ff48bf5SDavid du Colombier {
953ff48bf5SDavid du Colombier     double p;
963ff48bf5SDavid du Colombier 
97*593dc095SDavid du Colombier     if (fabs(v0) >= 0.001 || fabs(k) < 0.001)
983ff48bf5SDavid du Colombier 	return false;
993ff48bf5SDavid du Colombier     if (va == 0 || (va > 0) != (k > 0))
1003ff48bf5SDavid du Colombier 	return false;
101*593dc095SDavid du Colombier     p = log(va / k) / log(CC_KEY_A);
102*593dc095SDavid du Colombier     if (fabs(vb - k * pow(CC_KEY_B, p)) >= 0.001)
1033ff48bf5SDavid du Colombier 	return false;
1043ff48bf5SDavid du Colombier     *pexpt = p;
1053ff48bf5SDavid du Colombier     return true;
1063ff48bf5SDavid du Colombier }
1073ff48bf5SDavid du Colombier 
1083ff48bf5SDavid du Colombier private bool
cie_scalar_cache_is_exponential(const gx_cie_scalar_cache * pc,float * pexpt)1093ff48bf5SDavid du Colombier cie_scalar_cache_is_exponential(const gx_cie_scalar_cache * pc, float *pexpt)
1103ff48bf5SDavid du Colombier {
111*593dc095SDavid du Colombier     return cie_values_are_exponential(pc->floats.values[0],
112*593dc095SDavid du Colombier 				      pc->floats.values[CC_INDEX_A],
1133ff48bf5SDavid du Colombier 				      pc->floats.values[CC_INDEX_B],
114*593dc095SDavid du Colombier 				      pc->floats.values[CC_INDEX_1],
1153ff48bf5SDavid du Colombier 				      pexpt);
1163ff48bf5SDavid du Colombier }
1173ff48bf5SDavid du Colombier #define CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pca, expts)\
1183ff48bf5SDavid du Colombier   (cie_scalar_cache_is_exponential(&(pca)[0], &(expts).u) &&\
1193ff48bf5SDavid du Colombier    cie_scalar_cache_is_exponential(&(pca)[1], &(expts).v) &&\
1203ff48bf5SDavid du Colombier    cie_scalar_cache_is_exponential(&(pca)[2], &(expts).w))
1213ff48bf5SDavid du Colombier 
1223ff48bf5SDavid du Colombier private bool
cie_vector_cache_is_exponential(const gx_cie_vector_cache * pc,float * pexpt)1233ff48bf5SDavid du Colombier cie_vector_cache_is_exponential(const gx_cie_vector_cache * pc, float *pexpt)
1243ff48bf5SDavid du Colombier {
125*593dc095SDavid du Colombier     return cie_values_are_exponential(pc->vecs.values[0].u,
126*593dc095SDavid du Colombier 				      pc->vecs.values[CC_INDEX_A].u,
1273ff48bf5SDavid du Colombier 				      pc->vecs.values[CC_INDEX_B].u,
128*593dc095SDavid du Colombier 				      pc->vecs.values[CC_INDEX_1].u,
1293ff48bf5SDavid du Colombier 				      pexpt);
1303ff48bf5SDavid du Colombier }
1313ff48bf5SDavid du Colombier #define CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pca, expts)\
1323ff48bf5SDavid du Colombier   (cie_vector_cache_is_exponential(&(pca)[0], &(expts).u) &&\
1333ff48bf5SDavid du Colombier    cie_vector_cache_is_exponential(&(pca)[1], &(expts).v) &&\
1343ff48bf5SDavid du Colombier    cie_vector_cache_is_exponential(&(pca)[2], &(expts).w))
1353ff48bf5SDavid du Colombier 
1363ff48bf5SDavid du Colombier #undef CC_INDEX_A
1373ff48bf5SDavid du Colombier #undef CC_INDEX_B
138*593dc095SDavid du Colombier #undef CC_KEY_A
139*593dc095SDavid du Colombier #undef CC_KEY_B
1403ff48bf5SDavid du Colombier 
1413ff48bf5SDavid du Colombier /*
142*593dc095SDavid du Colombier  * Test whether a cached CIEBasedABC space consists only of a single
143*593dc095SDavid du Colombier  * Decode step followed by a single Matrix step.
1443ff48bf5SDavid du Colombier  */
145*593dc095SDavid du Colombier private cie_cache_one_step_t
cie_cached_abc_is_one_step(const gs_cie_abc * pcie,const gs_matrix3 ** ppmat)146*593dc095SDavid du Colombier cie_cached_abc_is_one_step(const gs_cie_abc *pcie, const gs_matrix3 **ppmat)
1473ff48bf5SDavid du Colombier {
148*593dc095SDavid du Colombier     /* The order of steps is, DecodeABC, MatrixABC, DecodeLMN, MatrixLMN. */
149*593dc095SDavid du Colombier 
150*593dc095SDavid du Colombier     if (CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN)) {
151*593dc095SDavid du Colombier 	if (pcie->MatrixABC.is_identity) {
152*593dc095SDavid du Colombier 	    *ppmat = &pcie->common.MatrixLMN;
153*593dc095SDavid du Colombier 	    return ONE_STEP_ABC;
1543ff48bf5SDavid du Colombier 	}
155*593dc095SDavid du Colombier 	if (pcie->common.MatrixLMN.is_identity) {
156*593dc095SDavid du Colombier 	    *ppmat = &pcie->MatrixABC;
157*593dc095SDavid du Colombier 	    return ONE_STEP_ABC;
1583ff48bf5SDavid du Colombier 	}
1593ff48bf5SDavid du Colombier     }
160*593dc095SDavid du Colombier     if (CIE_CACHE3_IS_IDENTITY(pcie->caches.DecodeABC.caches)) {
161*593dc095SDavid du Colombier 	if (pcie->MatrixABC.is_identity) {
162*593dc095SDavid du Colombier 	    *ppmat = &pcie->common.MatrixLMN;
163*593dc095SDavid du Colombier 	    return ONE_STEP_LMN;
1643ff48bf5SDavid du Colombier 	}
1653ff48bf5SDavid du Colombier     }
166*593dc095SDavid du Colombier     return ONE_STEP_NOT;
1673ff48bf5SDavid du Colombier }
1683ff48bf5SDavid du Colombier 
1693ff48bf5SDavid du Colombier /*
170*593dc095SDavid du Colombier  * Test whether a cached CIEBasedABC space is a L*a*b* space.
1713ff48bf5SDavid du Colombier  */
172*593dc095SDavid du Colombier private bool
cie_scalar_cache_is_lab_lmn(const gs_cie_abc * pcie,int i)173*593dc095SDavid du Colombier cie_scalar_cache_is_lab_lmn(const gs_cie_abc *pcie, int i)
1743ff48bf5SDavid du Colombier {
175*593dc095SDavid du Colombier     double k = CC_KEY(i);
176*593dc095SDavid du Colombier     double g = (k >= 6.0 / 29 ? k * k * k :
177*593dc095SDavid du Colombier 		(k - 4.0 / 29) * (108.0 / 841));
178*593dc095SDavid du Colombier 
179*593dc095SDavid du Colombier #define CC_V(j,i) (pcie->common.caches.DecodeLMN[j].floats.values[i])
180*593dc095SDavid du Colombier #define CC_WP(uvw) (pcie->common.points.WhitePoint.uvw)
181*593dc095SDavid du Colombier 
182*593dc095SDavid du Colombier     return (fabs(CC_V(0, i) - g * CC_WP(u)) < 0.001 &&
183*593dc095SDavid du Colombier 	    fabs(CC_V(1, i) - g * CC_WP(v)) < 0.001 &&
184*593dc095SDavid du Colombier 	    fabs(CC_V(2, i) - g * CC_WP(w)) < 0.001
185*593dc095SDavid du Colombier 	    );
186*593dc095SDavid du Colombier 
187*593dc095SDavid du Colombier #undef CC_V
188*593dc095SDavid du Colombier #undef CC_WP
1893ff48bf5SDavid du Colombier }
190*593dc095SDavid du Colombier private bool
cie_vector_cache_is_lab_abc(const gx_cie_vector_cache3_t * pvc,int i)191*593dc095SDavid du Colombier cie_vector_cache_is_lab_abc(const gx_cie_vector_cache3_t *pvc, int i)
192*593dc095SDavid du Colombier {
193*593dc095SDavid du Colombier     const gx_cie_vector_cache *const pc3 = pvc->caches;
194*593dc095SDavid du Colombier     double k = CC_KEY(i);
195*593dc095SDavid du Colombier     double l0 = pc3[0].vecs.params.base,
196*593dc095SDavid du Colombier 	l = l0 + k * (pc3[0].vecs.params.limit - l0);
197*593dc095SDavid du Colombier     double a0 = pc3[1].vecs.params.base,
198*593dc095SDavid du Colombier 	a = a0 + k * (pc3[1].vecs.params.limit - a0);
199*593dc095SDavid du Colombier     double b0 = pc3[2].vecs.params.base,
200*593dc095SDavid du Colombier 	b = b0 + k * (pc3[2].vecs.params.limit - b0);
201*593dc095SDavid du Colombier 
202*593dc095SDavid du Colombier     return (fabs(cie_cached2float(pc3[0].vecs.values[i].u) -
203*593dc095SDavid du Colombier 		 (l + 16) / 116) < 0.001 &&
204*593dc095SDavid du Colombier 	    fabs(cie_cached2float(pc3[1].vecs.values[i].u) -
205*593dc095SDavid du Colombier 		 a / 500) < 0.001 &&
206*593dc095SDavid du Colombier 	    fabs(cie_cached2float(pc3[2].vecs.values[i].w) -
207*593dc095SDavid du Colombier 		 b / -200) < 0.001
208*593dc095SDavid du Colombier 	    );
2093ff48bf5SDavid du Colombier }
2103ff48bf5SDavid du Colombier 
211*593dc095SDavid du Colombier private bool
cie_is_lab(const gs_cie_abc * pcie)212*593dc095SDavid du Colombier cie_is_lab(const gs_cie_abc *pcie)
213*593dc095SDavid du Colombier {
214*593dc095SDavid du Colombier     int i;
215*593dc095SDavid du Colombier 
216*593dc095SDavid du Colombier     /* Check MatrixABC and MatrixLMN. */
217*593dc095SDavid du Colombier     if (!(pcie->MatrixABC.cu.u == 1 && pcie->MatrixABC.cu.v == 1 &&
218*593dc095SDavid du Colombier 	  pcie->MatrixABC.cu.w == 1 &&
219*593dc095SDavid du Colombier 	  pcie->MatrixABC.cv.u == 1 && pcie->MatrixABC.cv.v == 0 &&
220*593dc095SDavid du Colombier 	  pcie->MatrixABC.cv.w == 0 &&
221*593dc095SDavid du Colombier 	  pcie->MatrixABC.cw.u == 0 && pcie->MatrixABC.cw.v == 0 &&
222*593dc095SDavid du Colombier 	  pcie->MatrixABC.cw.w == -1 &&
223*593dc095SDavid du Colombier 	  pcie->common.MatrixLMN.is_identity
224*593dc095SDavid du Colombier 	  ))
225*593dc095SDavid du Colombier 	return false;
226*593dc095SDavid du Colombier 
227*593dc095SDavid du Colombier     /* Check DecodeABC and DecodeLMN. */
228*593dc095SDavid du Colombier     for (i = 0; i <= CC_INDEX_1; ++i)
229*593dc095SDavid du Colombier 	if (!(cie_vector_cache_is_lab_abc(&pcie->caches.DecodeABC, i) &&
230*593dc095SDavid du Colombier 	      cie_scalar_cache_is_lab_lmn(pcie, i)
231*593dc095SDavid du Colombier 	      ))
232*593dc095SDavid du Colombier 	    return false;
233*593dc095SDavid du Colombier 
234*593dc095SDavid du Colombier     return true;
235*593dc095SDavid du Colombier }
236*593dc095SDavid du Colombier 
237*593dc095SDavid du Colombier #undef CC_INDEX_1
238*593dc095SDavid du Colombier #undef CC_KEY
239*593dc095SDavid du Colombier 
240*593dc095SDavid du Colombier /* Test whether one or more CIE-based ranges are [0..1]. */
241*593dc095SDavid du Colombier private bool
cie_ranges_are_0_1(const gs_range * prange,int n)242*593dc095SDavid du Colombier cie_ranges_are_0_1(const gs_range *prange, int n)
243*593dc095SDavid du Colombier {
244*593dc095SDavid du Colombier     int i;
245*593dc095SDavid du Colombier 
246*593dc095SDavid du Colombier     for (i = 0; i < n; ++i)
247*593dc095SDavid du Colombier 	if (prange[i].rmin != 0 || prange[i].rmax != 1)
248*593dc095SDavid du Colombier 	    return false;
249*593dc095SDavid du Colombier     return true;
250*593dc095SDavid du Colombier }
251*593dc095SDavid du Colombier 
252*593dc095SDavid du Colombier /* ------ Utilities ------ */
253*593dc095SDavid du Colombier 
2543ff48bf5SDavid du Colombier /* Add a 3-element vector to a Cos array or dictionary. */
2553ff48bf5SDavid du Colombier private int
cos_array_add_vector3(cos_array_t * pca,const gs_vector3 * pvec)2563ff48bf5SDavid du Colombier cos_array_add_vector3(cos_array_t *pca, const gs_vector3 *pvec)
2573ff48bf5SDavid du Colombier {
2583ff48bf5SDavid du Colombier     int code = cos_array_add_real(pca, pvec->u);
2593ff48bf5SDavid du Colombier 
2603ff48bf5SDavid du Colombier     if (code >= 0)
2613ff48bf5SDavid du Colombier 	code = cos_array_add_real(pca, pvec->v);
2623ff48bf5SDavid du Colombier     if (code >= 0)
2633ff48bf5SDavid du Colombier 	code = cos_array_add_real(pca, pvec->w);
2643ff48bf5SDavid du Colombier     return code;
2653ff48bf5SDavid du Colombier }
2663ff48bf5SDavid du Colombier private int
cos_dict_put_c_key_vector3(cos_dict_t * pcd,const char * key,const gs_vector3 * pvec)2673ff48bf5SDavid du Colombier cos_dict_put_c_key_vector3(cos_dict_t *pcd, const char *key,
2683ff48bf5SDavid du Colombier 			   const gs_vector3 *pvec)
2693ff48bf5SDavid du Colombier {
2703ff48bf5SDavid du Colombier     cos_array_t *pca = cos_array_alloc(pcd->pdev, "cos_array_from_vector3");
2713ff48bf5SDavid du Colombier     int code;
2723ff48bf5SDavid du Colombier 
2733ff48bf5SDavid du Colombier     if (pca == 0)
2743ff48bf5SDavid du Colombier 	return_error(gs_error_VMerror);
2753ff48bf5SDavid du Colombier     code = cos_array_add_vector3(pca, pvec);
2763ff48bf5SDavid du Colombier     if (code < 0) {
2773ff48bf5SDavid du Colombier 	COS_FREE(pca, "cos_array_from_vector3");
2783ff48bf5SDavid du Colombier 	return code;
2793ff48bf5SDavid du Colombier     }
2803ff48bf5SDavid du Colombier     return cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
2813ff48bf5SDavid du Colombier }
2823ff48bf5SDavid du Colombier 
283*593dc095SDavid du Colombier /*
284*593dc095SDavid du Colombier  * Finish creating a CIE-based color space (Calxxx or Lab.)
285*593dc095SDavid du Colombier  * This procedure is exported for gdevpdfk.c.
286*593dc095SDavid du Colombier  */
287*593dc095SDavid du Colombier int
pdf_finish_cie_space(cos_array_t * pca,cos_dict_t * pcd,const gs_cie_common * pciec)288*593dc095SDavid du Colombier pdf_finish_cie_space(cos_array_t *pca, cos_dict_t *pcd,
289*593dc095SDavid du Colombier 		     const gs_cie_common *pciec)
290*593dc095SDavid du Colombier {
291*593dc095SDavid du Colombier     int code = cos_dict_put_c_key_vector3(pcd, "/WhitePoint",
292*593dc095SDavid du Colombier 					  &pciec->points.WhitePoint);
293*593dc095SDavid du Colombier 
294*593dc095SDavid du Colombier     if (code < 0)
295*593dc095SDavid du Colombier 	return code;
296*593dc095SDavid du Colombier     if (pciec->points.BlackPoint.u != 0 ||
297*593dc095SDavid du Colombier 	pciec->points.BlackPoint.v != 0 ||
298*593dc095SDavid du Colombier 	pciec->points.BlackPoint.w != 0
299*593dc095SDavid du Colombier 	) {
300*593dc095SDavid du Colombier 	code = cos_dict_put_c_key_vector3(pcd, "/BlackPoint",
301*593dc095SDavid du Colombier 					  &pciec->points.BlackPoint);
302*593dc095SDavid du Colombier 	if (code < 0)
303*593dc095SDavid du Colombier 	    return code;
304*593dc095SDavid du Colombier     }
305*593dc095SDavid du Colombier     return cos_array_add_object(pca, COS_OBJECT(pcd));
306*593dc095SDavid du Colombier }
307*593dc095SDavid du Colombier 
308*593dc095SDavid du Colombier /* ------ Color space writing ------ */
309*593dc095SDavid du Colombier 
310*593dc095SDavid du Colombier /* Define standard and short color space names. */
311*593dc095SDavid du Colombier const pdf_color_space_names_t pdf_color_space_names = {
312*593dc095SDavid du Colombier     PDF_COLOR_SPACE_NAMES
313*593dc095SDavid du Colombier };
314*593dc095SDavid du Colombier const pdf_color_space_names_t pdf_color_space_names_short = {
315*593dc095SDavid du Colombier     PDF_COLOR_SPACE_NAMES_SHORT
316*593dc095SDavid du Colombier };
317*593dc095SDavid du Colombier 
318*593dc095SDavid du Colombier /*
319*593dc095SDavid du Colombier  * Create a local Device{Gray,RGB,CMYK} color space corresponding to the
320*593dc095SDavid du Colombier  * given number of components.
321*593dc095SDavid du Colombier  */
322*593dc095SDavid du Colombier int
pdf_cspace_init_Device(const gs_memory_t * mem,gs_color_space * pcs,int num_components)323*593dc095SDavid du Colombier pdf_cspace_init_Device(const gs_memory_t *mem, gs_color_space *pcs, int num_components)
324*593dc095SDavid du Colombier {
325*593dc095SDavid du Colombier     switch (num_components) {
326*593dc095SDavid du Colombier     case 1: gs_cspace_init_DeviceGray(mem, pcs); break;
327*593dc095SDavid du Colombier     case 3: gs_cspace_init_DeviceRGB(mem, pcs); break;
328*593dc095SDavid du Colombier     case 4: gs_cspace_init_DeviceCMYK(mem, pcs); break;
329*593dc095SDavid du Colombier     default: return_error(gs_error_rangecheck);
330*593dc095SDavid du Colombier     }
331*593dc095SDavid du Colombier     return 0;
332*593dc095SDavid du Colombier }
333*593dc095SDavid du Colombier 
3343ff48bf5SDavid du Colombier /* Create a Separation or DeviceN color space (internal). */
3353ff48bf5SDavid du Colombier private int
pdf_separation_color_space(gx_device_pdf * pdev,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)3363ff48bf5SDavid du Colombier pdf_separation_color_space(gx_device_pdf *pdev,
3373ff48bf5SDavid du Colombier 			   cos_array_t *pca, const char *csname,
3383ff48bf5SDavid du Colombier 			   const cos_value_t *snames,
3393ff48bf5SDavid du Colombier 			   const gs_color_space *alt_space,
3403ff48bf5SDavid du Colombier 			   const gs_function_t *pfn,
3413ff48bf5SDavid du Colombier 			   const pdf_color_space_names_t *pcsn)
3423ff48bf5SDavid du Colombier {
3433ff48bf5SDavid du Colombier     cos_value_t v;
344*593dc095SDavid du Colombier     const gs_range_t *ranges;
3453ff48bf5SDavid du Colombier     int code;
3463ff48bf5SDavid du Colombier 
3473ff48bf5SDavid du Colombier     if ((code = cos_array_add(pca, cos_c_string_value(&v, csname))) < 0 ||
3483ff48bf5SDavid du Colombier 	(code = cos_array_add_no_copy(pca, snames)) < 0 ||
349*593dc095SDavid du Colombier 	(code = pdf_color_space(pdev, &v, &ranges, alt_space, pcsn, false)) < 0 ||
3503ff48bf5SDavid du Colombier 	(code = cos_array_add(pca, &v)) < 0 ||
351*593dc095SDavid du Colombier 	(code = pdf_function_scaled(pdev, pfn, ranges, &v)) < 0 ||
3523ff48bf5SDavid du Colombier 	(code = cos_array_add(pca, &v)) < 0
3533ff48bf5SDavid du Colombier 	)
3543ff48bf5SDavid du Colombier 	return code;
3553ff48bf5SDavid du Colombier     return 0;
3563ff48bf5SDavid du Colombier }
3573ff48bf5SDavid du Colombier 
3583ff48bf5SDavid du Colombier /*
359*593dc095SDavid du Colombier  * Create an Indexed color space.  This is a single-use procedure,
360*593dc095SDavid du Colombier  * broken out only for readability.
361*593dc095SDavid du Colombier  */
362*593dc095SDavid du Colombier private int
pdf_indexed_color_space(gx_device_pdf * pdev,cos_value_t * pvalue,const gs_color_space * pcs,cos_array_t * pca)363*593dc095SDavid du Colombier pdf_indexed_color_space(gx_device_pdf *pdev, cos_value_t *pvalue,
364*593dc095SDavid du Colombier 			const gs_color_space *pcs, cos_array_t *pca)
365*593dc095SDavid du Colombier {
366*593dc095SDavid du Colombier     const gs_indexed_params *pip = &pcs->params.indexed;
367*593dc095SDavid du Colombier     const gs_color_space *base_space =
368*593dc095SDavid du Colombier 	(const gs_color_space *)&pip->base_space;
369*593dc095SDavid du Colombier     int num_entries = pip->hival + 1;
370*593dc095SDavid du Colombier     int num_components = gs_color_space_num_components(base_space);
371*593dc095SDavid du Colombier     uint table_size = num_entries * num_components;
372*593dc095SDavid du Colombier     /* Guess at the extra space needed for PS string encoding. */
373*593dc095SDavid du Colombier     uint string_size = 2 + table_size * 4;
374*593dc095SDavid du Colombier     uint string_used;
375*593dc095SDavid du Colombier     byte buf[100];		/* arbitrary */
376*593dc095SDavid du Colombier     stream_AXE_state st;
377*593dc095SDavid du Colombier     stream s, es;
378*593dc095SDavid du Colombier     gs_memory_t *mem = pdev->pdf_memory;
379*593dc095SDavid du Colombier     byte *table;
380*593dc095SDavid du Colombier     byte *palette;
381*593dc095SDavid du Colombier     gs_color_space cs_gray;
382*593dc095SDavid du Colombier     cos_value_t v;
383*593dc095SDavid du Colombier     int code;
384*593dc095SDavid du Colombier 
385*593dc095SDavid du Colombier     /* PDF doesn't support Indexed color spaces with more than 256 entries. */
386*593dc095SDavid du Colombier     if (num_entries > 256)
387*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
388*593dc095SDavid du Colombier     if (pdev->CompatibilityLevel < 1.3) {
389*593dc095SDavid du Colombier 	switch (gs_color_space_get_index(pcs)) {
390*593dc095SDavid du Colombier 	    case gs_color_space_index_Pattern:
391*593dc095SDavid du Colombier 	    case gs_color_space_index_Separation:
392*593dc095SDavid du Colombier 	    case gs_color_space_index_Indexed:
393*593dc095SDavid du Colombier 	    case gs_color_space_index_DeviceN:
394*593dc095SDavid du Colombier 		return_error(gs_error_rangecheck);
395*593dc095SDavid du Colombier 	    default: DO_NOTHING;
396*593dc095SDavid du Colombier 	}
397*593dc095SDavid du Colombier 
398*593dc095SDavid du Colombier     }
399*593dc095SDavid du Colombier     table = gs_alloc_string(mem, string_size, "pdf_color_space(table)");
400*593dc095SDavid du Colombier     palette = gs_alloc_string(mem, table_size, "pdf_color_space(palette)");
401*593dc095SDavid du Colombier     if (table == 0 || palette == 0) {
402*593dc095SDavid du Colombier 	gs_free_string(mem, palette, table_size,
403*593dc095SDavid du Colombier 		       "pdf_color_space(palette)");
404*593dc095SDavid du Colombier 	gs_free_string(mem, table, string_size,
405*593dc095SDavid du Colombier 		       "pdf_color_space(table)");
406*593dc095SDavid du Colombier 	return_error(gs_error_VMerror);
407*593dc095SDavid du Colombier     }
408*593dc095SDavid du Colombier     swrite_string(&s, table, string_size);
409*593dc095SDavid du Colombier     s_init(&es, mem);
410*593dc095SDavid du Colombier     s_init_state((stream_state *)&st, &s_PSSE_template, NULL);
411*593dc095SDavid du Colombier     s_init_filter(&es, (stream_state *)&st, buf, sizeof(buf), &s);
412*593dc095SDavid du Colombier     sputc(&s, '(');
413*593dc095SDavid du Colombier     if (pcs->params.indexed.use_proc) {
414*593dc095SDavid du Colombier 	gs_client_color cmin, cmax;
415*593dc095SDavid du Colombier 	byte *pnext = palette;
416*593dc095SDavid du Colombier 	int i, j;
417*593dc095SDavid du Colombier 
418*593dc095SDavid du Colombier 	/* Find the legal range for the color components. */
419*593dc095SDavid du Colombier 	for (j = 0; j < num_components; ++j)
420*593dc095SDavid du Colombier 	    cmin.paint.values[j] = (float)min_long,
421*593dc095SDavid du Colombier 		cmax.paint.values[j] = (float)max_long;
422*593dc095SDavid du Colombier 	gs_color_space_restrict_color(&cmin, base_space);
423*593dc095SDavid du Colombier 	gs_color_space_restrict_color(&cmax, base_space);
424*593dc095SDavid du Colombier 	/*
425*593dc095SDavid du Colombier 	 * Compute the palette values, with the legal range for each
426*593dc095SDavid du Colombier 	 * one mapped to [0 .. 255].
427*593dc095SDavid du Colombier 	 */
428*593dc095SDavid du Colombier 	for (i = 0; i < num_entries; ++i) {
429*593dc095SDavid du Colombier 	    gs_client_color cc;
430*593dc095SDavid du Colombier 
431*593dc095SDavid du Colombier 	    gs_cspace_indexed_lookup(&pcs->params.indexed, i, &cc);
432*593dc095SDavid du Colombier 	    for (j = 0; j < num_components; ++j) {
433*593dc095SDavid du Colombier 		float v = (cc.paint.values[j] - cmin.paint.values[j])
434*593dc095SDavid du Colombier 		    * 255 / (cmax.paint.values[j] - cmin.paint.values[j]);
435*593dc095SDavid du Colombier 
436*593dc095SDavid du Colombier 		*pnext++ = (v <= 0 ? 0 : v >= 255 ? 255 : (byte)v);
437*593dc095SDavid du Colombier 	    }
438*593dc095SDavid du Colombier 	}
439*593dc095SDavid du Colombier     } else
440*593dc095SDavid du Colombier 	memcpy(palette, pip->lookup.table.data, table_size);
441*593dc095SDavid du Colombier     if (gs_color_space_get_index(base_space) ==
442*593dc095SDavid du Colombier 	gs_color_space_index_DeviceRGB
443*593dc095SDavid du Colombier 	) {
444*593dc095SDavid du Colombier 	/* Check for an all-gray palette3. */
445*593dc095SDavid du Colombier 	int i;
446*593dc095SDavid du Colombier 
447*593dc095SDavid du Colombier 	for (i = table_size; (i -= 3) >= 0; )
448*593dc095SDavid du Colombier 	    if (palette[i] != palette[i + 1] ||
449*593dc095SDavid du Colombier 		palette[i] != palette[i + 2]
450*593dc095SDavid du Colombier 		)
451*593dc095SDavid du Colombier 		break;
452*593dc095SDavid du Colombier 	if (i < 0) {
453*593dc095SDavid du Colombier 	    /* Change the color space to DeviceGray. */
454*593dc095SDavid du Colombier 	    for (i = 0; i < num_entries; ++i)
455*593dc095SDavid du Colombier 		palette[i] = palette[i * 3];
456*593dc095SDavid du Colombier 	    table_size = num_entries;
457*593dc095SDavid du Colombier 	    gs_cspace_init_DeviceGray(mem, &cs_gray);
458*593dc095SDavid du Colombier 	    base_space = &cs_gray;
459*593dc095SDavid du Colombier 	}
460*593dc095SDavid du Colombier     }
461*593dc095SDavid du Colombier     stream_write(&es, palette, table_size);
462*593dc095SDavid du Colombier     gs_free_string(mem, palette, table_size, "pdf_color_space(palette)");
463*593dc095SDavid du Colombier     sclose(&es);
464*593dc095SDavid du Colombier     sflush(&s);
465*593dc095SDavid du Colombier     string_used = (uint)stell(&s);
466*593dc095SDavid du Colombier     table = gs_resize_string(mem, table, string_size, string_used,
467*593dc095SDavid du Colombier 			     "pdf_color_space(table)");
468*593dc095SDavid du Colombier     /*
469*593dc095SDavid du Colombier      * Since the array is always referenced by name as a resource
470*593dc095SDavid du Colombier      * rather than being written as a value, even for in-line images,
471*593dc095SDavid du Colombier      * always use the full name for the color space.
472*593dc095SDavid du Colombier      *
473*593dc095SDavid du Colombier      * We don't have to worry about the range of the base space:
474*593dc095SDavid du Colombier      * in PDF, unlike PostScript, the values from the lookup table are
475*593dc095SDavid du Colombier      * scaled automatically.
476*593dc095SDavid du Colombier      */
477*593dc095SDavid du Colombier     if ((code = pdf_color_space(pdev, pvalue, NULL, base_space,
478*593dc095SDavid du Colombier 				&pdf_color_space_names, false)) < 0 ||
479*593dc095SDavid du Colombier 	(code = cos_array_add(pca,
480*593dc095SDavid du Colombier 			      cos_c_string_value(&v,
481*593dc095SDavid du Colombier 						 pdf_color_space_names.Indexed
482*593dc095SDavid du Colombier 						 /*pcsn->Indexed*/))) < 0 ||
483*593dc095SDavid du Colombier 	(code = cos_array_add(pca, pvalue)) < 0 ||
484*593dc095SDavid du Colombier 	(code = cos_array_add_int(pca, pip->hival)) < 0 ||
485*593dc095SDavid du Colombier 	(code = cos_array_add_no_copy(pca,
486*593dc095SDavid du Colombier 				      cos_string_value(&v, table,
487*593dc095SDavid du Colombier 						       string_used))) < 0
488*593dc095SDavid du Colombier 	)
489*593dc095SDavid du Colombier 	return code;
490*593dc095SDavid du Colombier     return 0;
491*593dc095SDavid du Colombier }
492*593dc095SDavid du Colombier 
493*593dc095SDavid du Colombier /*
494*593dc095SDavid du Colombier  * Find a color space resource by seriialized data.
495*593dc095SDavid du Colombier  */
496*593dc095SDavid du Colombier private pdf_resource_t *
pdf_find_cspace_resource(gx_device_pdf * pdev,const byte * serialized,uint serialized_size)497*593dc095SDavid du Colombier pdf_find_cspace_resource(gx_device_pdf *pdev, const byte *serialized, uint serialized_size)
498*593dc095SDavid du Colombier {
499*593dc095SDavid du Colombier     pdf_resource_t **pchain = pdev->resources[resourceColorSpace].chains;
500*593dc095SDavid du Colombier     pdf_resource_t *pres;
501*593dc095SDavid du Colombier     int i;
502*593dc095SDavid du Colombier 
503*593dc095SDavid du Colombier     for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
504*593dc095SDavid du Colombier 	for (pres = pchain[i]; pres != 0; pres = pres->next) {
505*593dc095SDavid du Colombier 	    const pdf_color_space_t *const ppcs =
506*593dc095SDavid du Colombier 		(const pdf_color_space_t *)pres;
507*593dc095SDavid du Colombier 	    if (ppcs->serialized_size != serialized_size)
508*593dc095SDavid du Colombier 		continue;
509*593dc095SDavid du Colombier 	    if (!memcmp(ppcs->serialized, serialized, ppcs->serialized_size))
510*593dc095SDavid du Colombier 		return pres;
511*593dc095SDavid du Colombier 	}
512*593dc095SDavid du Colombier     }
513*593dc095SDavid du Colombier     return NULL;
514*593dc095SDavid du Colombier }
515*593dc095SDavid du Colombier 
516*593dc095SDavid du Colombier 
517*593dc095SDavid du Colombier /*
5183ff48bf5SDavid du Colombier  * Create a PDF color space corresponding to a PostScript color space.
5193ff48bf5SDavid du Colombier  * For parameterless color spaces, set *pvalue to a (literal) string with
520*593dc095SDavid du Colombier  * the color space name; for other color spaces, create a cos_array_t if
521*593dc095SDavid du Colombier  * necessary and set *pvalue to refer to it.  In the latter case, if
522*593dc095SDavid du Colombier  * by_name is true, return a string /Rxxxx rather than a reference to
523*593dc095SDavid du Colombier  * the actual object.
524*593dc095SDavid du Colombier  *
525*593dc095SDavid du Colombier  * If ppranges is not NULL, then if  the domain of the color space had
526*593dc095SDavid du Colombier  * to be scaled (to convert a CIEBased space to ICCBased), store a pointer
527*593dc095SDavid du Colombier  * to the ranges in *ppranges, otherwise set *ppranges to 0.
5283ff48bf5SDavid du Colombier  */
5293ff48bf5SDavid du Colombier int
pdf_color_space_named(gx_device_pdf * pdev,cos_value_t * pvalue,const gs_range_t ** ppranges,const gs_color_space * pcs,const pdf_color_space_names_t * pcsn,bool by_name,const byte * res_name,int name_length)530*593dc095SDavid du Colombier pdf_color_space_named(gx_device_pdf *pdev, cos_value_t *pvalue,
531*593dc095SDavid du Colombier 		const gs_range_t **ppranges,
5323ff48bf5SDavid du Colombier 		const gs_color_space *pcs,
5333ff48bf5SDavid du Colombier 		const pdf_color_space_names_t *pcsn,
534*593dc095SDavid du Colombier 		bool by_name, const byte *res_name, int name_length)
5353ff48bf5SDavid du Colombier {
5363ff48bf5SDavid du Colombier     gs_color_space_index csi = gs_color_space_get_index(pcs);
5373ff48bf5SDavid du Colombier     cos_array_t *pca;
5383ff48bf5SDavid du Colombier     cos_dict_t *pcd;
5393ff48bf5SDavid du Colombier     cos_value_t v;
5403ff48bf5SDavid du Colombier     const gs_cie_common *pciec;
5413ff48bf5SDavid du Colombier     gs_function_t *pfn;
542*593dc095SDavid du Colombier     const gs_range_t *ranges = 0;
543*593dc095SDavid du Colombier     uint serialized_size;
544*593dc095SDavid du Colombier     byte *serialized = NULL, serialized0[100];
545*593dc095SDavid du Colombier     pdf_resource_t *pres = NULL;
5463ff48bf5SDavid du Colombier     int code;
5473ff48bf5SDavid du Colombier 
548*593dc095SDavid du Colombier     if (ppranges)
549*593dc095SDavid du Colombier 	*ppranges = 0;		/* default */
5503ff48bf5SDavid du Colombier     switch (csi) {
5513ff48bf5SDavid du Colombier     case gs_color_space_index_DeviceGray:
5523ff48bf5SDavid du Colombier 	cos_c_string_value(pvalue, pcsn->DeviceGray);
5533ff48bf5SDavid du Colombier 	return 0;
5543ff48bf5SDavid du Colombier     case gs_color_space_index_DeviceRGB:
5553ff48bf5SDavid du Colombier 	cos_c_string_value(pvalue, pcsn->DeviceRGB);
5563ff48bf5SDavid du Colombier 	return 0;
5573ff48bf5SDavid du Colombier     case gs_color_space_index_DeviceCMYK:
5583ff48bf5SDavid du Colombier 	cos_c_string_value(pvalue, pcsn->DeviceCMYK);
5593ff48bf5SDavid du Colombier 	return 0;
5603ff48bf5SDavid du Colombier     case gs_color_space_index_Pattern:
5613ff48bf5SDavid du Colombier 	if (!pcs->params.pattern.has_base_space) {
5623ff48bf5SDavid du Colombier 	    cos_c_string_value(pvalue, "/Pattern");
5633ff48bf5SDavid du Colombier 	    return 0;
5643ff48bf5SDavid du Colombier 	}
5653ff48bf5SDavid du Colombier 	break;
5663ff48bf5SDavid du Colombier     case gs_color_space_index_CIEICC:
5673ff48bf5SDavid du Colombier         /*
5683ff48bf5SDavid du Colombier 	 * Take a special early exit for unrecognized ICCBased color spaces,
5693ff48bf5SDavid du Colombier 	 * or for PDF 1.2 output (ICCBased color spaces date from PDF 1.3).
5703ff48bf5SDavid du Colombier 	 */
5713ff48bf5SDavid du Colombier         if (pcs->params.icc.picc_info->picc == 0 ||
5723ff48bf5SDavid du Colombier 	    pdev->CompatibilityLevel < 1.3
573*593dc095SDavid du Colombier 	    ) {
574*593dc095SDavid du Colombier 	    if (res_name != NULL)
575*593dc095SDavid du Colombier 		return 0; /* Ignore .includecolorspace */
576*593dc095SDavid du Colombier             return pdf_color_space( pdev, pvalue, ppranges,
5773ff48bf5SDavid du Colombier                                     (const gs_color_space *)
5783ff48bf5SDavid du Colombier                                         &pcs->params.icc.alt_space,
5793ff48bf5SDavid du Colombier                                     pcsn, by_name);
580*593dc095SDavid du Colombier 	}
5813ff48bf5SDavid du Colombier         break;
5823ff48bf5SDavid du Colombier     default:
5833ff48bf5SDavid du Colombier 	break;
5843ff48bf5SDavid du Colombier     }
5853ff48bf5SDavid du Colombier 
586*593dc095SDavid du Colombier     /* Check whether we already have a PDF object for this color space. */
587*593dc095SDavid du Colombier     if (pcs->id != gs_no_id)
588*593dc095SDavid du Colombier 	pres = pdf_find_resource_by_gs_id(pdev, resourceColorSpace, pcs->id);
589*593dc095SDavid du Colombier     if (pres == NULL) {
590*593dc095SDavid du Colombier 	stream s;
591*593dc095SDavid du Colombier 
592*593dc095SDavid du Colombier 	s_init(&s, pdev->memory);
593*593dc095SDavid du Colombier 	swrite_position_only(&s);
594*593dc095SDavid du Colombier 	code = cs_serialize(pcs, &s);
595*593dc095SDavid du Colombier 	if (code < 0)
596*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
597*593dc095SDavid du Colombier 	serialized_size = stell(&s);
598*593dc095SDavid du Colombier 	sclose(&s);
599*593dc095SDavid du Colombier 	if (serialized_size <= sizeof(serialized0))
600*593dc095SDavid du Colombier 	    serialized = serialized0;
601*593dc095SDavid du Colombier 	else {
602*593dc095SDavid du Colombier 	    serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space");
603*593dc095SDavid du Colombier 	    if (serialized == NULL)
604*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
605*593dc095SDavid du Colombier 	}
606*593dc095SDavid du Colombier 	swrite_string(&s, serialized, serialized_size);
607*593dc095SDavid du Colombier 	code = cs_serialize(pcs, &s);
608*593dc095SDavid du Colombier 	if (code < 0)
609*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
610*593dc095SDavid du Colombier 	if (stell(&s) != serialized_size)
611*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
612*593dc095SDavid du Colombier 	sclose(&s);
613*593dc095SDavid du Colombier 	pres = pdf_find_cspace_resource(pdev, serialized, serialized_size);
614*593dc095SDavid du Colombier 	if (pres != NULL) {
615*593dc095SDavid du Colombier 	    if (serialized != serialized0)
616*593dc095SDavid du Colombier 		gs_free_object(pdev->pdf_memory, serialized, "pdf_color_space");
617*593dc095SDavid du Colombier 	    serialized = NULL;
618*593dc095SDavid du Colombier 	}
619*593dc095SDavid du Colombier     }
620*593dc095SDavid du Colombier     if (pres) {
621*593dc095SDavid du Colombier 	const pdf_color_space_t *const ppcs =
622*593dc095SDavid du Colombier 	    (const pdf_color_space_t *)pres;
623*593dc095SDavid du Colombier 
624*593dc095SDavid du Colombier 	if (ppranges != 0 && ppcs->ranges != 0)
625*593dc095SDavid du Colombier 	    *ppranges = ppcs->ranges;
626*593dc095SDavid du Colombier 	pca = (cos_array_t *)pres->object;
627*593dc095SDavid du Colombier 	goto ret;
628*593dc095SDavid du Colombier     }
629*593dc095SDavid du Colombier 
6303ff48bf5SDavid du Colombier     /* Space has parameters -- create an array. */
6313ff48bf5SDavid du Colombier     pca = cos_array_alloc(pdev, "pdf_color_space");
6323ff48bf5SDavid du Colombier     if (pca == 0)
6333ff48bf5SDavid du Colombier 	return_error(gs_error_VMerror);
634*593dc095SDavid du Colombier 
6353ff48bf5SDavid du Colombier     switch (csi) {
6363ff48bf5SDavid du Colombier 
637*593dc095SDavid du Colombier     case gs_color_space_index_CIEICC:
638*593dc095SDavid du Colombier 	code = pdf_iccbased_color_space(pdev, pvalue, pcs, pca);
6393ff48bf5SDavid du Colombier         break;
6403ff48bf5SDavid du Colombier 
6413ff48bf5SDavid du Colombier     case gs_color_space_index_CIEA: {
6423ff48bf5SDavid du Colombier 	/* Check that we can represent this as a CalGray space. */
6433ff48bf5SDavid du Colombier 	const gs_cie_a *pcie = pcs->params.a;
644*593dc095SDavid du Colombier 	bool unitary = cie_ranges_are_0_1(&pcie->RangeA, 1);
645*593dc095SDavid du Colombier 	bool identityA = (pcie->MatrixA.u == 1 && pcie->MatrixA.v == 1 &&
646*593dc095SDavid du Colombier 	                  pcie->MatrixA.w == 1);
6473ff48bf5SDavid du Colombier 	gs_vector3 expts;
6483ff48bf5SDavid du Colombier 
6493ff48bf5SDavid du Colombier 	pciec = (const gs_cie_common *)pcie;
650*593dc095SDavid du Colombier 	if (!pcie->common.MatrixLMN.is_identity) {
651*593dc095SDavid du Colombier 	    code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec,
652*593dc095SDavid du Colombier 					 &pcie->RangeA, ONE_STEP_NOT, NULL,
653*593dc095SDavid du Colombier 					 &ranges);
654*593dc095SDavid du Colombier 	    break;
655*593dc095SDavid du Colombier 	}
656*593dc095SDavid du Colombier 	if (unitary && identityA &&
657*593dc095SDavid du Colombier 	    CIE_CACHE_IS_IDENTITY(&pcie->caches.DecodeA) &&
6583ff48bf5SDavid du Colombier 	    CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts) &&
6593ff48bf5SDavid du Colombier 	    expts.v == expts.u && expts.w == expts.u
6603ff48bf5SDavid du Colombier 	    ) {
6613ff48bf5SDavid du Colombier 	    DO_NOTHING;
662*593dc095SDavid du Colombier 	} else if (unitary && identityA &&
663*593dc095SDavid du Colombier 		   CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN) &&
6643ff48bf5SDavid du Colombier 		   cie_vector_cache_is_exponential(&pcie->caches.DecodeA, &expts.u)
6653ff48bf5SDavid du Colombier 		   ) {
6663ff48bf5SDavid du Colombier 	    DO_NOTHING;
667*593dc095SDavid du Colombier 	} else {
668*593dc095SDavid du Colombier 	    code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec,
669*593dc095SDavid du Colombier 					 &pcie->RangeA, ONE_STEP_NOT, NULL,
670*593dc095SDavid du Colombier 					 &ranges);
671*593dc095SDavid du Colombier 	    break;
672*593dc095SDavid du Colombier 	}
6733ff48bf5SDavid du Colombier 	code = cos_array_add(pca, cos_c_string_value(&v, "/CalGray"));
6743ff48bf5SDavid du Colombier 	if (code < 0)
6753ff48bf5SDavid du Colombier 	    return code;
6763ff48bf5SDavid du Colombier 	pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
6773ff48bf5SDavid du Colombier 	if (pcd == 0)
6783ff48bf5SDavid du Colombier 	    return_error(gs_error_VMerror);
6793ff48bf5SDavid du Colombier 	if (expts.u != 1) {
6803ff48bf5SDavid du Colombier 	    code = cos_dict_put_c_key_real(pcd, "/Gamma", expts.u);
6813ff48bf5SDavid du Colombier 	    if (code < 0)
6823ff48bf5SDavid du Colombier 		return code;
6833ff48bf5SDavid du Colombier 	}
6843ff48bf5SDavid du Colombier     }
6853ff48bf5SDavid du Colombier     cal:
686*593dc095SDavid du Colombier     /* Finish handling a CIE-based color space (Calxxx or Lab). */
6873ff48bf5SDavid du Colombier     if (code < 0)
6883ff48bf5SDavid du Colombier 	return code;
689*593dc095SDavid du Colombier     code = pdf_finish_cie_space(pca, pcd, pciec);
6903ff48bf5SDavid du Colombier     break;
691*593dc095SDavid du Colombier 
6923ff48bf5SDavid du Colombier     case gs_color_space_index_CIEABC: {
6933ff48bf5SDavid du Colombier 	/* Check that we can represent this as a CalRGB space. */
6943ff48bf5SDavid du Colombier 	const gs_cie_abc *pcie = pcs->params.abc;
695*593dc095SDavid du Colombier 	bool unitary = cie_ranges_are_0_1(pcie->RangeABC.ranges, 3);
6963ff48bf5SDavid du Colombier 	gs_vector3 expts;
697*593dc095SDavid du Colombier 	const gs_matrix3 *pmat = NULL;
698*593dc095SDavid du Colombier 	cie_cache_one_step_t one_step =
699*593dc095SDavid du Colombier 	    cie_cached_abc_is_one_step(pcie, &pmat);
7003ff48bf5SDavid du Colombier 
7013ff48bf5SDavid du Colombier 	pciec = (const gs_cie_common *)pcie;
702*593dc095SDavid du Colombier 	if (unitary) {
703*593dc095SDavid du Colombier 	    switch (one_step) {
704*593dc095SDavid du Colombier 	    case ONE_STEP_ABC:
705*593dc095SDavid du Colombier 		if (CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pcie->caches.DecodeABC.caches, expts))
706*593dc095SDavid du Colombier 		    goto calrgb;
707*593dc095SDavid du Colombier 		break;
708*593dc095SDavid du Colombier 	    case ONE_STEP_LMN:
709*593dc095SDavid du Colombier 		if (CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts))
710*593dc095SDavid du Colombier 		    goto calrgb;
711*593dc095SDavid du Colombier 	    default:
712*593dc095SDavid du Colombier 		break;
713*593dc095SDavid du Colombier 	    }
714*593dc095SDavid du Colombier 	}
715*593dc095SDavid du Colombier 	if (cie_is_lab(pcie)) {
716*593dc095SDavid du Colombier 	    /* Represent this as a Lab space. */
717*593dc095SDavid du Colombier 	    pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
718*593dc095SDavid du Colombier 	    if (pcd == 0)
719*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
720*593dc095SDavid du Colombier 	    code = pdf_put_lab_color_space(pca, pcd, pcie->RangeABC.ranges);
721*593dc095SDavid du Colombier 	    goto cal;
722*593dc095SDavid du Colombier 	} else {
723*593dc095SDavid du Colombier 	    code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ", pciec,
724*593dc095SDavid du Colombier 					 pcie->RangeABC.ranges,
725*593dc095SDavid du Colombier 					 one_step, pmat, &ranges);
726*593dc095SDavid du Colombier 	    break;
727*593dc095SDavid du Colombier 	}
728*593dc095SDavid du Colombier     calrgb:
7293ff48bf5SDavid du Colombier 	code = cos_array_add(pca, cos_c_string_value(&v, "/CalRGB"));
7303ff48bf5SDavid du Colombier 	if (code < 0)
7313ff48bf5SDavid du Colombier 	    return code;
7323ff48bf5SDavid du Colombier 	pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
7333ff48bf5SDavid du Colombier 	if (pcd == 0)
7343ff48bf5SDavid du Colombier 	    return_error(gs_error_VMerror);
7353ff48bf5SDavid du Colombier 	if (expts.u != 1 || expts.v != 1 || expts.w != 1) {
7363ff48bf5SDavid du Colombier 	    code = cos_dict_put_c_key_vector3(pcd, "/Gamma", &expts);
7373ff48bf5SDavid du Colombier 	    if (code < 0)
7383ff48bf5SDavid du Colombier 		return code;
7393ff48bf5SDavid du Colombier 	}
7403ff48bf5SDavid du Colombier 	if (!pmat->is_identity) {
7413ff48bf5SDavid du Colombier 	    cos_array_t *pcma =
7423ff48bf5SDavid du Colombier 		cos_array_alloc(pdev, "pdf_color_space(Matrix)");
7433ff48bf5SDavid du Colombier 
7443ff48bf5SDavid du Colombier 	    if (pcma == 0)
7453ff48bf5SDavid du Colombier 		return_error(gs_error_VMerror);
7463ff48bf5SDavid du Colombier 	    if ((code = cos_array_add_vector3(pcma, &pmat->cu)) < 0 ||
7473ff48bf5SDavid du Colombier 		(code = cos_array_add_vector3(pcma, &pmat->cv)) < 0 ||
7483ff48bf5SDavid du Colombier 		(code = cos_array_add_vector3(pcma, &pmat->cw)) < 0 ||
7493ff48bf5SDavid du Colombier 		(code = cos_dict_put(pcd, (const byte *)"/Matrix", 7,
7503ff48bf5SDavid du Colombier 				     COS_OBJECT_VALUE(&v, pcma))) < 0
7513ff48bf5SDavid du Colombier 		)
7523ff48bf5SDavid du Colombier 		return code;
7533ff48bf5SDavid du Colombier 	}
7543ff48bf5SDavid du Colombier     }
7553ff48bf5SDavid du Colombier     goto cal;
756*593dc095SDavid du Colombier 
7573ff48bf5SDavid du Colombier     case gs_color_space_index_CIEDEF:
758*593dc095SDavid du Colombier 	code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ",
759*593dc095SDavid du Colombier 				     (const gs_cie_common *)pcs->params.def,
760*593dc095SDavid du Colombier 				     pcs->params.def->RangeDEF.ranges,
761*593dc095SDavid du Colombier 				     ONE_STEP_NOT, NULL, &ranges);
762*593dc095SDavid du Colombier 	break;
763*593dc095SDavid du Colombier 
7643ff48bf5SDavid du Colombier     case gs_color_space_index_CIEDEFG:
765*593dc095SDavid du Colombier 	code = pdf_convert_cie_space(pdev, pca, pcs, "CMYK",
766*593dc095SDavid du Colombier 				     (const gs_cie_common *)pcs->params.defg,
767*593dc095SDavid du Colombier 				     pcs->params.defg->RangeDEFG.ranges,
768*593dc095SDavid du Colombier 				     ONE_STEP_NOT, NULL, &ranges);
7693ff48bf5SDavid du Colombier 	break;
770*593dc095SDavid du Colombier 
771*593dc095SDavid du Colombier     case gs_color_space_index_Indexed:
772*593dc095SDavid du Colombier 	code = pdf_indexed_color_space(pdev, pvalue, pcs, pca);
7733ff48bf5SDavid du Colombier 	break;
774*593dc095SDavid du Colombier 
7753ff48bf5SDavid du Colombier     case gs_color_space_index_DeviceN:
776*593dc095SDavid du Colombier         if (pdev->CompatibilityLevel < 1.3)
777*593dc095SDavid du Colombier 	    return_error(gs_error_rangecheck);
7783ff48bf5SDavid du Colombier 	pfn = gs_cspace_get_devn_function(pcs);
7793ff48bf5SDavid du Colombier 	/****** CURRENTLY WE ONLY HANDLE Functions ******/
7803ff48bf5SDavid du Colombier 	if (pfn == 0)
7813ff48bf5SDavid du Colombier 	    return_error(gs_error_rangecheck);
7823ff48bf5SDavid du Colombier 	{
7833ff48bf5SDavid du Colombier 	    cos_array_t *psna =
7843ff48bf5SDavid du Colombier 		cos_array_alloc(pdev, "pdf_color_space(DeviceN)");
7853ff48bf5SDavid du Colombier 	    int i;
786*593dc095SDavid du Colombier 	    byte *name_string;
787*593dc095SDavid du Colombier 	    uint name_string_length;
7883ff48bf5SDavid du Colombier 
7893ff48bf5SDavid du Colombier 	    if (psna == 0)
7903ff48bf5SDavid du Colombier 		return_error(gs_error_VMerror);
7913ff48bf5SDavid du Colombier 	    for (i = 0; i < pcs->params.device_n.num_components; ++i) {
792*593dc095SDavid du Colombier 	 	if ((code = pcs->params.device_n.get_colorname_string(
793*593dc095SDavid du Colombier 				  pdev->memory,
794*593dc095SDavid du Colombier 				  pcs->params.device_n.names[i], &name_string,
795*593dc095SDavid du Colombier 				  &name_string_length)) < 0 ||
796*593dc095SDavid du Colombier 		    (code = pdf_string_to_cos_name(pdev, name_string,
797*593dc095SDavid du Colombier 				  name_string_length, &v)) < 0 ||
7983ff48bf5SDavid du Colombier 		    (code = cos_array_add_no_copy(psna, &v)) < 0)
7993ff48bf5SDavid du Colombier 		    return code;
8003ff48bf5SDavid du Colombier 	    }
8013ff48bf5SDavid du Colombier 	    COS_OBJECT_VALUE(&v, psna);
8023ff48bf5SDavid du Colombier 	    if ((code = pdf_separation_color_space(pdev, pca, "/DeviceN", &v,
8033ff48bf5SDavid du Colombier 						   (const gs_color_space *)
8043ff48bf5SDavid du Colombier 					&pcs->params.device_n.alt_space,
8053ff48bf5SDavid du Colombier 					pfn, &pdf_color_space_names)) < 0)
8063ff48bf5SDavid du Colombier 		return code;
8073ff48bf5SDavid du Colombier 	}
8083ff48bf5SDavid du Colombier 	break;
809*593dc095SDavid du Colombier 
8103ff48bf5SDavid du Colombier     case gs_color_space_index_Separation:
8113ff48bf5SDavid du Colombier 	pfn = gs_cspace_get_sepr_function(pcs);
8123ff48bf5SDavid du Colombier 	/****** CURRENTLY WE ONLY HANDLE Functions ******/
8133ff48bf5SDavid du Colombier 	if (pfn == 0)
8143ff48bf5SDavid du Colombier 	    return_error(gs_error_rangecheck);
815*593dc095SDavid du Colombier 	{
816*593dc095SDavid du Colombier 	    byte *name_string;
817*593dc095SDavid du Colombier 	    uint name_string_length;
818*593dc095SDavid du Colombier 	    if ((code = pcs->params.separation.get_colorname_string(
819*593dc095SDavid du Colombier 				  pdev->memory,
820*593dc095SDavid du Colombier 				  pcs->params.separation.sep_name, &name_string,
821*593dc095SDavid du Colombier 				  &name_string_length)) < 0 ||
822*593dc095SDavid du Colombier 		(code = pdf_string_to_cos_name(pdev, name_string,
823*593dc095SDavid du Colombier 				      name_string_length, &v)) < 0 ||
8243ff48bf5SDavid du Colombier 		(code = pdf_separation_color_space(pdev, pca, "/Separation", &v,
8253ff48bf5SDavid du Colombier 						   (const gs_color_space *)
8263ff48bf5SDavid du Colombier 					    &pcs->params.separation.alt_space,
8273ff48bf5SDavid du Colombier 					    pfn, &pdf_color_space_names)) < 0)
8283ff48bf5SDavid du Colombier 		return code;
829*593dc095SDavid du Colombier 	}
8303ff48bf5SDavid du Colombier 	break;
831*593dc095SDavid du Colombier 
8323ff48bf5SDavid du Colombier     case gs_color_space_index_Pattern:
833*593dc095SDavid du Colombier 	if ((code = pdf_color_space(pdev, pvalue, ppranges,
8343ff48bf5SDavid du Colombier 				    (const gs_color_space *)
8353ff48bf5SDavid du Colombier 				    &pcs->params.pattern.base_space,
8363ff48bf5SDavid du Colombier 				    &pdf_color_space_names, false)) < 0 ||
8373ff48bf5SDavid du Colombier 	    (code = cos_array_add(pca,
8383ff48bf5SDavid du Colombier 				  cos_c_string_value(&v, "/Pattern"))) < 0 ||
8393ff48bf5SDavid du Colombier 	    (code = cos_array_add(pca, pvalue)) < 0
8403ff48bf5SDavid du Colombier 	    )
8413ff48bf5SDavid du Colombier 	    return code;
8423ff48bf5SDavid du Colombier 	break;
843*593dc095SDavid du Colombier 
8443ff48bf5SDavid du Colombier     default:
8453ff48bf5SDavid du Colombier 	return_error(gs_error_rangecheck);
8463ff48bf5SDavid du Colombier     }
8473ff48bf5SDavid du Colombier     /*
8483ff48bf5SDavid du Colombier      * Register the color space as a resource, since it must be referenced
8493ff48bf5SDavid du Colombier      * by name rather than directly.
8503ff48bf5SDavid du Colombier      */
8513ff48bf5SDavid du Colombier     {
852*593dc095SDavid du Colombier 	pdf_color_space_t *ppcs;
8533ff48bf5SDavid du Colombier 
854*593dc095SDavid du Colombier 	if (code < 0 ||
855*593dc095SDavid du Colombier 	    (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
856*593dc095SDavid du Colombier 				       &pres, -1)) < 0
857*593dc095SDavid du Colombier 	    ) {
8583ff48bf5SDavid du Colombier 	    COS_FREE(pca, "pdf_color_space");
8593ff48bf5SDavid du Colombier 	    return code;
8603ff48bf5SDavid du Colombier 	}
861*593dc095SDavid du Colombier 	pdf_reserve_object_id(pdev, pres, 0);
862*593dc095SDavid du Colombier 	if (res_name != NULL) {
863*593dc095SDavid du Colombier 	    int l = min(name_length, sizeof(pres->rname) - 1);
864*593dc095SDavid du Colombier 
865*593dc095SDavid du Colombier 	    memcpy(pres->rname, res_name, l);
866*593dc095SDavid du Colombier 	    pres->rname[l] = 0;
867*593dc095SDavid du Colombier 	}
868*593dc095SDavid du Colombier 	ppcs = (pdf_color_space_t *)pres;
869*593dc095SDavid du Colombier 	if (serialized == serialized0) {
870*593dc095SDavid du Colombier 	    serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space");
871*593dc095SDavid du Colombier 	    if (serialized == NULL)
872*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
873*593dc095SDavid du Colombier 	    memcpy(serialized, serialized0, serialized_size);
874*593dc095SDavid du Colombier 	}
875*593dc095SDavid du Colombier 	ppcs->serialized = serialized;
876*593dc095SDavid du Colombier 	ppcs->serialized_size = serialized_size;
877*593dc095SDavid du Colombier 	if (ranges) {
878*593dc095SDavid du Colombier 	    int num_comp = gs_color_space_num_components(pcs);
879*593dc095SDavid du Colombier 	    gs_range_t *copy_ranges = (gs_range_t *)
880*593dc095SDavid du Colombier 		gs_alloc_byte_array(pdev->pdf_memory, num_comp,
881*593dc095SDavid du Colombier 				    sizeof(gs_range_t), "pdf_color_space");
882*593dc095SDavid du Colombier 
883*593dc095SDavid du Colombier 	    if (copy_ranges == 0) {
884*593dc095SDavid du Colombier 		COS_FREE(pca, "pdf_color_space");
885*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
886*593dc095SDavid du Colombier 	    }
887*593dc095SDavid du Colombier 	    memcpy(copy_ranges, ranges, num_comp * sizeof(gs_range_t));
888*593dc095SDavid du Colombier 	    ppcs->ranges = copy_ranges;
889*593dc095SDavid du Colombier 	    if (ppranges)
890*593dc095SDavid du Colombier 		*ppranges = copy_ranges;
891*593dc095SDavid du Colombier 	} else
892*593dc095SDavid du Colombier 	    ppcs->ranges = 0;
8933ff48bf5SDavid du Colombier 	pca->id = pres->object->id;
8943ff48bf5SDavid du Colombier 	COS_FREE(pres->object, "pdf_color_space");
8953ff48bf5SDavid du Colombier 	pres->object = (cos_object_t *)pca;
8963ff48bf5SDavid du Colombier 	cos_write_object(COS_OBJECT(pca), pdev);
8973ff48bf5SDavid du Colombier     }
898*593dc095SDavid du Colombier  ret:
8993ff48bf5SDavid du Colombier     if (by_name) {
9003ff48bf5SDavid du Colombier 	/* Return a resource name rather than an object reference. */
9013ff48bf5SDavid du Colombier 	discard(COS_RESOURCE_VALUE(pvalue, pca));
9023ff48bf5SDavid du Colombier     } else
9033ff48bf5SDavid du Colombier 	discard(COS_OBJECT_VALUE(pvalue, pca));
904*593dc095SDavid du Colombier     if (pres != NULL) {
905*593dc095SDavid du Colombier 	pres->where_used |= pdev->used_mask;
906*593dc095SDavid du Colombier 	code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
907*593dc095SDavid du Colombier 	if (code < 0)
908*593dc095SDavid du Colombier 	    return code;
909*593dc095SDavid du Colombier     }
9103ff48bf5SDavid du Colombier     return 0;
9113ff48bf5SDavid du Colombier }
9123ff48bf5SDavid du Colombier 
913*593dc095SDavid du Colombier int
pdf_color_space(gx_device_pdf * pdev,cos_value_t * pvalue,const gs_range_t ** ppranges,const gs_color_space * pcs,const pdf_color_space_names_t * pcsn,bool by_name)914*593dc095SDavid du Colombier pdf_color_space(gx_device_pdf *pdev, cos_value_t *pvalue,
915*593dc095SDavid du Colombier 		const gs_range_t **ppranges,
916*593dc095SDavid du Colombier 		const gs_color_space *pcs,
917*593dc095SDavid du Colombier 		const pdf_color_space_names_t *pcsn,
918*593dc095SDavid du Colombier 		bool by_name)
919*593dc095SDavid du Colombier {
920*593dc095SDavid du Colombier     return pdf_color_space_named(pdev, pvalue, ppranges, pcs, pcsn, by_name, NULL, 0);
921*593dc095SDavid du Colombier }
922*593dc095SDavid du Colombier 
923*593dc095SDavid du Colombier /* ---------------- Miscellaneous ---------------- */
924*593dc095SDavid du Colombier 
9253ff48bf5SDavid du Colombier /* Create colored and uncolored Pattern color spaces. */
9263ff48bf5SDavid du Colombier private int
pdf_pattern_space(gx_device_pdf * pdev,cos_value_t * pvalue,pdf_resource_t ** ppres,const char * cs_name)9273ff48bf5SDavid du Colombier pdf_pattern_space(gx_device_pdf *pdev, cos_value_t *pvalue,
9283ff48bf5SDavid du Colombier 		  pdf_resource_t **ppres, const char *cs_name)
9293ff48bf5SDavid du Colombier {
930*593dc095SDavid du Colombier     int code;
931*593dc095SDavid du Colombier 
9323ff48bf5SDavid du Colombier     if (!*ppres) {
9333ff48bf5SDavid du Colombier 	int code = pdf_begin_resource_body(pdev, resourceColorSpace, gs_no_id,
9343ff48bf5SDavid du Colombier 					   ppres);
9353ff48bf5SDavid du Colombier 
9363ff48bf5SDavid du Colombier 	if (code < 0)
9373ff48bf5SDavid du Colombier 	    return code;
9383ff48bf5SDavid du Colombier 	pprints1(pdev->strm, "%s\n", cs_name);
9393ff48bf5SDavid du Colombier 	pdf_end_resource(pdev);
9403ff48bf5SDavid du Colombier 	(*ppres)->object->written = true; /* don't write at end */
941*593dc095SDavid du Colombier 	((pdf_color_space_t *)*ppres)->ranges = 0;
942*593dc095SDavid du Colombier 	((pdf_color_space_t *)*ppres)->serialized = 0;
9433ff48bf5SDavid du Colombier     }
944*593dc095SDavid du Colombier     code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", *ppres);
945*593dc095SDavid du Colombier     if (code < 0)
946*593dc095SDavid du Colombier 	return code;
9473ff48bf5SDavid du Colombier     cos_resource_value(pvalue, (*ppres)->object);
9483ff48bf5SDavid du Colombier     return 0;
9493ff48bf5SDavid du Colombier }
9503ff48bf5SDavid du Colombier int
pdf_cs_Pattern_colored(gx_device_pdf * pdev,cos_value_t * pvalue)9513ff48bf5SDavid du Colombier pdf_cs_Pattern_colored(gx_device_pdf *pdev, cos_value_t *pvalue)
9523ff48bf5SDavid du Colombier {
9533ff48bf5SDavid du Colombier     return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[0],
9543ff48bf5SDavid du Colombier 			     "[/Pattern]");
9553ff48bf5SDavid du Colombier }
9563ff48bf5SDavid du Colombier int
pdf_cs_Pattern_uncolored(gx_device_pdf * pdev,cos_value_t * pvalue)9573ff48bf5SDavid du Colombier pdf_cs_Pattern_uncolored(gx_device_pdf *pdev, cos_value_t *pvalue)
9583ff48bf5SDavid du Colombier {
959*593dc095SDavid du Colombier     /* Only for process colors. */
9603ff48bf5SDavid du Colombier     int ncomp = pdev->color_info.num_components;
9613ff48bf5SDavid du Colombier     static const char *const pcs_names[5] = {
9623ff48bf5SDavid du Colombier 	0, "[/Pattern /DeviceGray]", 0, "[/Pattern /DeviceRGB]",
9633ff48bf5SDavid du Colombier 	"[/Pattern /DeviceCMYK]"
9643ff48bf5SDavid du Colombier     };
9653ff48bf5SDavid du Colombier 
9663ff48bf5SDavid du Colombier     return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[ncomp],
9673ff48bf5SDavid du Colombier 			     pcs_names[ncomp]);
9683ff48bf5SDavid du Colombier }
969*593dc095SDavid du Colombier int
pdf_cs_Pattern_uncolored_hl(gx_device_pdf * pdev,const gs_color_space * pcs,cos_value_t * pvalue)970*593dc095SDavid du Colombier pdf_cs_Pattern_uncolored_hl(gx_device_pdf *pdev,
971*593dc095SDavid du Colombier 		const gs_color_space *pcs, cos_value_t *pvalue)
972*593dc095SDavid du Colombier {
973*593dc095SDavid du Colombier     /* Only for high level colors. */
974*593dc095SDavid du Colombier     return pdf_color_space(pdev, pvalue, NULL, pcs, &pdf_color_space_names, true);
975*593dc095SDavid du Colombier }
9763ff48bf5SDavid du Colombier 
9773ff48bf5SDavid du Colombier /* Set the ProcSets bits corresponding to an image color space. */
9783ff48bf5SDavid du Colombier void
pdf_color_space_procsets(gx_device_pdf * pdev,const gs_color_space * pcs)9793ff48bf5SDavid du Colombier pdf_color_space_procsets(gx_device_pdf *pdev, const gs_color_space *pcs)
9803ff48bf5SDavid du Colombier {
9813ff48bf5SDavid du Colombier     const gs_color_space *pbcs = pcs;
9823ff48bf5SDavid du Colombier 
9833ff48bf5SDavid du Colombier  csw:
9843ff48bf5SDavid du Colombier     switch (gs_color_space_get_index(pbcs)) {
9853ff48bf5SDavid du Colombier     case gs_color_space_index_DeviceGray:
9863ff48bf5SDavid du Colombier     case gs_color_space_index_CIEA:
9873ff48bf5SDavid du Colombier 	/* We only handle CIEBasedA spaces that map to CalGray. */
9883ff48bf5SDavid du Colombier 	pdev->procsets |= ImageB;
9893ff48bf5SDavid du Colombier 	break;
9903ff48bf5SDavid du Colombier     case gs_color_space_index_Indexed:
9913ff48bf5SDavid du Colombier 	pdev->procsets |= ImageI;
9923ff48bf5SDavid du Colombier 	pbcs = (const gs_color_space *)&pcs->params.indexed.base_space;
9933ff48bf5SDavid du Colombier 	goto csw;
9943ff48bf5SDavid du Colombier     default:
9953ff48bf5SDavid du Colombier 	pdev->procsets |= ImageC;
9963ff48bf5SDavid du Colombier 	break;
9973ff48bf5SDavid du Colombier     }
9983ff48bf5SDavid du Colombier }
999