/src/ghostpdl/devices/vector/gdevpdft.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* transparency processing for PDF-writing driver */ |
18 | | #include "gx.h" |
19 | | #include "string_.h" |
20 | | #include "gserrors.h" |
21 | | #include "gstrans.h" |
22 | | #include "gscolor2.h" |
23 | | #include "gzstate.h" |
24 | | #include "gdevpdfx.h" |
25 | | #include "gdevpdfg.h" |
26 | | #include "gdevpdfo.h" |
27 | | #include "gsccolor.h" |
28 | | #include "gsicc_manage.h" |
29 | | #include "gdevpdfc.h" |
30 | | |
31 | | static int |
32 | | pdf_make_soft_mask_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams) |
33 | 110 | { |
34 | 110 | pdf_resource_t *pres_soft_mask_dict = 0; |
35 | 110 | cos_dict_t *soft_mask_dict; |
36 | 110 | int code; |
37 | | |
38 | | /* Fixme : merge redundant objects. */ |
39 | 110 | code = pdf_alloc_resource(pdev, resourceSoftMaskDict, gs_no_id, &pres_soft_mask_dict, -1); |
40 | 110 | if (code < 0) |
41 | 0 | return code; |
42 | 110 | cos_become(pres_soft_mask_dict->object, cos_type_dict); |
43 | 110 | pdev->pres_soft_mask_dict = pres_soft_mask_dict; |
44 | 110 | soft_mask_dict = (cos_dict_t *)pres_soft_mask_dict->object; |
45 | 110 | code = cos_dict_put_c_key_string(soft_mask_dict, "/S", |
46 | 110 | pparams->subtype == TRANSPARENCY_MASK_Alpha ? (byte *)"/Alpha" : (byte *)"/Luminosity", |
47 | 110 | pparams->subtype == TRANSPARENCY_MASK_Alpha ? 6 : 11); |
48 | 110 | if (code < 0) |
49 | 0 | return code; |
50 | 110 | if (pparams->Background_components) { |
51 | 85 | cos_array_t *Background; |
52 | | |
53 | 85 | Background = cos_array_from_floats(pdev, pparams->Background, |
54 | 85 | pparams->Background_components, "pdf_write_soft_mask_dict"); |
55 | 85 | if (Background == NULL) |
56 | 0 | return_error(gs_error_VMerror); |
57 | 85 | code = cos_dict_put_c_key_object(soft_mask_dict, "/BC", (cos_object_t *)Background); |
58 | 85 | if (code < 0) |
59 | 0 | return code; |
60 | 85 | } |
61 | 110 | if (pdev->CompatibilityLevel <= 1.7 && pparams->transfer_function != NULL && pdev->params.TransferFunctionInfo == tfi_Preserve) { |
62 | 24 | int64_t id; |
63 | 24 | char buf[20]; |
64 | | |
65 | 24 | code = pdf_write_function(pdev, pparams->transfer_function, &id); |
66 | 24 | if (code < 0) |
67 | 0 | return code; |
68 | 24 | gs_snprintf(buf, sizeof(buf), " %"PRId64" 0 R", id); |
69 | 24 | code = cos_dict_put_c_key_string(soft_mask_dict, "/TR", (const byte *)buf, strlen(buf)); |
70 | 24 | if (code < 0) |
71 | 0 | return code; |
72 | 24 | } |
73 | 110 | return 0; |
74 | | |
75 | 110 | } |
76 | | |
77 | | static int WriteDefaultSpaces(gx_device_pdf * pdev, const gs_gstate * pgs, cos_value_t *cs_value) |
78 | 0 | { |
79 | 0 | cmm_profile_t *pprofile = NULL; |
80 | 0 | gs_color_space cs, *pcs; |
81 | 0 | gs_color_space_type t; |
82 | 0 | cos_array_t *pca; |
83 | 0 | cos_dict_t *Resources = NULL, *ColorSpaces = NULL; |
84 | 0 | int code = 0; |
85 | 0 | const cos_value_t *pcos = NULL; |
86 | 0 | pdf_color_space_t *ppcs; |
87 | 0 | pdf_resource_t *pres = NULL; |
88 | 0 | gs_color_space_index target; |
89 | | |
90 | | /* Check type before using */ |
91 | 0 | if (cs_value->value_type != COS_VALUE_CONST) |
92 | 0 | return_error(gs_error_typecheck); |
93 | | /* Compare its value to find out which space we want */ |
94 | 0 | if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceGray", 11) == 0) |
95 | 0 | target = gs_color_space_index_DeviceGray; |
96 | 0 | else |
97 | 0 | if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceRGB", 10) == 0) |
98 | 0 | target = gs_color_space_index_DeviceRGB; |
99 | 0 | else |
100 | 0 | if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceCMYK", 11) == 0) |
101 | 0 | target = gs_color_space_index_DeviceCMYK; |
102 | 0 | else |
103 | 0 | return_error(gs_error_rangecheck); |
104 | | |
105 | | /* Retrieve/Create the Resources->ColorSpace dictionary in the Pages tree */ |
106 | 0 | Resources = (cos_dict_t *)cos_dict_find(pdev->Pages, (const byte *)"/Resources", 10); |
107 | 0 | if (Resources == NULL) { |
108 | 0 | Resources = cos_dict_alloc(pdev, "WriteDefaultSpaces"); |
109 | 0 | if (Resources == NULL) |
110 | 0 | return_error(gs_error_undefined); |
111 | 0 | code = cos_dict_put_c_key_object(pdev->Pages, "/Resources", (cos_object_t *)Resources); |
112 | 0 | if (code < 0) |
113 | 0 | return code; |
114 | 0 | } |
115 | | |
116 | | /* Then the ColorSpace sub-dictionary */ |
117 | 0 | ColorSpaces = (cos_dict_t *)cos_dict_find(Resources, (const byte *)"/ColorSpace", 11); |
118 | 0 | if(ColorSpaces == NULL) { |
119 | 0 | ColorSpaces = cos_dict_alloc(pdev, "WriteDefaultSpaces"); |
120 | 0 | if (ColorSpaces == NULL) |
121 | 0 | return_error(gs_error_undefined); |
122 | 0 | code = cos_dict_put_c_key_object(Resources, "/ColorSpace", (cos_object_t *)ColorSpaces); |
123 | 0 | if (code < 0) |
124 | 0 | return code; |
125 | 0 | } |
126 | | |
127 | | /* Now check to see if we already have a /Default space, if so just exit. */ |
128 | 0 | switch(target) { |
129 | 0 | case gs_color_space_index_DeviceGray: |
130 | 0 | pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultGray"); |
131 | 0 | break; |
132 | 0 | case gs_color_space_index_DeviceRGB: |
133 | 0 | pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultRGB"); |
134 | 0 | break; |
135 | 0 | case gs_color_space_index_DeviceCMYK: |
136 | 0 | pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultCMYK"); |
137 | 0 | break; |
138 | 0 | default: |
139 | 0 | return_error(gs_error_rangecheck); |
140 | 0 | break; |
141 | 0 | } |
142 | | /* If we've already written the relevant Default space, don't write another one! */ |
143 | 0 | if (pcos != NULL) |
144 | 0 | return 0; |
145 | | |
146 | | /* This is all a bit hairy, but there seems to be no easy way to achieve this |
147 | | * Probably could export some static functions from the ICC code, but this works too. Just manufacture |
148 | | * enough of a colour space to retrieve the ICC profile. |
149 | | */ |
150 | 0 | cs.cmm_icc_profile_data = NULL; |
151 | 0 | t.index = target; |
152 | 0 | cs.type = &t; |
153 | 0 | pprofile = gsicc_get_gscs_profile(&cs, pgs->icc_manager); |
154 | 0 | if (pprofile == NULL) |
155 | 0 | return_error(gs_error_undefined); |
156 | | /* gsicc_get_gscs_profile() does not count up the returned reference. We |
157 | | * deliberately don't count it up here; we're only using it temporarily. |
158 | | */ |
159 | | |
160 | | /* Now manufacture a colour space to attach the profile to. */ |
161 | 0 | pcs = gs_cspace_new_ICC(pdev->memory, (gs_gstate *)pgs, 4); |
162 | 0 | if (pcs == NULL) |
163 | 0 | return_error(gs_error_undefined); |
164 | | |
165 | 0 | switch(target) { |
166 | 0 | case gs_color_space_index_DeviceGray: |
167 | 0 | pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceGray; |
168 | 0 | break; |
169 | 0 | case gs_color_space_index_DeviceRGB: |
170 | 0 | pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceRGB; |
171 | 0 | break; |
172 | 0 | case gs_color_space_index_DeviceCMYK: |
173 | 0 | pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceCMYK; |
174 | 0 | break; |
175 | 0 | default: |
176 | 0 | return_error(gs_error_undefined); |
177 | 0 | } |
178 | | |
179 | 0 | pcs->cmm_icc_profile_data = pprofile; |
180 | | |
181 | | /* Create an array. */ |
182 | 0 | pca = cos_array_alloc(pdev, "WriteDefaultSpaces"); |
183 | 0 | if (pca == 0) { |
184 | 0 | rc_decrement(pcs, "WriteDefaultSpaces"); |
185 | 0 | return_error(gs_error_VMerror); |
186 | 0 | } |
187 | | |
188 | | /* Turn the array in to a PDF colour space, using the GS colour space */ |
189 | 0 | code = pdf_iccbased_color_space(pdev, pgs, NULL, pcs, pca); |
190 | 0 | if (code < 0) { |
191 | 0 | rc_decrement(pcs, "WriteDefaultSpaces"); |
192 | 0 | cos_release((cos_object_t *)pca, "WriteDefaultSpaces"); |
193 | 0 | return code; |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * Register the color space as a resource, since it must be referenced |
198 | | * by name rather than directly. |
199 | | */ |
200 | 0 | { |
201 | 0 | if (code < 0 || |
202 | 0 | (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id, |
203 | 0 | &pres, -1)) < 0 |
204 | 0 | ) { |
205 | 0 | COS_FREE(pca, "pdf_color_space"); |
206 | 0 | return code; |
207 | 0 | } |
208 | 0 | pdf_reserve_object_id(pdev, pres, 0); |
209 | |
|
210 | 0 | switch(target) { |
211 | 0 | case gs_color_space_index_DeviceGray: |
212 | 0 | memcpy(pres->rname, "DefaultGray", 11); |
213 | 0 | pres->rname[11] = 0; |
214 | 0 | break; |
215 | 0 | case gs_color_space_index_DeviceRGB: |
216 | 0 | memcpy(pres->rname, "DefaultRGB", 10); |
217 | 0 | pres->rname[10] = 0; |
218 | 0 | break; |
219 | 0 | case gs_color_space_index_DeviceCMYK: |
220 | 0 | memcpy(pres->rname, "DefaultCMYK", 11); |
221 | 0 | pres->rname[11] = 0; |
222 | 0 | break; |
223 | 0 | default: |
224 | 0 | return_error(gs_error_undefined); |
225 | 0 | } |
226 | | |
227 | 0 | ppcs = (pdf_color_space_t *)pres; |
228 | 0 | ppcs->serialized = 0; |
229 | 0 | ppcs->serialized_size = 0; |
230 | 0 | ppcs->ranges = 0; |
231 | 0 | pca->id = pres->object->id; |
232 | 0 | COS_FREE(pres->object, "pdf_color_space"); |
233 | 0 | pres->object = (cos_object_t *)pca; |
234 | 0 | cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace); |
235 | 0 | } |
236 | | |
237 | 0 | code = pdf_add_resource(pdev, Resources, "/ColorSpace", pres); |
238 | |
|
239 | 0 | rc_decrement(pcs, "WriteDefaultSpaces"); |
240 | 0 | cos_release((cos_object_t *)pca, "WriteDefaultSpaces"); |
241 | 0 | return code; |
242 | 0 | } |
243 | | |
244 | | static int |
245 | | pdf_make_group_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams, |
246 | | const gs_gstate * pgs, cos_dict_t **pdict) |
247 | 5.51k | { |
248 | 5.51k | pdf_resource_t *pres_group; |
249 | 5.51k | cos_dict_t *group_dict; |
250 | 5.51k | int code = 0; |
251 | 5.51k | cos_value_t cs_value; |
252 | | |
253 | 5.51k | code = pdf_alloc_resource(pdev, resourceGroup, gs_no_id, &pres_group, -1); |
254 | 5.51k | if (code < 0) |
255 | 0 | return code; |
256 | 5.51k | cos_become(pres_group->object, cos_type_dict); |
257 | 5.51k | group_dict = (cos_dict_t *)pres_group->object; |
258 | 5.51k | code = cos_dict_put_c_key_string(group_dict, "/Type", (const byte *)"/Group", 6); |
259 | 5.51k | if (code < 0) |
260 | 0 | return code; |
261 | 5.51k | code = cos_dict_put_c_key_string(group_dict, "/S", (const byte *)"/Transparency", 13); |
262 | 5.51k | if (code < 0) |
263 | 0 | return code; |
264 | 5.51k | if (pparams->Isolated) { |
265 | 4.69k | code = cos_dict_put_c_key_bool(group_dict, "/I", true); |
266 | 4.69k | if (code < 0) |
267 | 0 | return code; |
268 | 4.69k | } |
269 | 5.51k | if (pparams->Knockout) { |
270 | 3 | code = cos_dict_put_c_key_bool(group_dict, "/K", true); |
271 | 3 | if (code < 0) |
272 | 0 | return code; |
273 | 3 | } |
274 | | /* Note that we should not add in the graphic state |
275 | | color space for the group color if there was not |
276 | | a group color specified. |
277 | | In this case, the parent group is inherited from |
278 | | the previous group or the device color space */ |
279 | 5.51k | if (pgs != NULL && pparams->ColorSpace != NULL) { |
280 | 888 | const gs_color_space *cs = pparams->ColorSpace; |
281 | | |
282 | 888 | if (pparams->ColorSpace == NULL) |
283 | 0 | code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, cs, |
284 | 0 | &pdf_color_space_names, false, NULL, 0, false); |
285 | 888 | else |
286 | 888 | code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, pparams->ColorSpace, |
287 | 888 | &pdf_color_space_names, false, NULL, 0, false); |
288 | 888 | if (code < 0) |
289 | 0 | return code; |
290 | 888 | if (cs_value.value_type == COS_VALUE_CONST && cs_value.contents.chars.size == 11 && strncmp((const char *)cs_value.contents.chars.data, "/DeviceGray", cs_value.contents.chars.size) == 0 && pdev->smask_construction) |
291 | 56 | code = cos_dict_put_c_key(group_dict, "/CS", &cs_value); |
292 | 832 | else { |
293 | 832 | switch(pdev->params.BlendConversionStrategy) { |
294 | 0 | case bcs_Managed: |
295 | 0 | if (pdev->params.ColorConversionStrategy != ccs_LeaveColorUnchanged) { |
296 | 0 | if (pdev->params.ColorConversionStrategy < ccs_CMYK || pdev->params.ColorConversionStrategy == ccs_ByObjectType) { |
297 | 0 | outprintf(pdev->memory, "\tCannot use DeviceIndependentColor for a Blending colour space.\n\tCannot use /Managed or /Simple replacement strategy with DeviceIndependentColor.\n\tChange ColorConversionStrategy or BlendConversionStrategy.\n\n"); |
298 | 0 | return_error(gs_error_rangecheck); |
299 | 0 | } |
300 | 0 | code = WriteDefaultSpaces(pdev, pgs, &cs_value); |
301 | 0 | } |
302 | 0 | if (code < 0) |
303 | 0 | return code; |
304 | | /* Fall through */ |
305 | 0 | case bcs_None: |
306 | 0 | code = cos_dict_put_c_key(group_dict, "/CS", &cs_value); |
307 | 0 | break; |
308 | 832 | case bcs_Simple: |
309 | 832 | switch(pdev->params.ColorConversionStrategy) { |
310 | 832 | case ccs_LeaveColorUnchanged: |
311 | 832 | code = cos_dict_put_c_key(group_dict, "/CS", &cs_value); |
312 | 832 | break; |
313 | 0 | case ccs_UseDeviceIndependentColor: |
314 | 0 | case ccs_UseDeviceIndependentColorForImages: |
315 | 0 | case ccs_ByObjectType: |
316 | 0 | case ccs_sRGB: |
317 | 0 | outprintf(pdev->memory, "\tCannot use DeviceIndependentColor for a Blending colour space.\n\tCannot use /Managed or /Simple replacement strategy with DeviceIndependentColor.\n\tChange ColorConversionStrategy or BlendConversionStrategy.\n\n"); |
318 | 0 | return_error(gs_error_rangecheck); |
319 | 0 | break; |
320 | | |
321 | 0 | case ccs_CMYK: |
322 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceCMYK", 11); |
323 | 0 | break; |
324 | 0 | case ccs_Gray: |
325 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceGray", 11); |
326 | 0 | break; |
327 | 0 | case ccs_RGB: |
328 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceRGB", 10); |
329 | 0 | break; |
330 | 832 | } |
331 | 832 | break; |
332 | 832 | } |
333 | 832 | } |
334 | 888 | } |
335 | 5.51k | if (code < 0) |
336 | 0 | return code; |
337 | 5.51k | group_dict = NULL; /* The next line invalidates it. */ |
338 | 5.51k | code = pdf_substitute_resource(pdev, &pres_group, resourceGroup, NULL, false); |
339 | 5.51k | if (code < 0) |
340 | 0 | return code; |
341 | 5.51k | pres_group->where_used |= pdev->used_mask; |
342 | 5.51k | *pdict = (cos_dict_t *)pres_group->object; |
343 | 5.51k | return 0; |
344 | 5.51k | } |
345 | | |
346 | | static int |
347 | | pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams, |
348 | | const gs_gstate * pgs, |
349 | | const cos_dict_t *group_dict, cos_dict_t *form_dict) |
350 | 1.30k | { |
351 | 1.30k | cos_array_t *bbox_array; |
352 | 1.30k | float bbox[4]; |
353 | 1.30k | gs_rect bbox_rect; |
354 | 1.30k | int code; |
355 | | |
356 | 1.30k | code = gs_bbox_transform(&pparams->bbox, &ctm_only(pgs), &bbox_rect); |
357 | 1.30k | if (code < 0) |
358 | 0 | return code; |
359 | 1.30k | bbox[0] = bbox_rect.p.x; |
360 | 1.30k | bbox[1] = bbox_rect.p.y; |
361 | 1.30k | bbox[2] = bbox_rect.q.x; |
362 | 1.30k | bbox[3] = bbox_rect.q.y; |
363 | 1.30k | code = cos_dict_put_c_key_string(form_dict, "/Type", (const byte *)"/XObject", 8); |
364 | 1.30k | if (code < 0) |
365 | 0 | return code; |
366 | 1.30k | code = cos_dict_put_c_key_string(form_dict, "/Subtype", (const byte *)"/Form", 5); |
367 | 1.30k | if (code < 0) |
368 | 0 | return code; |
369 | 1.30k | code = cos_dict_put_c_key_int(form_dict, "/FormType", 1); |
370 | 1.30k | if (code < 0) |
371 | 0 | return code; |
372 | 1.30k | code = cos_dict_put_c_key_string(form_dict, "/Matrix", (const byte *)"[1 0 0 1 0 0]", 13); |
373 | 1.30k | if (code < 0) |
374 | 0 | return code; |
375 | 1.30k | bbox_array = cos_array_from_floats(pdev, bbox, 4, "pdf_begin_transparency_group"); |
376 | 1.30k | if (bbox_array == NULL) |
377 | 0 | return_error(gs_error_VMerror); |
378 | 1.30k | code = cos_dict_put_c_key_object(form_dict, "/BBox", (cos_object_t *)bbox_array); |
379 | 1.30k | if (code < 0) |
380 | 0 | return code; |
381 | 1.30k | if (pdev->PendingOC != 0) { |
382 | 1 | if (pdev->CompatibilityLevel < 1.4999) { |
383 | 0 | if (pdev->PDFA) { |
384 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
385 | 0 | case 0: |
386 | 0 | emprintf(pdev->memory, |
387 | 0 | "Optional Content not valid in this version of PDF, reverting to normal PDF output\n"); |
388 | 0 | pdev->AbortPDFAX = true; |
389 | 0 | pdev->PDFA = 0; |
390 | 0 | break; |
391 | 0 | case 1: |
392 | 0 | emprintf(pdev->memory, |
393 | 0 | "Optional Content not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n"); |
394 | 0 | break; |
395 | 0 | case 2: |
396 | 0 | emprintf(pdev->memory, |
397 | 0 | "Optional Content not valid in this version of PDF, aborting conversion\n"); |
398 | 0 | return_error (gs_error_typecheck); |
399 | 0 | break; |
400 | 0 | default: |
401 | 0 | emprintf(pdev->memory, |
402 | 0 | "Optional Content not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
403 | 0 | pdev->AbortPDFAX = true; |
404 | 0 | pdev->PDFA = 0; |
405 | 0 | break; |
406 | 0 | } |
407 | 0 | } else { |
408 | 0 | emprintf(pdev->memory, |
409 | 0 | "Optional Content not valid in this version of PDF. Dropping feature to preserve compatibility\n"); |
410 | 0 | } |
411 | 1 | } else { |
412 | 1 | char str[256]; |
413 | 1 | gs_param_string param; |
414 | 1 | cos_object_t *pco = NULL; |
415 | | |
416 | 1 | param.data = (const byte *)pdev->PendingOC; |
417 | 1 | param.size = strlen(pdev->PendingOC); |
418 | 1 | code = pdf_refer_named(pdev, ¶m, &pco); |
419 | 1 | if(code < 0) |
420 | 0 | return code; |
421 | | |
422 | 1 | gs_snprintf(str, sizeof(str), "%"PRId64" 0 R", pco->id); |
423 | 1 | code = cos_dict_put_string_copy(form_dict, "/OC", str); |
424 | 1 | if (code < 0) |
425 | 0 | return code; |
426 | | |
427 | 1 | gs_free_object(pdev->memory->non_gc_memory, pdev->PendingOC, ""); |
428 | 1 | pdev->PendingOC = NULL; |
429 | 1 | } |
430 | 1 | } |
431 | 1.30k | return cos_dict_put_c_key_object(form_dict, "/Group", (cos_object_t *)group_dict); |
432 | 1.30k | } |
433 | | |
434 | | static int |
435 | | pdf_begin_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev, |
436 | | const gs_pdf14trans_params_t * pparams, bool page_group) |
437 | 5.51k | { |
438 | 5.51k | cos_dict_t *group_dict; |
439 | 5.51k | int code; |
440 | | |
441 | 5.51k | if (pgs == NULL) |
442 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
443 | 5.51k | code = pdf_make_group_dict(pdev, pparams, pgs, &group_dict); |
444 | 5.51k | if (code < 0) |
445 | 0 | return code; |
446 | 5.51k | code = pdf_open_page(pdev, PDF_IN_STREAM); |
447 | 5.51k | if (code < 0) |
448 | 0 | return code; |
449 | 5.51k | code = pdf_check_soft_mask(pdev, pgs); |
450 | 5.51k | if (code < 0) |
451 | 0 | return code; |
452 | 5.51k | if (pdf_must_put_clip_path(pdev, pgs->clip_path)) { |
453 | 481 | code = pdf_put_clip_path(pdev, pgs->clip_path); |
454 | 481 | if (code < 0) |
455 | 0 | return code; |
456 | 481 | } |
457 | 5.51k | if (page_group) |
458 | 431 | pdev->pages[pdev->next_page].group_id = group_dict->id; |
459 | 5.08k | else if (pparams->image_with_SMask) { |
460 | | /* An internal group for the image implementation. |
461 | | See doimagesmask in gs/lib/pdf_draw.ps . |
462 | | Just set a flag for skipping pdf_end_transparency_group. */ |
463 | 3.78k | pdev->image_with_SMask |= 1 << ++pdev->FormDepth; |
464 | 3.78k | pdev->PatternsSinceForm = 0; |
465 | 3.78k | } else { |
466 | 1.30k | pdf_resource_t *pres, *pres_gstate = NULL; |
467 | 1.30k | cos_dict_t *pcd = NULL, *pcd_Resources = NULL; |
468 | | |
469 | 1.30k | code = pdf_prepare_drawing(pdev, pgs, &pres_gstate, false); |
470 | 1.30k | if (code < 0) |
471 | 0 | return code; |
472 | 1.30k | code = pdf_end_gstate(pdev, pres_gstate); |
473 | 1.30k | if (code < 0) |
474 | 0 | return code; |
475 | 1.30k | code = pdf_enter_substream(pdev, resourceXObject, |
476 | 1.30k | gs_no_id, &pres, false, pdev->params.CompressPages); |
477 | 1.30k | if (code < 0) |
478 | 0 | return code; |
479 | 1.30k | pdev->FormDepth++; |
480 | 1.30k | pdev->PatternsSinceForm = 0; |
481 | 1.30k | code = pdf_make_form_dict(pdev, pparams, pgs, group_dict, (cos_dict_t *)pres->object); |
482 | 1.30k | if (code < 0) |
483 | 0 | return code; |
484 | | |
485 | | /* Create a Resources dictionary and add it to the form dictionary */ |
486 | 1.30k | pcd = cos_stream_dict((cos_stream_t *)pres->object); |
487 | 1.30k | pcd_Resources = cos_dict_alloc(pdev, "pdf_group(Resources)"); |
488 | 1.30k | if (pcd == NULL || pcd_Resources == NULL) |
489 | 0 | return_error(gs_error_VMerror); |
490 | 1.30k | code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources)); |
491 | 1.30k | pdev->substream_Resources = pcd_Resources; |
492 | 1.30k | return code; |
493 | 1.30k | } |
494 | 4.21k | return 0; |
495 | 5.51k | } |
496 | | |
497 | | static int |
498 | | pdf_end_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev) |
499 | 5.40k | { |
500 | 5.40k | int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0); |
501 | | |
502 | 5.40k | if (!is_in_page(pdev) && pdev->sbstack_depth == 0) |
503 | 0 | return 0; /* A Group definition at the page level, handled separately. */ |
504 | 5.40k | if (pdev->image_with_SMask & (1 << pdev->FormDepth)) { |
505 | | /* An internal group for the image implementation. |
506 | | See pdf_begin_transparency_group. */ |
507 | 3.78k | pdev->image_with_SMask &= ~(1 << pdev->FormDepth--); |
508 | 3.78k | pdev->PatternsSinceForm = 0; |
509 | 3.78k | return 0; |
510 | 3.78k | } else if (pdev->sbstack_depth == bottom) { |
511 | | /* We're closing the page group. */ |
512 | 431 | if (pdev->pages[pdev->next_page].group_id == 0) |
513 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
514 | 431 | return 0; |
515 | 1.19k | } else { |
516 | 1.19k | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
517 | 1.19k | int code; |
518 | 1.19k | uint ignore; |
519 | | |
520 | 1.19k | if (pres == NULL) |
521 | 0 | return_error(gs_error_unregistered); |
522 | 1.19k | pdev->FormDepth--; |
523 | 1.19k | pdev->PatternsSinceForm = 0; |
524 | 1.19k | code = pdf_exit_substream(pdev); |
525 | 1.19k | if (code < 0) |
526 | 0 | return code; |
527 | 1.19k | code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false); |
528 | 1.19k | if (code < 0) |
529 | 0 | return code; |
530 | | /* We need to update the 'where_used' field, in case we substituted a resource */ |
531 | 1.19k | pres->where_used |= pdev->used_mask; |
532 | 1.19k | sputc(pdev->strm,'/'); |
533 | 1.19k | sputs(pdev->strm, (const byte *)pres->rname, strlen(pres->rname), &ignore); |
534 | 1.19k | sputs(pdev->strm, (const byte *)" Do\n", 4, &ignore); |
535 | 1.19k | code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pres); |
536 | 1.19k | return code; |
537 | 1.19k | } |
538 | 5.40k | } |
539 | | |
540 | | static int |
541 | | pdf_begin_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev, |
542 | | const gs_pdf14trans_params_t * pparams) |
543 | 2.04k | { |
544 | 2.04k | if (pparams->subtype == TRANSPARENCY_MASK_None) { |
545 | 1.93k | int code; |
546 | 1.93k | pdf_resource_t *pres = 0L; |
547 | | |
548 | | /* reset the soft mask ID. Apparently this is only used by pdfwrite, if we don't |
549 | | * reset it, then the pdf_prepare_drawing code doesn't know that the SMask has |
550 | | * changed, and so doesn't write out the GState |
551 | | */ |
552 | 1.93k | pgs->soft_mask_id = 0; |
553 | 1.93k | code = pdf_prepare_drawing(pdev, pgs, &pres, false); |
554 | 1.93k | if (code == gs_error_interrupt) { |
555 | | /* */ |
556 | | /* Not in an appropriate context. Do not restore the soft_mask_id. |
557 | | Otherwise any group push that occurs following this will use that |
558 | | softmask, which clearly should be NONE here. |
559 | | */ |
560 | | /* ignore return code, we don't care about this graphics state as we aren't |
561 | | * emitting it anyway |
562 | | */ |
563 | 358 | pdf_end_gstate(pdev, pres); |
564 | 358 | return 0; |
565 | 358 | } |
566 | 1.57k | if (code < 0) |
567 | 0 | return code; |
568 | 1.57k | code = pdf_end_gstate(pdev, pres); |
569 | 1.57k | if (code < 0) |
570 | 0 | return code; |
571 | 1.57k | return 0; |
572 | 1.57k | } |
573 | 110 | if (pparams->mask_is_image) { |
574 | | /* HACK : |
575 | | The control comes here when |
576 | | the PDF interpreter will make the PS interpreter |
577 | | to interprete the mask for filling the transparency buffer |
578 | | with an SMask image. |
579 | | Since we handle Type 3 images as a high level objects, |
580 | | we don't install the transparency buffer here |
581 | | and need to skip the image enumeration for the SMask. |
582 | | However we have no right method for skipping |
583 | | an image enumeration due to possible side effect |
584 | | of the image data proc in Postscript language. |
585 | | Therefore we do enumerate the image mask and accumulate |
586 | | it as a PDF stream, but don't create a reference to it. |
587 | | Later it will be enumerated once again as a part of SMask-ed image, |
588 | | and the pdfwrite image handler will recognize duplicated images |
589 | | and won't create the second stream for same image. |
590 | | |
591 | | We could make a special workaround for |
592 | | skipping mask images either in the graphics library or |
593 | | in the PS code of the PDF interpreter, |
594 | | but we don't want to complicate things now. |
595 | | The performance leak for the second enumeration |
596 | | shouldn't be harmful. |
597 | | |
598 | | So now just set a flag for pdf_end_and_do_image. |
599 | | */ |
600 | 0 | pdev->image_mask_skip = true; |
601 | 0 | return 0; |
602 | 110 | } else { |
603 | 110 | int code; |
604 | | |
605 | 110 | pdev->smask_construction = true; |
606 | 110 | code = pdf_make_soft_mask_dict(pdev, pparams); |
607 | 110 | if (code < 0) |
608 | 0 | return code; |
609 | 110 | code = pdf_open_page(pdev, PDF_IN_STREAM); |
610 | 110 | if (code < 0) |
611 | 0 | return code; |
612 | 110 | return pdf_begin_transparency_group(pgs, pdev, pparams, 0); |
613 | 110 | } |
614 | 110 | } |
615 | | |
616 | | static int |
617 | | pdf_end_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev, |
618 | | const gs_pdf14trans_params_t * pparams) |
619 | 110 | { |
620 | 110 | pdev->smask_construction = false; |
621 | 110 | if (pdev->image_mask_skip) |
622 | 0 | pdev->image_mask_skip = false; |
623 | 110 | else { |
624 | 110 | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
625 | 110 | int code; |
626 | 110 | char buf[20]; |
627 | | |
628 | 110 | if (pres == NULL) |
629 | 0 | return_error(gs_error_unregistered); |
630 | 110 | code = pdf_exit_substream(pdev); |
631 | 110 | if (code < 0) |
632 | 0 | return code; |
633 | 110 | code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false); |
634 | 110 | if (code < 0) |
635 | 0 | return 0; |
636 | | /* We need to update the 'where_used' field, in case we substituted a resource */ |
637 | 110 | pres->where_used |= pdev->used_mask; |
638 | 110 | gs_snprintf(buf, sizeof(buf), "%"PRId64" 0 R", pdf_resource_id(pres)); |
639 | 110 | if (pdev->pres_soft_mask_dict == 0L) { |
640 | | /* something went horribly wrong, we have an 'end' wihtout a matching 'begin' |
641 | | * Give up, throw an error. |
642 | | */ |
643 | 0 | return_error(gs_error_undefined); |
644 | 0 | } |
645 | 110 | code = cos_dict_put_c_key_string((cos_dict_t *)pdev->pres_soft_mask_dict->object, |
646 | 110 | "/G", (const byte *)buf, strlen(buf)); |
647 | 110 | if (code < 0) |
648 | 0 | return code; |
649 | 110 | code = pdf_substitute_resource(pdev, &pdev->pres_soft_mask_dict, |
650 | 110 | resourceSoftMaskDict, NULL, false); |
651 | 110 | if (code < 0) |
652 | 0 | return code; |
653 | 110 | pdev->pres_soft_mask_dict->where_used |= pdev->used_mask; |
654 | 110 | pgs->soft_mask_id = pdev->pres_soft_mask_dict->object->id; |
655 | 110 | pdev->pres_soft_mask_dict = NULL; |
656 | | /* We called pdf_start_trnasparency_group (see pdf_begin_transparency_mask |
657 | | * above) but we don't call pdf_end_transparency_group, so we must reduce |
658 | | * the FormDepth ourselves. |
659 | | */ |
660 | 110 | pdev->FormDepth--; |
661 | 110 | pdev->PatternsSinceForm = 0; |
662 | 110 | } |
663 | 110 | return 0; |
664 | 110 | } |
665 | | |
666 | | static int |
667 | | pdf_set_blend_params(gs_gstate * pgs, gx_device_pdf * dev, |
668 | | const gs_pdf14trans_params_t * pparams) |
669 | 0 | { |
670 | 0 | return 0; |
671 | 0 | } |
672 | | |
673 | | int |
674 | | gdev_pdf_composite(gx_device *dev, |
675 | | gx_device **pcdev, const gs_composite_t *pct, |
676 | | gs_gstate *pgs, gs_memory_t *memory, gx_device *cdev) |
677 | 207k | { |
678 | 207k | gx_device_pdf *pdev = (gx_device_pdf *)dev; |
679 | | |
680 | 207k | if (pdev->HaveTransparency && pdev->CompatibilityLevel >= 1.4 && |
681 | 194k | pct->type->comp_id == GX_COMPOSITOR_PDF14_TRANS && |
682 | 55.3k | pdev->PDFA != 1) { |
683 | 55.3k | gs_pdf14trans_t *pcte = (gs_pdf14trans_t *)pct; |
684 | 55.3k | gs_pdf14trans_params_t *params = &pcte->params; |
685 | | |
686 | 55.3k | *pcdev = dev; |
687 | 55.3k | switch(params->pdf14_op) { |
688 | 720 | case PDF14_PUSH_DEVICE: |
689 | 720 | return 0; |
690 | 720 | case PDF14_POP_DEVICE: |
691 | 720 | return 0; |
692 | 0 | case PDF14_ABORT_DEVICE: |
693 | 0 | return 0; |
694 | 431 | case PDF14_BEGIN_TRANS_PAGE_GROUP: |
695 | 431 | return pdf_begin_transparency_group(pgs, pdev, params, 1); |
696 | 4.97k | case PDF14_BEGIN_TRANS_GROUP: |
697 | 4.97k | return pdf_begin_transparency_group(pgs, pdev, params, 0); |
698 | 5.40k | case PDF14_END_TRANS_GROUP: |
699 | 5.40k | return pdf_end_transparency_group(pgs, pdev); |
700 | 19.2k | case PDF14_BEGIN_TRANS_TEXT_GROUP: |
701 | 19.2k | return 0; |
702 | 21.4k | case PDF14_END_TRANS_TEXT_GROUP: |
703 | 21.4k | return 0; |
704 | 2.04k | case PDF14_BEGIN_TRANS_MASK: |
705 | 2.04k | return pdf_begin_transparency_mask(pgs, pdev, params); |
706 | 110 | case PDF14_END_TRANS_MASK: |
707 | 110 | return pdf_end_transparency_mask(pgs, pdev, params); |
708 | 0 | case PDF14_SET_BLEND_PARAMS: |
709 | 0 | return pdf_set_blend_params(pgs, pdev, params); |
710 | 0 | case PDF14_PUSH_TRANS_STATE: |
711 | 0 | return 0; |
712 | 69 | case PDF14_POP_TRANS_STATE: |
713 | 69 | return 0; |
714 | 110 | case PDF14_PUSH_SMASK_COLOR: |
715 | 110 | return 0; |
716 | 110 | case PDF14_POP_SMASK_COLOR: |
717 | 110 | return 0; |
718 | | |
719 | 0 | default : |
720 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
721 | 55.3k | } |
722 | 0 | return 0; |
723 | 55.3k | } |
724 | 152k | return psdf_composite(dev, pcdev, pct, pgs, memory, cdev); |
725 | 207k | } |
726 | | |
727 | | /* We're not sure why the folllowing device methods are never called. |
728 | | Stub them for a while. */ |
729 | | |
730 | | int |
731 | | gdev_pdf_begin_transparency_group(gx_device *dev, |
732 | | const gs_transparency_group_params_t *ptgp, |
733 | | const gs_rect *pbbox, |
734 | | gs_gstate *pgs, gs_memory_t *mem) |
735 | 0 | { |
736 | 0 | return 0; |
737 | 0 | } |
738 | | |
739 | | int |
740 | | gdev_pdf_end_transparency_group(gx_device *dev, |
741 | | gs_gstate *pgs) |
742 | 0 | { |
743 | 0 | return 0; |
744 | 0 | } |
745 | | |
746 | | int |
747 | | gdev_pdf_begin_transparency_mask(gx_device *dev, |
748 | | const gx_transparency_mask_params_t *ptmp, |
749 | | const gs_rect *pbbox, |
750 | | gs_gstate *pgs, gs_memory_t *mem) |
751 | 0 | { |
752 | 0 | return 0; |
753 | 0 | } |
754 | | |
755 | | int |
756 | | gdev_pdf_end_transparency_mask(gx_device *dev, |
757 | | gs_gstate *pgs) |
758 | 0 | { |
759 | 0 | return 0; |
760 | 0 | } |