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