xref: /plan9/sys/src/cmd/gs/src/gdevpdfv.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevpdfv.c,v 1.39 2005/06/20 08:59:23 igor Exp $ */
18 /* Color value writing for pdfwrite driver */
19 #include "math_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gscindex.h"
23 #include "gserrors.h"
24 #include "gsiparm3.h"		/* for pattern colors */
25 #include "gsmatrix.h"		/* for gspcolor.h */
26 #include "gscoord.h"		/* for gs_currentmatrix, requires gsmatrix.h */
27 #include "gsptype2.h"
28 #include "gxcolor2.h"		/* for gxpcolor.h */
29 #include "gxdcolor.h"		/* for gxpcolor.h */
30 #include "gxpcolor.h"		/* for pattern device color types */
31 #include "gxshade.h"
32 #include "gdevpdfx.h"
33 #include "gdevpdfg.h"
34 #include "gdevpdfo.h"
35 #include "szlibx.h"
36 
37 /* Import the PatternType 2 Pattern device color type. */
38 extern const gx_device_color_type_t gx_dc_pattern2;
39 
40 /*
41  * Define the scaling and range of values written for mesh shadings.
42  * BitsPerCoordinate is always 24; BitsPerComponent (for colors) is
43  * always 16.
44  */
45 #define ENCODE_VALUE(v, emax, vmin, vmax)\
46   ( ((v) - (vmin)) * ((double)(emax) / ((vmax) - (vmin))) )
47 /*
48  * Because of the Acrobat Reader limitation noted in gdevpdfx.h,
49  * we must limit coordinate values to 14 bits.
50  */
51 #define MIN_MESH_COORDINATE (-0x400000 / 256.0)
52 #define MAX_MESH_COORDINATE ( 0x3fffff / 256.0)
53 #define ENCODE_MESH_COORDINATE(v)\
54   ENCODE_VALUE(v, 0xffffff, MIN_MESH_COORDINATE, MAX_MESH_COORDINATE)
55 
56 #define MIN_MESH_COLOR_INDEX 0
57 #define MAX_MESH_COLOR_INDEX 0xffff
58 #define ENCODE_MESH_COLOR_INDEX(v) ((v) + MIN_MESH_COLOR_INDEX)
59 
60 #define ENCODE_MESH_COMPONENT(v, vmin, vmax)\
61   ENCODE_VALUE(v, 0xffff, vmin, vmax)
62 
63 /* ---------------- Utilities ---------------- */
64 
65 /* Write a matrix parameter. */
66 private int
cos_dict_put_matrix(cos_dict_t * pscd,const char * key,const gs_matrix * pmat)67 cos_dict_put_matrix(cos_dict_t *pscd, const char *key, const gs_matrix *pmat)
68 {
69     float matrix[6];
70 
71     matrix[0] = pmat->xx;
72     matrix[1] = pmat->xy;
73     matrix[2] = pmat->yx;
74     matrix[3] = pmat->yy;
75     matrix[4] = pmat->tx;
76     matrix[5] = pmat->ty;
77     return cos_dict_put_c_key_floats(pscd, key, matrix, 6);
78 }
79 
80 /* ---------------- PatternType 1 colors ---------------- */
81 
82 /*
83  * Create a Pattern resource referencing an image (currently only an XObject).
84  * p_tile is NULL for uncolored patterns or the NULL pattern.
85  * m_tile is NULL for colored patterns that fill their bounding box,
86  * including the NULL pattern.
87  ****** WE DON'T HANDLE NULL PATTERNS YET ******
88  */
89 private uint
tile_size(const gx_strip_bitmap * tile,int depth)90 tile_size(const gx_strip_bitmap *tile, int depth)
91 {
92     return (tile->rep_width * depth + 7) / 8 * tile->rep_height;
93 }
94 private bool
tile_size_ok(const gx_device_pdf * pdev,const gx_color_tile * p_tile,const gx_color_tile * m_tile)95 tile_size_ok(const gx_device_pdf *pdev, const gx_color_tile *p_tile,
96 	     const gx_color_tile *m_tile)
97 {
98     /*
99      * Acrobat Reader can't handle image Patterns with more than
100      * 64K of data.  :-(
101      */
102     uint p_size =
103 	(p_tile == 0 ? 0 : tile_size(&p_tile->tbits, p_tile->depth));
104     uint m_size =
105 	(m_tile == 0 ? 0 : tile_size(&m_tile->tmask, 1));
106     return (max(p_size, m_size) <= 65500);
107 }
108 
109 private int
pdf_pattern(gx_device_pdf * pdev,const gx_drawing_color * pdc,const gx_color_tile * p_tile,const gx_color_tile * m_tile,cos_stream_t * pcs_image,pdf_resource_t ** ppres)110 pdf_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
111 	    const gx_color_tile *p_tile, const gx_color_tile *m_tile,
112 	    cos_stream_t *pcs_image, pdf_resource_t **ppres)
113 {
114     pdf_resource_t *pres;
115     int code = pdf_alloc_resource(pdev, resourcePattern, pdc->mask.id, ppres,
116 				  0L);
117     cos_stream_t *pcos;
118     cos_dict_t *pcd;
119     cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
120     const gx_color_tile *tile = (p_tile ? p_tile : m_tile);
121     const gx_strip_bitmap *btile = (p_tile ? &p_tile->tbits : &m_tile->tmask);
122     bool mask = p_tile == 0;
123     gs_point step;
124     gs_matrix smat;
125 
126     if (code < 0)
127 	return code;
128     if (!tile_size_ok(pdev, p_tile, m_tile))
129 	return_error(gs_error_limitcheck);
130     /*
131      * We currently can't handle Patterns whose X/Y step isn't parallel
132      * to the coordinate axes.
133      */
134     if (is_xxyy(&tile->step_matrix))
135 	step.x = tile->step_matrix.xx, step.y = tile->step_matrix.yy;
136     else if (is_xyyx(&tile->step_matrix))
137 	step.x = tile->step_matrix.yx, step.y = tile->step_matrix.xy;
138     else
139 	return_error(gs_error_rangecheck);
140     if (pcd_Resources == 0)
141 	return_error(gs_error_VMerror);
142     gs_make_identity(&smat);
143     smat.xx = btile->rep_width / (pdev->HWResolution[0] / 72.0);
144     smat.yy = btile->rep_height / (pdev->HWResolution[1] / 72.0);
145     smat.tx = tile->step_matrix.tx / (pdev->HWResolution[0] / 72.0);
146     smat.ty = tile->step_matrix.ty / (pdev->HWResolution[1] / 72.0);
147     pres = *ppres;
148     {
149 	cos_dict_t *pcd_XObject = cos_dict_alloc(pdev, "pdf_pattern(XObject)");
150 	char key[MAX_REF_CHARS + 3];
151 	cos_value_t v;
152 
153 	if (pcd_XObject == 0)
154 	    return_error(gs_error_VMerror);
155 	sprintf(key, "/R%ld", pcs_image->id);
156 	COS_OBJECT_VALUE(&v, pcs_image);
157 	if ((code = cos_dict_put(pcd_XObject, (byte *)key, strlen(key), &v)) < 0 ||
158 	    (code = cos_dict_put_c_key_object(pcd_Resources, "/XObject",
159 					      COS_OBJECT(pcd_XObject))) < 0
160 	    )
161 	    return code;
162     }
163     if ((code = cos_dict_put_c_strings(pcd_Resources, "/ProcSet",
164 				       (mask ? "[/PDF/ImageB]" :
165 					"[/PDF/ImageC]"))) < 0)
166 	return code;
167     cos_become(pres->object, cos_type_stream);
168     pcos = (cos_stream_t *)pres->object;
169     pcd = cos_stream_dict(pcos);
170     if ((code = cos_dict_put_c_key_int(pcd, "/PatternType", 1)) < 0 ||
171 	(code = cos_dict_put_c_key_int(pcd, "/PaintType",
172 				       (mask ? 2 : 1))) < 0 ||
173 	(code = cos_dict_put_c_key_int(pcd, "/TilingType",
174 				       tile->tiling_type)) < 0 ||
175 	(code = cos_dict_put_c_key_object(pcd, "/Resources",
176 					  COS_OBJECT(pcd_Resources))) < 0 ||
177 	(code = cos_dict_put_c_strings(pcd, "/BBox", "[0 0 1 1]")) < 0 ||
178 	(code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0 ||
179 	(code = cos_dict_put_c_key_real(pcd, "/XStep", step.x / btile->rep_width)) < 0 ||
180 	(code = cos_dict_put_c_key_real(pcd, "/YStep", step.y / btile->rep_height)) < 0
181 	) {
182 	return code;
183     }
184 
185     {
186 	char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */
187 
188 	sprintf(buf, "/R%ld Do\n", pcs_image->id);
189 	cos_stream_add_bytes(pcos, (const byte *)buf, strlen(buf));
190     }
191 
192     return 0;
193 }
194 
195 /* Store pattern 1 parameters to cos dictionary. */
196 int
pdf_store_pattern1_params(gx_device_pdf * pdev,pdf_resource_t * pres,gs_pattern1_instance_t * pinst)197 pdf_store_pattern1_params(gx_device_pdf *pdev, pdf_resource_t *pres,
198 			gs_pattern1_instance_t *pinst)
199 {
200     gs_pattern1_template_t *t = &pinst->template;
201     gs_matrix smat = ctm_only((gs_imager_state *)pinst->saved);
202     double scale_x = pdev->HWResolution[0] / 72.0;
203     double scale_y = pdev->HWResolution[1] / 72.0;
204     cos_dict_t *pcd = cos_stream_dict((cos_stream_t *)pres->object);
205     cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
206     char buf[60];
207     int code;
208 
209     if (pcd == NULL || pcd_Resources == NULL)
210 	return_error(gs_error_VMerror);
211     pdev->substream_Resources = pcd_Resources;
212     sprintf(buf, "[%g %g %g %g]", t->BBox.p.x, t->BBox.p.y,
213 				  t->BBox.q.x, t->BBox.q.y);
214     /* The graphics library assumes a shifted origin to provide
215        positive bitmap pixel indices. Compensate it now. */
216     smat.tx += pinst->step_matrix.tx;
217     smat.ty += pinst->step_matrix.ty;
218     smat.xx /= scale_x;
219     smat.xy /= scale_x;
220     smat.yx /= scale_y;
221     smat.yy /= scale_y;
222     smat.tx /= scale_x;
223     smat.ty /= scale_y;
224     if (any_abs(smat.tx) < 0.0001)  /* Noise. */
225 	smat.tx = 0;
226     if (any_abs(smat.ty) < 0.0001)
227 	smat.ty = 0;
228     code = cos_dict_put_c_strings(pcd, "/Type", "/Pattern");
229     if (code >= 0)
230 	code = cos_dict_put_c_key_int(pcd, "/PatternType", 1);
231     if (code >= 0)
232 	code = cos_dict_put_c_key_int(pcd, "/PaintType", t->PaintType);
233     if (code >= 0)
234 	code = cos_dict_put_c_key_int(pcd, "/TilingType", t->TilingType);
235     if (code >= 0)
236 	code = cos_dict_put_string(pcd, (byte *)"/BBox", 5, (byte *)buf, strlen(buf));
237     if (code >= 0)
238 	code = cos_dict_put_matrix(pcd, "/Matrix", &smat);
239     if (code >= 0)
240 	code = cos_dict_put_c_key_real(pcd, "/XStep", t->XStep);
241     if (code >= 0)
242 	code = cos_dict_put_c_key_real(pcd, "/YStep", t->YStep);
243     if (code >= 0)
244 	code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
245     pdev->skip_colors = (t->PaintType == 2);
246     return code;
247 }
248 
249 /* Set the ImageMatrix, Width, and Height for a Pattern image. */
250 private void
pdf_set_pattern_image(gs_data_image_t * pic,const gx_strip_bitmap * tile)251 pdf_set_pattern_image(gs_data_image_t *pic, const gx_strip_bitmap *tile)
252 {
253     pic->ImageMatrix.xx = (float)(pic->Width = tile->rep_width);
254     pic->ImageMatrix.yy = (float)(pic->Height = tile->rep_height);
255 }
256 
257 /* Write the mask for a Pattern (colored or uncolored). */
258 private int
pdf_put_pattern_mask(gx_device_pdf * pdev,const gx_color_tile * m_tile,cos_stream_t ** ppcs_mask)259 pdf_put_pattern_mask(gx_device_pdf *pdev, const gx_color_tile *m_tile,
260 		     cos_stream_t **ppcs_mask)
261 {
262     int w = m_tile->tmask.rep_width, h = m_tile->tmask.rep_height;
263     gs_image1_t image;
264     pdf_image_writer writer;
265     int code;
266 
267     gs_image_t_init_mask_adjust(&image, true, false);
268     pdf_set_pattern_image((gs_data_image_t *)&image, &m_tile->tmask);
269     pdf_image_writer_init(&writer);
270     if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
271 	(pdev->params.MonoImage.Encode &&
272 	 (code = psdf_CFE_binary(&writer.binary[0], w, h, true)) < 0) ||
273 	(code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, NULL, 0)) < 0
274 	)
275 	return code;
276     /* Pattern masks are specified in device coordinates, so invert Y. */
277     if ((code = pdf_copy_mask_bits(writer.binary[0].strm, m_tile->tmask.data + (h - 1) * m_tile->tmask.raster, 0, -m_tile->tmask.raster, w, h, 0)) < 0 ||
278 	(code = pdf_end_image_binary(pdev, &writer, h)) < 0 ||
279 	(code = pdf_end_write_image(pdev, &writer)) < 0
280 	)
281 	return code;
282     *ppcs_mask = (cos_stream_t *)writer.pres->object;
283     return 0;
284 }
285 
286 /* Write an uncolored Pattern color. */
287 int
pdf_put_uncolored_pattern(gx_device_pdf * pdev,const gx_drawing_color * pdc,const gs_color_space * pcs,const psdf_set_color_commands_t * ppscc,bool have_pattern_streams,pdf_resource_t ** ppres)288 pdf_put_uncolored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
289 			  const gs_color_space *pcs,
290 			  const psdf_set_color_commands_t *ppscc,
291 			  bool have_pattern_streams, pdf_resource_t **ppres)
292 {
293     const gx_color_tile *m_tile = pdc->mask.m_tile;
294     gx_drawing_color dc_pure;
295 
296     if (!have_pattern_streams && m_tile == 0) {
297 	/*
298 	 * If m_tile == 0, this uncolored Pattern is all 1's,
299 	 * equivalent to a pure color.
300 	 */
301 	*ppres = 0;
302 	set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
303 	return psdf_set_color((gx_device_vector *)pdev, &dc_pure, ppscc);
304     } else {
305 	cos_value_t v;
306 	stream *s = pdev->strm;
307 	int code;
308 	cos_stream_t *pcs_image;
309 	static const psdf_set_color_commands_t no_scc = {0, 0, 0};
310 
311 	if (!tile_size_ok(pdev, NULL, m_tile))
312 	    return_error(gs_error_limitcheck);
313 	if (!have_pattern_streams) {
314 	    if ((code = pdf_cs_Pattern_uncolored(pdev, &v)) < 0 ||
315 		(code = pdf_put_pattern_mask(pdev, m_tile, &pcs_image)) < 0 ||
316 		(code = pdf_pattern(pdev, pdc, NULL, m_tile, pcs_image, ppres)) < 0
317 		)
318 		return code;
319 	} else {
320 	    code = pdf_cs_Pattern_uncolored_hl(pdev, pcs, &v);
321 	    if (code < 0)
322 		return code;
323 	    *ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, pdc->mask.id);
324 	    *ppres = pdf_substitute_pattern(*ppres);
325 	    if (!pdev->AR4_save_bug && pdev->CompatibilityLevel <= 1.3) {
326 		/* We reconnized AR4 behavior as reserving "q Q" stack elements
327 		 * on demand. It looks as processing a pattern stream
328 		 * with PaintType 1 AR4 replaces the topmost stack element
329 		 * instead allocating a new one, if it was not previousely allocated.
330 		 * AR 5 doesn't have this bug. Working around the AR4 bug here.
331 		 */
332 		stream_puts(pdev->strm, "q q Q Q\n");
333 		pdev->AR4_save_bug = true;
334 	    }
335 	}
336 	cos_value_write(&v, pdev);
337 	pprints1(s, " %s ", ppscc->setcolorspace);
338 	if (have_pattern_streams)
339 	    return 0;
340 	set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
341 	return psdf_set_color((gx_device_vector *)pdev, &dc_pure, &no_scc);
342     }
343 }
344 
345 int
pdf_put_colored_pattern(gx_device_pdf * pdev,const gx_drawing_color * pdc,const gs_color_space * pcs,const psdf_set_color_commands_t * ppscc,bool have_pattern_streams,pdf_resource_t ** ppres)346 pdf_put_colored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
347 			const gs_color_space *pcs,
348 			const psdf_set_color_commands_t *ppscc,
349 			bool have_pattern_streams, pdf_resource_t **ppres)
350 {
351     const gx_color_tile *p_tile = pdc->colors.pattern.p_tile;
352     gs_color_space cs_Device;
353     cos_value_t cs_value;
354     cos_value_t v;
355     int code;
356     gs_image1_t image;
357     const gx_color_tile *m_tile = NULL;
358     pdf_image_writer writer;
359     int w = p_tile->tbits.rep_width, h = p_tile->tbits.rep_height;
360 
361     if (!have_pattern_streams) {
362 	/*
363 	 * NOTE: We assume here that the color space of the cached Pattern
364 	 * is the same as the native color space of the device.  This will
365 	 * have to change in the future!
366 	 */
367 	/*
368 	 * Check whether this colored pattern is actually a masked pure color,
369 	 * by testing whether all the colored pixels have the same color.
370 	 */
371 	m_tile = pdc->mask.m_tile;
372 	if (m_tile) {
373 	    if (p_tile && !(p_tile->depth & 7) && p_tile->depth <= arch_sizeof_color_index * 8) {
374 		int depth_bytes = p_tile->depth >> 3;
375 		int width = p_tile->tbits.rep_width;
376 		int skip = p_tile->tbits.raster -
377 		    p_tile->tbits.rep_width * depth_bytes;
378 		const byte *bp;
379 		const byte *mp;
380 		int i, j, k;
381 		gx_color_index color = 0; /* init is arbitrary if not empty */
382 		bool first = true;
383 
384 		for (i = 0, bp = p_tile->tbits.data, mp = p_tile->tmask.data;
385 		     i < p_tile->tbits.rep_height;
386 		     ++i, bp += skip, mp += p_tile->tmask.raster) {
387 
388 		    for (j = 0; j < width; ++j) {
389 			if (mp[j >> 3] & (0x80 >> (j & 7))) {
390 			    gx_color_index ci = 0;
391 
392 			    for (k = 0; k < depth_bytes; ++k)
393 				ci = (ci << 8) + *bp++;
394 			    if (first)
395 				color = ci, first = false;
396 			    else if (ci != color)
397 				goto not_pure;
398 			} else
399 			    bp += depth_bytes;
400 		    }
401 		}
402 		{
403 		    /* Set the color, then handle as an uncolored pattern. */
404 		    gx_drawing_color dcolor;
405 
406 		    dcolor = *pdc;
407 		    dcolor.colors.pure = color;
408 		    return pdf_put_uncolored_pattern(pdev, &dcolor, pcs, ppscc,
409 				have_pattern_streams, ppres);
410 		}
411 	    not_pure:
412 		DO_NOTHING;		/* required by MSVC */
413 	    }
414 	    if (pdev->CompatibilityLevel < 1.3) {
415 		/* Masked images are only supported starting in PDF 1.3. */
416 		return_error(gs_error_rangecheck);
417 	    }
418 	}
419 	/* Acrobat Reader has a size limit for image Patterns. */
420 	if (!tile_size_ok(pdev, p_tile, m_tile))
421 	    return_error(gs_error_limitcheck);
422     }
423     code = pdf_cs_Pattern_colored(pdev, &v);
424     if (code < 0)
425 	return code;
426     pdf_cspace_init_Device(pdev->memory, &cs_Device, pdev->color_info.num_components);
427     /*
428      * We don't have to worry about color space scaling: the color
429      * space is always a Device space.
430      */
431     code = pdf_color_space(pdev, &cs_value, NULL, &cs_Device,
432 			   &pdf_color_space_names, true);
433     if (code < 0)
434 	return code;
435     if (!have_pattern_streams) {
436 	cos_stream_t *pcs_mask = 0;
437 	cos_stream_t *pcs_image;
438 
439 	gs_image_t_init_adjust(&image, &cs_Device, false);
440 	image.BitsPerComponent = 8;
441 	pdf_set_pattern_image((gs_data_image_t *)&image, &p_tile->tbits);
442 	if (m_tile) {
443 	    if ((code = pdf_put_pattern_mask(pdev, m_tile, &pcs_mask)) < 0)
444 		return code;
445 	}
446 	pdf_image_writer_init(&writer);
447 	if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
448 	    (code = psdf_setup_lossless_filters((gx_device_psdf *)pdev,
449 						&writer.binary[0],
450 						(gs_pixel_image_t *)&image)) < 0 ||
451 	    (code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, &cs_value, 0)) < 0
452 	    )
453 	    return code;
454 	/* Pattern masks are specified in device coordinates, so invert Y. */
455 	if ((code = pdf_copy_color_bits(writer.binary[0].strm, p_tile->tbits.data + (h - 1) * p_tile->tbits.raster, 0, -p_tile->tbits.raster, w, h, pdev->color_info.depth >> 3)) < 0 ||
456 	    (code = pdf_end_image_binary(pdev, &writer, h)) < 0
457 	    )
458 	    return code;
459 	pcs_image = (cos_stream_t *)writer.pres->object;
460 	if ((pcs_mask != 0 &&
461 	     (code = cos_dict_put_c_key_object(cos_stream_dict(pcs_image), "/Mask",
462 					       COS_OBJECT(pcs_mask))) < 0) ||
463 	    (code = pdf_end_write_image(pdev, &writer)) < 0
464 	    )
465 	    return code;
466 	pcs_image = (cos_stream_t *)writer.pres->object; /* pdf_end_write_image may change it. */
467 	code = pdf_pattern(pdev, pdc, p_tile, m_tile, pcs_image, ppres);
468 	if (code < 0)
469 	    return code;
470     } else {
471 	*ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, p_tile->id);
472 	*ppres = pdf_substitute_pattern(*ppres);
473     }
474     cos_value_write(&v, pdev);
475     pprints1(pdev->strm, " %s", ppscc->setcolorspace);
476     return 0;
477 }
478 
479 
480 /* ---------------- PatternType 2 colors ---------------- */
481 
482 /* Write parameters common to all Shadings. */
483 private int
pdf_put_shading_common(cos_dict_t * pscd,const gs_shading_t * psh,const gs_range_t ** ppranges)484 pdf_put_shading_common(cos_dict_t *pscd, const gs_shading_t *psh,
485 		       const gs_range_t **ppranges)
486 {
487     gs_shading_type_t type = ShadingType(psh);
488     const gs_color_space *pcs = psh->params.ColorSpace;
489     int code = cos_dict_put_c_key_int(pscd, "/ShadingType", (int)type);
490     cos_value_t cs_value;
491 
492     if (code < 0 ||
493 	(psh->params.AntiAlias &&
494 	 (code = cos_dict_put_c_strings(pscd, "/AntiAlias", "true")) < 0) ||
495 	(code = pdf_color_space(pscd->pdev, &cs_value, ppranges, pcs,
496 				&pdf_color_space_names, false)) < 0 ||
497 	(code = cos_dict_put_c_key(pscd, "/ColorSpace", &cs_value)) < 0
498 	)
499 	return code;
500     if (psh->params.Background) {
501 	/****** SCALE Background ******/
502 	code = cos_dict_put_c_key_floats(pscd, "/Background",
503 				   psh->params.Background->paint.values,
504 				   gs_color_space_num_components(pcs));
505 	if (code < 0)
506 	    return code;
507     }
508     if (psh->params.have_BBox) {
509 	float bbox[4];
510 
511 	bbox[0] = psh->params.BBox.p.x;
512 	bbox[1] = psh->params.BBox.p.y;
513 	bbox[2] = psh->params.BBox.q.x;
514 	bbox[3] = psh->params.BBox.q.y;
515 	code = cos_dict_put_c_key_floats(pscd, "/BBox", bbox, 4);
516 	if (code < 0)
517 	    return code;
518     }
519     return 0;
520 }
521 
522 /* Write an optional Function parameter. */
523 private int
pdf_put_shading_Function(cos_dict_t * pscd,const gs_function_t * pfn,const gs_range_t * pranges)524 pdf_put_shading_Function(cos_dict_t *pscd, const gs_function_t *pfn,
525 			 const gs_range_t *pranges)
526 {
527     int code = 0;
528 
529     if (pfn != 0) {
530 	cos_value_t fn_value;
531 
532 	if ((code = pdf_function_scaled(pscd->pdev, pfn, pranges, &fn_value)) >= 0)
533 	    code = cos_dict_put_c_key(pscd, "/Function", &fn_value);
534     }
535     return code;
536 }
537 
538 /* Write a linear (Axial / Radial) Shading. */
539 private int
pdf_put_linear_shading(cos_dict_t * pscd,const float * Coords,int num_coords,const float * Domain,const gs_function_t * Function,const bool * Extend,const gs_range_t * pranges)540 pdf_put_linear_shading(cos_dict_t *pscd, const float *Coords,
541 		       int num_coords, const float *Domain /*[2]*/,
542 		       const gs_function_t *Function,
543 		       const bool *Extend /*[2]*/,
544 		       const gs_range_t *pranges)
545 {
546     int code = cos_dict_put_c_key_floats(pscd, "/Coords", Coords, num_coords);
547 
548     if (code < 0 ||
549 	((Domain[0] != 0 || Domain[1] != 1) &&
550 	 (code = cos_dict_put_c_key_floats(pscd, "/Domain", Domain, 2)) < 0) ||
551 	(code = pdf_put_shading_Function(pscd, Function, pranges)) < 0
552 	)
553 	return code;
554     if (Extend[0] | Extend[1]) {
555 	char extend_str[1 + 5 + 1 + 5 + 1 + 1]; /* [bool bool] */
556 
557 	sprintf(extend_str, "[%s %s]",
558 		(Extend[0] ? "true" : "false"),
559 		(Extend[1] ? "true" : "false"));
560 	code = cos_dict_put_c_key_string(pscd, "/Extend",
561 					 (const byte *)extend_str,
562 					 strlen(extend_str));
563     }
564     return code;
565 }
566 
567 /* Write a scalar (non-mesh) Shading. */
568 /* (Single-use procedure for readability.) */
569 private int
pdf_put_scalar_shading(cos_dict_t * pscd,const gs_shading_t * psh,const gs_range_t * pranges)570 pdf_put_scalar_shading(cos_dict_t *pscd, const gs_shading_t *psh,
571 		       const gs_range_t *pranges)
572 {
573     int code;
574 
575     switch (ShadingType(psh)) {
576     case shading_type_Function_based: {
577 	const gs_shading_Fb_params_t *const params =
578 	    (const gs_shading_Fb_params_t *)&psh->params;
579 
580 	if ((code = cos_dict_put_c_key_floats(pscd, "/Domain", params->Domain, 4)) < 0 ||
581 	    (code = pdf_put_shading_Function(pscd, params->Function, pranges)) < 0 ||
582 	    (code = cos_dict_put_matrix(pscd, "/Matrix", &params->Matrix)) < 0
583 	    )
584 	    return code;
585 	return 0;
586     }
587     case shading_type_Axial: {
588 	const gs_shading_A_params_t *const params =
589 	    (const gs_shading_A_params_t *)&psh->params;
590 
591 	return pdf_put_linear_shading(pscd, params->Coords, 4,
592 				      params->Domain, params->Function,
593 				      params->Extend, pranges);
594     }
595     case shading_type_Radial: {
596 	const gs_shading_R_params_t *const params =
597 	    (const gs_shading_R_params_t *)&psh->params;
598 
599 	return pdf_put_linear_shading(pscd, params->Coords, 6,
600 				      params->Domain, params->Function,
601 				      params->Extend, pranges);
602     }
603     default:
604 	return_error(gs_error_rangecheck);
605     }
606 }
607 
608 /* Add a floating point range to an array. */
609 private int
pdf_array_add_real2(cos_array_t * pca,floatp lower,floatp upper)610 pdf_array_add_real2(cos_array_t *pca, floatp lower, floatp upper)
611 {
612     int code = cos_array_add_real(pca, lower);
613 
614     if (code >= 0)
615 	code = cos_array_add_real(pca, upper);
616     return code;
617 }
618 
619 /* Define a parameter structure for mesh data. */
620 typedef struct pdf_mesh_data_params_s {
621     int num_points;
622     int num_components;
623     bool is_indexed;
624     const float *Domain;	/* iff Function */
625     const gs_range_t *ranges;
626 } pdf_mesh_data_params_t;
627 
628 /* Put a clamped value into a data stream.  num_bytes < sizeof(int). */
629 private void
put_clamped(byte * p,floatp v,int num_bytes)630 put_clamped(byte *p, floatp v, int num_bytes)
631 {
632     int limit = 1 << (num_bytes * 8);
633     int i, shift;
634 
635     if (v <= -limit)
636 	i = -limit + 1;
637     else if (v >= limit)
638 	i = limit - 1;
639     else
640 	i = (int)v;
641     for (shift = (num_bytes - 1) * 8; shift >= 0; shift -= 8)
642 	*p++ = (byte)(i >> shift);
643 }
644 inline private void
put_clamped_coord(byte * p,floatp v,int num_bytes)645 put_clamped_coord(byte *p, floatp v, int num_bytes)
646 {
647     put_clamped(p, ENCODE_MESH_COORDINATE(v), num_bytes);
648 }
649 
650 /* Convert floating-point mesh data to packed binary. */
651 /* BitsPerFlag = 8, BitsPerCoordinate = 24, BitsPerComponent = 16, */
652 /* scaling is as defined below. */
653 private int
put_float_mesh_data(cos_stream_t * pscs,shade_coord_stream_t * cs,int flag,const pdf_mesh_data_params_t * pmdp)654 put_float_mesh_data(cos_stream_t *pscs, shade_coord_stream_t *cs,
655 		    int flag, const pdf_mesh_data_params_t *pmdp)
656 {
657     int num_points = pmdp->num_points;
658     byte b[1 + (3 + 3) * 16];	/* flag + x + y or c */
659     gs_fixed_point pts[16];
660     const float *domain = pmdp->Domain;
661     const gs_range_t *pranges = pmdp->ranges;
662     int i, code;
663 
664     b[0] = (byte)flag;		/* may be -1 */
665     if ((code = shade_next_coords(cs, pts, num_points)) < 0)
666 	return code;
667     for (i = 0; i < num_points; ++i) {
668 	put_clamped_coord(b + 1 + i * 6, fixed2float(pts[i].x), 3);
669 	put_clamped_coord(b + 4 + i * 6, fixed2float(pts[i].y), 3);
670     }
671     if ((code = cos_stream_add_bytes(pscs, b + (flag < 0),
672 				     (flag >= 0) + num_points * 6)) < 0)
673 	return code;
674     for (i = 0; i < pmdp->num_components; ++i) {
675 	float c;
676 	double v;
677 
678 	cs->get_decoded(cs, 0, NULL, &c);
679 	if (pmdp->is_indexed)
680 	    v = ENCODE_MESH_COLOR_INDEX(c);
681 	else {
682 	    /*
683 	     * We don't rescale stream data values, only the Decode ranges.
684 	     * (We do have to rescale data values from an array, unless
685 	     * they are the input parameter for a Function.)
686 	     * This makes everything come out as it should.
687 	     */
688 	    double vmin, vmax;
689 
690 	    if (domain)
691 		vmin = domain[2 * i], vmax = domain[2 * i + 1];
692 	    else
693 		vmin = 0.0, vmax = 1.0;
694 	    if (pranges) {
695 		double base = pranges[i].rmin, factor = pranges[i].rmax - base;
696 
697 		vmin = vmin * factor + base;
698 		vmax = vmax * factor + base;
699 	    }
700 	    v = ENCODE_MESH_COMPONENT(c, vmin, vmax);
701 	}
702 	put_clamped(b, v, 2);
703 	if ((code = cos_stream_add_bytes(pscs, b, 2)) < 0)
704 	    return code;
705     }
706     return 0;
707 }
708 
709 /* Write a mesh Shading. */
710 private int
pdf_put_mesh_shading(cos_stream_t * pscs,const gs_shading_t * psh,const gs_range_t * pranges)711 pdf_put_mesh_shading(cos_stream_t *pscs, const gs_shading_t *psh,
712 		     const gs_range_t *pranges)
713 {
714     cos_dict_t *const pscd = cos_stream_dict(pscs);
715     gs_color_space *pcs = psh->params.ColorSpace;
716     const gs_shading_mesh_params_t *const pmp =
717 	(const gs_shading_mesh_params_t *)&psh->params;
718     int code, code1;
719     int bits_per_coordinate, bits_per_component, bits_per_flag;
720     int num_comp;
721     bool from_array = data_source_is_array(pmp->DataSource);
722     pdf_mesh_data_params_t data_params;
723     shade_coord_stream_t cs;
724     gs_matrix_fixed ctm_ident;
725     int flag;
726 
727     if (pmp->Function) {
728 	data_params.Domain = 0;
729 	num_comp = 1;
730     } else {
731 	data_params.Domain = (pmp->Decode != 0 ? pmp->Decode + 4 : NULL);
732 	num_comp = gs_color_space_num_components(pcs);
733     }
734     data_params.ranges = pranges;
735 
736     /* Write parameters common to all mesh Shadings. */
737     shade_next_init(&cs, pmp, NULL);
738     if (from_array) {
739 	cos_array_t *pca = cos_array_alloc(pscd->pdev, "pdf_put_mesh_shading");
740 	int i;
741 
742 	if (pca == 0)
743 	    return_error(gs_error_VMerror);
744 	for (i = 0; i < 2; ++i)
745 	    if ((code = pdf_array_add_real2(pca, MIN_MESH_COORDINATE,
746 					    MAX_MESH_COORDINATE)) < 0)
747 		return code;
748 	data_params.is_indexed = false;
749 	if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
750 	    data_params.is_indexed = true;
751 	    if ((code = pdf_array_add_real2(pca, MIN_MESH_COLOR_INDEX,
752 					    MAX_MESH_COLOR_INDEX)) < 0)
753 		return code;
754 	} else {
755 	    for (i = 0; i < num_comp; ++i) {
756 		double rmin, rmax;
757 
758 		if (pmp->Function || pranges || data_params.Domain == 0)
759 		    rmin = 0.0, rmax = 1.0;
760 		else
761 		    rmin = data_params.Domain[2 * i],
762 			rmax = data_params.Domain[2 * i + 1];
763 		if ((code =
764 		     pdf_array_add_real2(pca, rmin, rmax)) < 0)
765 		    return code;
766 	    }
767 	}
768 	code = cos_dict_put_c_key_object(pscd, "/Decode", COS_OBJECT(pca));
769 	if (code < 0)
770 	    return code;
771 	bits_per_coordinate = 24;
772 	bits_per_component = 16;
773 	bits_per_flag = 8;
774 	gs_make_identity((gs_matrix *)&ctm_ident);
775 	ctm_ident.tx_fixed = ctm_ident.ty_fixed = 0;
776 	cs.pctm = &ctm_ident;
777 	if (pmp->Function)
778 	    data_params.ranges = 0; /* don't scale function parameter */
779     } else {
780 	/****** SCALE Decode ******/
781 	code = cos_dict_put_c_key_floats(pscd, "/Decode", pmp->Decode,
782 					 4 + num_comp * 2);
783 	if (code >= 0)
784 	    code = cos_stream_add_stream_contents(pscs, cs.s);
785 	bits_per_coordinate = pmp->BitsPerCoordinate;
786 	bits_per_component = pmp->BitsPerComponent;
787 	bits_per_flag = -1;
788     }
789     if (code < 0 ||
790 	(code = pdf_put_shading_Function(pscd, pmp->Function, pranges)) < 0 ||
791 	(code = cos_dict_put_c_key_int(pscd, "/BitsPerCoordinate",
792 				       bits_per_coordinate)) < 0 ||
793 	(code = cos_dict_put_c_key_int(pscd, "/BitsPerComponent",
794 				       bits_per_component)) < 0
795 	)
796 	return code;
797 
798     switch (ShadingType(psh)) {
799     case shading_type_Free_form_Gouraud_triangle: {
800 	const gs_shading_FfGt_params_t *const params =
801 	    (const gs_shading_FfGt_params_t *)pmp;
802 
803 	data_params.num_points = 1;
804 	data_params.num_components = num_comp;
805 	if (from_array) {
806 	    while ((flag = shade_next_flag(&cs, 0)) >= 0)
807 		if ((code = put_float_mesh_data(pscs, &cs, flag,
808 						&data_params)) < 0)
809 		    return code;
810 	    if (!seofp(cs.s))
811 		code = gs_note_error(gs_error_rangecheck);
812 	}
813 	if (bits_per_flag < 0)
814 	    bits_per_flag = params->BitsPerFlag;
815 	break;
816     }
817     case shading_type_Lattice_form_Gouraud_triangle: {
818 	const gs_shading_LfGt_params_t *const params =
819 	    (const gs_shading_LfGt_params_t *)pmp;
820 
821 	data_params.num_points = 1;
822 	data_params.num_components = num_comp;
823 	if (from_array)
824 	    while (!seofp(cs.s))
825 		if ((code = put_float_mesh_data(pscs, &cs, -1,
826 						&data_params)) < 0)
827 		    return code;
828 	code = cos_dict_put_c_key_int(pscd, "/VerticesPerRow",
829 				      params->VerticesPerRow);
830 	return code;
831     }
832     case shading_type_Coons_patch: {
833 	const gs_shading_Cp_params_t *const params =
834 	    (const gs_shading_Cp_params_t *)pmp;
835 
836 	if (from_array) {
837 	    while ((flag = shade_next_flag(&cs, 0)) >= 0) {
838 		data_params.num_points = (flag == 0 ? 12 : 8);
839 		data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
840 		if ((code = put_float_mesh_data(pscs, &cs, flag,
841 						&data_params)) < 0)
842 		    return code;
843 	    }
844 	    if (!seofp(cs.s))
845 		code = gs_note_error(gs_error_rangecheck);
846 	}
847 	if (bits_per_flag < 0)
848 	    bits_per_flag = params->BitsPerFlag;
849 	break;
850     }
851     case shading_type_Tensor_product_patch: {
852 	const gs_shading_Tpp_params_t *const params =
853 	    (const gs_shading_Tpp_params_t *)pmp;
854 
855 	if (from_array) {
856 	    while ((flag = shade_next_flag(&cs, 0)) >= 0) {
857 		data_params.num_points = (flag == 0 ? 16 : 12);
858 		data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
859 		if ((code = put_float_mesh_data(pscs, &cs, flag,
860 						&data_params)) < 0)
861 		    return code;
862 	    }
863 	    if (!seofp(cs.s))
864 		code = gs_note_error(gs_error_rangecheck);
865 	}
866 	if (bits_per_flag < 0)
867 	    bits_per_flag = params->BitsPerFlag;
868 	break;
869     }
870     default:
871 	return_error(gs_error_rangecheck);
872     }
873     code1 =  cos_dict_put_c_key_int(pscd, "/BitsPerFlag", bits_per_flag);
874     if (code1 < 0)
875 	return code;
876     return code;
877 }
878 
879 /* Write a PatternType 2 (shading pattern) color. */
880 int
pdf_put_pattern2(gx_device_pdf * pdev,const gx_drawing_color * pdc,const psdf_set_color_commands_t * ppscc,pdf_resource_t ** ppres)881 pdf_put_pattern2(gx_device_pdf *pdev, const gx_drawing_color *pdc,
882 		 const psdf_set_color_commands_t *ppscc,
883 		 pdf_resource_t **ppres)
884 {
885     const gs_pattern2_instance_t *pinst =
886 	(gs_pattern2_instance_t *)pdc->ccolor.pattern;
887     const gs_shading_t *psh = pinst->template.Shading;
888     cos_value_t v;
889     pdf_resource_t *pres;
890     pdf_resource_t *psres;
891     cos_dict_t *pcd;
892     cos_object_t *psco;
893     const gs_range_t *pranges;
894     int code = pdf_cs_Pattern_colored(pdev, &v);
895     int code1 = 0;
896     gs_matrix smat;
897 
898     if (code < 0)
899 	return code;
900     code = pdf_alloc_resource(pdev, resourcePattern, gs_no_id, ppres, 0L);
901     if (code < 0)
902 	return code;
903     pres = *ppres;
904     cos_become(pres->object, cos_type_dict);
905     pcd = (cos_dict_t *)pres->object;
906     code = pdf_alloc_resource(pdev, resourceShading, gs_no_id, &psres, 0L);
907     if (code < 0)
908 	return code;
909     psco = psres->object;
910     if (ShadingType(psh) >= 4) {
911 	/* Shading has an associated data stream. */
912 	cos_become(psco, cos_type_stream);
913 	code = pdf_put_shading_common(cos_stream_dict((cos_stream_t *)psco),
914 				      psh, &pranges);
915 	if (code >= 0)
916 	    code1 = pdf_put_mesh_shading((cos_stream_t *)psco, psh, pranges);
917     } else {
918 	cos_become(psco, cos_type_dict);
919 	code = pdf_put_shading_common((cos_dict_t *)psco, psh, &pranges);
920 	if (code >= 0)
921 	    code = pdf_put_scalar_shading((cos_dict_t *)psco, psh, pranges);
922     }
923     /*
924      * In PDF, the Matrix is the transformation from the pattern space to
925      * the *default* user coordinate space, not the current space.
926      */
927     gs_currentmatrix(pinst->saved, &smat);
928     {
929 	double xscale = 72.0 / pdev->HWResolution[0],
930 	    yscale = 72.0 / pdev->HWResolution[1];
931 
932 	smat.xx *= xscale, smat.yx *= xscale, smat.tx *= xscale;
933 	smat.xy *= yscale, smat.yy *= yscale, smat.ty *= yscale;
934     }
935     if (code < 0 ||
936 	(code = cos_dict_put_c_key_int(pcd, "/PatternType", 2)) < 0 ||
937 	(code = cos_dict_put_c_key_object(pcd, "/Shading", psco)) < 0 ||
938 	(code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0
939 	/****** ExtGState ******/
940 	)
941 	return code;
942     cos_value_write(&v, pdev);
943     pprints1(pdev->strm, " %s", ppscc->setcolorspace);
944     return code1;
945 }
946 
947 /*
948     Include color space.
949  */
950 int
gdev_pdf_include_color_space(gx_device * dev,gs_color_space * cspace,const byte * res_name,int name_length)951 gdev_pdf_include_color_space(gx_device *dev, gs_color_space *cspace, const byte *res_name, int name_length)
952 {
953     gx_device_pdf * pdev = (gx_device_pdf *)dev;
954     cos_value_t cs_value;
955 
956     return pdf_color_space_named(pdev, &cs_value, NULL, cspace,
957 				&pdf_color_space_names, true, res_name, name_length);
958 }
959