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", ¶ms->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