/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 | 199 | { |
34 | 199 | pdf_resource_t *pres_soft_mask_dict = 0; |
35 | 199 | cos_dict_t *soft_mask_dict; |
36 | 199 | int code; |
37 | | |
38 | | /* Fixme : merge redundant objects. */ |
39 | 199 | code = pdf_alloc_resource(pdev, resourceSoftMaskDict, gs_no_id, &pres_soft_mask_dict, -1); |
40 | 199 | if (code < 0) |
41 | 0 | return code; |
42 | 199 | cos_become(pres_soft_mask_dict->object, cos_type_dict); |
43 | 199 | pdev->pres_soft_mask_dict = pres_soft_mask_dict; |
44 | 199 | soft_mask_dict = (cos_dict_t *)pres_soft_mask_dict->object; |
45 | 199 | code = cos_dict_put_c_key_string(soft_mask_dict, "/S", |
46 | 199 | pparams->subtype == TRANSPARENCY_MASK_Alpha ? (byte *)"/Alpha" : (byte *)"/Luminosity", |
47 | 199 | pparams->subtype == TRANSPARENCY_MASK_Alpha ? 6 : 11); |
48 | 199 | if (code < 0) |
49 | 0 | return code; |
50 | 199 | if (pparams->Background_components) { |
51 | 131 | cos_array_t *Background; |
52 | | |
53 | 131 | Background = cos_array_from_floats(pdev, pparams->Background, |
54 | 131 | pparams->Background_components, "pdf_write_soft_mask_dict"); |
55 | 131 | if (Background == NULL) |
56 | 0 | return_error(gs_error_VMerror); |
57 | 131 | code = cos_dict_put_c_key_object(soft_mask_dict, "/BC", (cos_object_t *)Background); |
58 | 131 | if (code < 0) |
59 | 0 | return code; |
60 | 131 | } |
61 | 199 | if (pdev->CompatibilityLevel <= 1.7 && pparams->transfer_function != NULL && pdev->params.TransferFunctionInfo == tfi_Preserve) { |
62 | 25 | int64_t id; |
63 | 25 | char buf[20]; |
64 | | |
65 | 25 | code = pdf_write_function(pdev, pparams->transfer_function, &id); |
66 | 25 | if (code < 0) |
67 | 0 | return code; |
68 | 25 | gs_snprintf(buf, sizeof(buf), " %"PRId64" 0 R", id); |
69 | 25 | code = cos_dict_put_c_key_string(soft_mask_dict, "/TR", (const byte *)buf, strlen(buf)); |
70 | 25 | if (code < 0) |
71 | 0 | return code; |
72 | 25 | } |
73 | 199 | return 0; |
74 | | |
75 | 199 | } |
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 | 6.39k | { |
248 | 6.39k | pdf_resource_t *pres_group; |
249 | 6.39k | cos_dict_t *group_dict; |
250 | 6.39k | int code = 0; |
251 | 6.39k | cos_value_t cs_value; |
252 | | |
253 | 6.39k | code = pdf_alloc_resource(pdev, resourceGroup, gs_no_id, &pres_group, -1); |
254 | 6.39k | if (code < 0) |
255 | 0 | return code; |
256 | 6.39k | cos_become(pres_group->object, cos_type_dict); |
257 | 6.39k | group_dict = (cos_dict_t *)pres_group->object; |
258 | 6.39k | code = cos_dict_put_c_key_string(group_dict, "/Type", (const byte *)"/Group", 6); |
259 | 6.39k | if (code < 0) |
260 | 0 | return code; |
261 | 6.39k | code = cos_dict_put_c_key_string(group_dict, "/S", (const byte *)"/Transparency", 13); |
262 | 6.39k | if (code < 0) |
263 | 0 | return code; |
264 | 6.39k | if (pparams->Isolated) { |
265 | 4.10k | code = cos_dict_put_c_key_bool(group_dict, "/I", true); |
266 | 4.10k | if (code < 0) |
267 | 0 | return code; |
268 | 4.10k | } |
269 | 6.39k | if (pparams->Knockout) { |
270 | 0 | code = cos_dict_put_c_key_bool(group_dict, "/K", true); |
271 | 0 | if (code < 0) |
272 | 0 | return code; |
273 | 0 | } |
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 | 6.39k | if (pgs != NULL && pparams->ColorSpace != NULL) { |
280 | 1.99k | const gs_color_space *cs = pparams->ColorSpace; |
281 | | |
282 | 1.99k | 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 | 1.99k | else |
286 | 1.99k | code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, pparams->ColorSpace, |
287 | 1.99k | &pdf_color_space_names, false, NULL, 0, false); |
288 | 1.99k | if (code < 0) |
289 | 0 | return code; |
290 | 1.99k | switch(pdev->params.BlendConversionStrategy) { |
291 | 0 | case bcs_Managed: |
292 | 0 | if (pdev->params.ColorConversionStrategy != ccs_LeaveColorUnchanged) { |
293 | 0 | if (pdev->params.ColorConversionStrategy < ccs_CMYK || pdev->params.ColorConversionStrategy == ccs_ByObjectType) |
294 | 0 | return_error(gs_error_rangecheck); |
295 | 0 | code = WriteDefaultSpaces(pdev, pgs, &cs_value); |
296 | 0 | } |
297 | 0 | if (code < 0) |
298 | 0 | return code; |
299 | | /* Fall through */ |
300 | 0 | case bcs_None: |
301 | 0 | code = cos_dict_put_c_key(group_dict, "/CS", &cs_value); |
302 | 0 | break; |
303 | 1.99k | case bcs_Simple: |
304 | 1.99k | switch(pdev->params.ColorConversionStrategy) { |
305 | 1.99k | case ccs_LeaveColorUnchanged: |
306 | 1.99k | code = cos_dict_put_c_key(group_dict, "/CS", &cs_value); |
307 | 1.99k | break; |
308 | 0 | case ccs_UseDeviceIndependentColor: |
309 | 0 | case ccs_UseDeviceIndependentColorForImages: |
310 | 0 | case ccs_ByObjectType: |
311 | 0 | case ccs_sRGB: |
312 | 0 | return_error(gs_error_rangecheck); |
313 | 0 | break; |
314 | | |
315 | 0 | case ccs_CMYK: |
316 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceCMYK", 11); |
317 | 0 | break; |
318 | 0 | case ccs_Gray: |
319 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceGray", 11); |
320 | 0 | break; |
321 | 0 | case ccs_RGB: |
322 | 0 | code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceRGB", 10); |
323 | 0 | break; |
324 | 1.99k | } |
325 | 1.99k | break; |
326 | 1.99k | } |
327 | 1.99k | } |
328 | 6.39k | if (code < 0) |
329 | 0 | return code; |
330 | 6.39k | group_dict = NULL; /* The next line invalidates it. */ |
331 | 6.39k | code = pdf_substitute_resource(pdev, &pres_group, resourceGroup, NULL, false); |
332 | 6.39k | if (code < 0) |
333 | 0 | return code; |
334 | 6.39k | pres_group->where_used |= pdev->used_mask; |
335 | 6.39k | *pdict = (cos_dict_t *)pres_group->object; |
336 | 6.39k | return 0; |
337 | 6.39k | } |
338 | | |
339 | | static int |
340 | | pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams, |
341 | | const gs_gstate * pgs, |
342 | | const cos_dict_t *group_dict, cos_dict_t *form_dict) |
343 | 2.87k | { |
344 | 2.87k | cos_array_t *bbox_array; |
345 | 2.87k | float bbox[4]; |
346 | 2.87k | gs_rect bbox_rect; |
347 | 2.87k | int code; |
348 | | |
349 | 2.87k | code = gs_bbox_transform(&pparams->bbox, &ctm_only(pgs), &bbox_rect); |
350 | 2.87k | if (code < 0) |
351 | 0 | return code; |
352 | 2.87k | bbox[0] = bbox_rect.p.x; |
353 | 2.87k | bbox[1] = bbox_rect.p.y; |
354 | 2.87k | bbox[2] = bbox_rect.q.x; |
355 | 2.87k | bbox[3] = bbox_rect.q.y; |
356 | 2.87k | code = cos_dict_put_c_key_string(form_dict, "/Type", (const byte *)"/XObject", 8); |
357 | 2.87k | if (code < 0) |
358 | 0 | return code; |
359 | 2.87k | code = cos_dict_put_c_key_string(form_dict, "/Subtype", (const byte *)"/Form", 5); |
360 | 2.87k | if (code < 0) |
361 | 0 | return code; |
362 | 2.87k | code = cos_dict_put_c_key_int(form_dict, "/FormType", 1); |
363 | 2.87k | if (code < 0) |
364 | 0 | return code; |
365 | 2.87k | code = cos_dict_put_c_key_string(form_dict, "/Matrix", (const byte *)"[1 0 0 1 0 0]", 13); |
366 | 2.87k | if (code < 0) |
367 | 0 | return code; |
368 | 2.87k | bbox_array = cos_array_from_floats(pdev, bbox, 4, "pdf_begin_transparency_group"); |
369 | 2.87k | if (bbox_array == NULL) |
370 | 0 | return_error(gs_error_VMerror); |
371 | 2.87k | code = cos_dict_put_c_key_object(form_dict, "/BBox", (cos_object_t *)bbox_array); |
372 | 2.87k | if (code < 0) |
373 | 0 | return code; |
374 | 2.87k | if (pdev->PendingOC != 0) { |
375 | 25 | if (pdev->CompatibilityLevel < 1.4999) { |
376 | 0 | if (pdev->PDFA) { |
377 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
378 | 0 | case 0: |
379 | 0 | emprintf(pdev->memory, |
380 | 0 | "Optional Content not valid in this version of PDF, reverting to normal PDF output\n"); |
381 | 0 | pdev->AbortPDFAX = true; |
382 | 0 | pdev->PDFA = 0; |
383 | 0 | break; |
384 | 0 | case 1: |
385 | 0 | emprintf(pdev->memory, |
386 | 0 | "Optional Content not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n"); |
387 | 0 | break; |
388 | 0 | case 2: |
389 | 0 | emprintf(pdev->memory, |
390 | 0 | "Optional Content not valid in this version of PDF, aborting conversion\n"); |
391 | 0 | return_error (gs_error_typecheck); |
392 | 0 | break; |
393 | 0 | default: |
394 | 0 | emprintf(pdev->memory, |
395 | 0 | "Optional Content not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
396 | 0 | pdev->AbortPDFAX = true; |
397 | 0 | pdev->PDFA = 0; |
398 | 0 | break; |
399 | 0 | } |
400 | 0 | } else { |
401 | 0 | emprintf(pdev->memory, |
402 | 0 | "Optional Content not valid in this version of PDF. Dropping feature to preserve compatibility\n"); |
403 | 0 | } |
404 | 25 | } else { |
405 | 25 | char str[256]; |
406 | 25 | gs_param_string param; |
407 | 25 | cos_object_t *pco = NULL; |
408 | | |
409 | 25 | param.data = (const byte *)pdev->PendingOC; |
410 | 25 | param.size = strlen(pdev->PendingOC); |
411 | 25 | code = pdf_refer_named(pdev, ¶m, &pco); |
412 | 25 | if(code < 0) |
413 | 0 | return code; |
414 | | |
415 | 25 | gs_snprintf(str, sizeof(str), "%"PRId64" 0 R", pco->id); |
416 | 25 | code = cos_dict_put_string_copy(form_dict, "/OC", str); |
417 | 25 | if (code < 0) |
418 | 0 | return code; |
419 | | |
420 | 25 | gs_free_object(pdev->memory->non_gc_memory, pdev->PendingOC, ""); |
421 | 25 | pdev->PendingOC = NULL; |
422 | 25 | } |
423 | 25 | } |
424 | 2.87k | return cos_dict_put_c_key_object(form_dict, "/Group", (cos_object_t *)group_dict); |
425 | 2.87k | } |
426 | | |
427 | | static int |
428 | | pdf_begin_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev, |
429 | | const gs_pdf14trans_params_t * pparams, bool page_group) |
430 | 6.39k | { |
431 | 6.39k | cos_dict_t *group_dict; |
432 | 6.39k | int code; |
433 | | |
434 | 6.39k | if (pgs == NULL) |
435 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
436 | 6.39k | code = pdf_make_group_dict(pdev, pparams, pgs, &group_dict); |
437 | 6.39k | if (code < 0) |
438 | 0 | return code; |
439 | 6.39k | code = pdf_open_page(pdev, PDF_IN_STREAM); |
440 | 6.39k | if (code < 0) |
441 | 0 | return code; |
442 | 6.39k | code = pdf_check_soft_mask(pdev, pgs); |
443 | 6.39k | if (code < 0) |
444 | 0 | return code; |
445 | 6.39k | if (pdf_must_put_clip_path(pdev, pgs->clip_path)) { |
446 | 1.40k | code = pdf_put_clip_path(pdev, pgs->clip_path); |
447 | 1.40k | if (code < 0) |
448 | 0 | return code; |
449 | 1.40k | } |
450 | 6.39k | if (page_group) |
451 | 556 | pdev->pages[pdev->next_page].group_id = group_dict->id; |
452 | 5.83k | else if (pparams->image_with_SMask) { |
453 | | /* An internal group for the image implementation. |
454 | | See doimagesmask in gs/lib/pdf_draw.ps . |
455 | | Just set a flag for skipping pdf_end_transparency_group. */ |
456 | 2.96k | pdev->image_with_SMask |= 1 << ++pdev->FormDepth; |
457 | 2.96k | pdev->PatternsSinceForm = 0; |
458 | 2.96k | } else { |
459 | 2.87k | pdf_resource_t *pres, *pres_gstate = NULL; |
460 | 2.87k | cos_dict_t *pcd = NULL, *pcd_Resources = NULL; |
461 | | |
462 | 2.87k | code = pdf_prepare_drawing(pdev, pgs, &pres_gstate, false); |
463 | 2.87k | if (code < 0) |
464 | 0 | return code; |
465 | 2.87k | code = pdf_end_gstate(pdev, pres_gstate); |
466 | 2.87k | if (code < 0) |
467 | 0 | return code; |
468 | 2.87k | code = pdf_enter_substream(pdev, resourceXObject, |
469 | 2.87k | gs_no_id, &pres, false, pdev->params.CompressPages); |
470 | 2.87k | if (code < 0) |
471 | 0 | return code; |
472 | 2.87k | pdev->FormDepth++; |
473 | 2.87k | pdev->PatternsSinceForm = 0; |
474 | 2.87k | code = pdf_make_form_dict(pdev, pparams, pgs, group_dict, (cos_dict_t *)pres->object); |
475 | 2.87k | if (code < 0) |
476 | 0 | return code; |
477 | | |
478 | | /* Create a Resources dictionary and add it to the form dictionary */ |
479 | 2.87k | pcd = cos_stream_dict((cos_stream_t *)pres->object); |
480 | 2.87k | pcd_Resources = cos_dict_alloc(pdev, "pdf_group(Resources)"); |
481 | 2.87k | if (pcd == NULL || pcd_Resources == NULL) |
482 | 0 | return_error(gs_error_VMerror); |
483 | 2.87k | code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources)); |
484 | 2.87k | pdev->substream_Resources = pcd_Resources; |
485 | 2.87k | return code; |
486 | 2.87k | } |
487 | 3.52k | return 0; |
488 | 6.39k | } |
489 | | |
490 | | static int |
491 | | pdf_end_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev) |
492 | 6.19k | { |
493 | 6.19k | int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0); |
494 | | |
495 | 6.19k | if (!is_in_page(pdev) && pdev->sbstack_depth == 0) |
496 | 0 | return 0; /* A Group definition at the page level, handled separately. */ |
497 | 6.19k | if (pdev->image_with_SMask & (1 << pdev->FormDepth)) { |
498 | | /* An internal group for the image implementation. |
499 | | See pdf_begin_transparency_group. */ |
500 | 2.96k | pdev->image_with_SMask &= ~(1 << pdev->FormDepth--); |
501 | 2.96k | pdev->PatternsSinceForm = 0; |
502 | 2.96k | return 0; |
503 | 3.22k | } else if (pdev->sbstack_depth == bottom) { |
504 | | /* We're closing the page group. */ |
505 | 556 | if (pdev->pages[pdev->next_page].group_id == 0) |
506 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
507 | 556 | return 0; |
508 | 2.67k | } else { |
509 | 2.67k | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
510 | 2.67k | int code; |
511 | 2.67k | uint ignore; |
512 | | |
513 | 2.67k | if (pres == NULL) |
514 | 0 | return_error(gs_error_unregistered); |
515 | 2.67k | pdev->FormDepth--; |
516 | 2.67k | pdev->PatternsSinceForm = 0; |
517 | 2.67k | code = pdf_exit_substream(pdev); |
518 | 2.67k | if (code < 0) |
519 | 0 | return code; |
520 | 2.67k | code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false); |
521 | 2.67k | if (code < 0) |
522 | 0 | return code; |
523 | | /* We need to update the 'where_used' field, in case we substituted a resource */ |
524 | 2.67k | pres->where_used |= pdev->used_mask; |
525 | 2.67k | sputc(pdev->strm,'/'); |
526 | 2.67k | sputs(pdev->strm, (const byte *)pres->rname, strlen(pres->rname), &ignore); |
527 | 2.67k | sputs(pdev->strm, (const byte *)" Do\n", 4, &ignore); |
528 | 2.67k | code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pres); |
529 | 2.67k | return code; |
530 | 2.67k | } |
531 | 6.19k | } |
532 | | |
533 | | static int |
534 | | pdf_begin_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev, |
535 | | const gs_pdf14trans_params_t * pparams) |
536 | 3.17k | { |
537 | 3.17k | if (pparams->subtype == TRANSPARENCY_MASK_None) { |
538 | 2.98k | int code; |
539 | 2.98k | pdf_resource_t *pres = 0L; |
540 | | |
541 | | /* reset the soft mask ID. Apparently this is only used by pdfwrite, if we don't |
542 | | * reset it, then the pdf_prepare_drawing code doesn't know that the SMask has |
543 | | * changed, and so doesn't write out the GState |
544 | | */ |
545 | 2.98k | pgs->soft_mask_id = 0; |
546 | 2.98k | code = pdf_prepare_drawing(pdev, pgs, &pres, false); |
547 | 2.98k | if (code == gs_error_interrupt) { |
548 | | /* */ |
549 | | /* Not in an appropriate context. Do not restore the soft_mask_id. |
550 | | Otherwise any group push that occurs following this will use that |
551 | | softmask, which clearly should be NONE here. |
552 | | */ |
553 | | /* ignore return code, we don't care about this graphics state as we aren't |
554 | | * emitting it anyway |
555 | | */ |
556 | 521 | pdf_end_gstate(pdev, pres); |
557 | 521 | return 0; |
558 | 521 | } |
559 | 2.45k | if (code < 0) |
560 | 0 | return code; |
561 | 2.45k | code = pdf_end_gstate(pdev, pres); |
562 | 2.45k | if (code < 0) |
563 | 0 | return code; |
564 | 2.45k | return 0; |
565 | 2.45k | } |
566 | 199 | if (pparams->mask_is_image) { |
567 | | /* HACK : |
568 | | The control comes here when |
569 | | the PDF interpreter will make the PS interpreter |
570 | | to interprete the mask for filling the transparency buffer |
571 | | with an SMask image. |
572 | | Since we handle Type 3 images as a high level objects, |
573 | | we don't install the transparency buffer here |
574 | | and need to skip the image enumeration for the SMask. |
575 | | However we have no right method for skipping |
576 | | an image enumeration due to possible side effect |
577 | | of the image data proc in Postscript language. |
578 | | Therefore we do enumerate the image mask and accumulate |
579 | | it as a PDF stream, but don't create a reference to it. |
580 | | Later it will be enumerated once again as a part of SMask-ed image, |
581 | | and the pdfwrite image handler will recognize duplicated images |
582 | | and won't create the second stream for same image. |
583 | | |
584 | | We could make a special workaround for |
585 | | skipping mask images either in the graphics library or |
586 | | in the PS code of the PDF interpreter, |
587 | | but we don't want to complicate things now. |
588 | | The performance leak for the second enumeration |
589 | | shouldn't be harmful. |
590 | | |
591 | | So now just set a flag for pdf_end_and_do_image. |
592 | | */ |
593 | 0 | pdev->image_mask_skip = true; |
594 | 0 | return 0; |
595 | 199 | } else { |
596 | 199 | int code; |
597 | | |
598 | 199 | pdev->smask_construction = true; |
599 | 199 | code = pdf_make_soft_mask_dict(pdev, pparams); |
600 | 199 | if (code < 0) |
601 | 0 | return code; |
602 | 199 | code = pdf_open_page(pdev, PDF_IN_STREAM); |
603 | 199 | if (code < 0) |
604 | 0 | return code; |
605 | 199 | return pdf_begin_transparency_group(pgs, pdev, pparams, 0); |
606 | 199 | } |
607 | 199 | } |
608 | | |
609 | | static int |
610 | | pdf_end_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev, |
611 | | const gs_pdf14trans_params_t * pparams) |
612 | 199 | { |
613 | 199 | pdev->smask_construction = false; |
614 | 199 | if (pdev->image_mask_skip) |
615 | 0 | pdev->image_mask_skip = false; |
616 | 199 | else { |
617 | 199 | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
618 | 199 | int code; |
619 | 199 | char buf[20]; |
620 | | |
621 | 199 | if (pres == NULL) |
622 | 0 | return_error(gs_error_unregistered); |
623 | 199 | code = pdf_exit_substream(pdev); |
624 | 199 | if (code < 0) |
625 | 0 | return code; |
626 | 199 | code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false); |
627 | 199 | if (code < 0) |
628 | 0 | return 0; |
629 | | /* We need to update the 'where_used' field, in case we substituted a resource */ |
630 | 199 | pres->where_used |= pdev->used_mask; |
631 | 199 | gs_snprintf(buf, sizeof(buf), "%"PRId64" 0 R", pdf_resource_id(pres)); |
632 | 199 | if (pdev->pres_soft_mask_dict == 0L) { |
633 | | /* something went horribly wrong, we have an 'end' wihtout a matching 'begin' |
634 | | * Give up, throw an error. |
635 | | */ |
636 | 0 | return_error(gs_error_undefined); |
637 | 0 | } |
638 | 199 | code = cos_dict_put_c_key_string((cos_dict_t *)pdev->pres_soft_mask_dict->object, |
639 | 199 | "/G", (const byte *)buf, strlen(buf)); |
640 | 199 | if (code < 0) |
641 | 0 | return code; |
642 | 199 | code = pdf_substitute_resource(pdev, &pdev->pres_soft_mask_dict, |
643 | 199 | resourceSoftMaskDict, NULL, false); |
644 | 199 | if (code < 0) |
645 | 0 | return code; |
646 | 199 | pdev->pres_soft_mask_dict->where_used |= pdev->used_mask; |
647 | 199 | pgs->soft_mask_id = pdev->pres_soft_mask_dict->object->id; |
648 | 199 | pdev->pres_soft_mask_dict = NULL; |
649 | | /* We called pdf_start_trnasparency_group (see pdf_begin_transparency_mask |
650 | | * above) but we don't call pdf_end_transparency_group, so we must reduce |
651 | | * the FormDepth ourselves. |
652 | | */ |
653 | 199 | pdev->FormDepth--; |
654 | 199 | pdev->PatternsSinceForm = 0; |
655 | 199 | } |
656 | 199 | return 0; |
657 | 199 | } |
658 | | |
659 | | static int |
660 | | pdf_set_blend_params(gs_gstate * pgs, gx_device_pdf * dev, |
661 | | const gs_pdf14trans_params_t * pparams) |
662 | 0 | { |
663 | 0 | return 0; |
664 | 0 | } |
665 | | |
666 | | int |
667 | | gdev_pdf_composite(gx_device *dev, |
668 | | gx_device **pcdev, const gs_composite_t *pct, |
669 | | gs_gstate *pgs, gs_memory_t *memory, gx_device *cdev) |
670 | 344k | { |
671 | 344k | gx_device_pdf *pdev = (gx_device_pdf *)dev; |
672 | | |
673 | 344k | if (pdev->HaveTransparency && pdev->CompatibilityLevel >= 1.4 && |
674 | 331k | pct->type->comp_id == GX_COMPOSITOR_PDF14_TRANS && |
675 | 133k | pdev->PDFA != 1) { |
676 | 133k | gs_pdf14trans_t *pcte = (gs_pdf14trans_t *)pct; |
677 | 133k | gs_pdf14trans_params_t *params = &pcte->params; |
678 | | |
679 | 133k | *pcdev = dev; |
680 | 133k | switch(params->pdf14_op) { |
681 | 1.19k | case PDF14_PUSH_DEVICE: |
682 | 1.19k | return 0; |
683 | 1.19k | case PDF14_POP_DEVICE: |
684 | 1.19k | return 0; |
685 | 0 | case PDF14_ABORT_DEVICE: |
686 | 0 | return 0; |
687 | 556 | case PDF14_BEGIN_TRANS_PAGE_GROUP: |
688 | 556 | return pdf_begin_transparency_group(pgs, pdev, params, 1); |
689 | 5.64k | case PDF14_BEGIN_TRANS_GROUP: |
690 | 5.64k | return pdf_begin_transparency_group(pgs, pdev, params, 0); |
691 | 6.19k | case PDF14_END_TRANS_GROUP: |
692 | 6.19k | return pdf_end_transparency_group(pgs, pdev); |
693 | 56.5k | case PDF14_BEGIN_TRANS_TEXT_GROUP: |
694 | 56.5k | return 0; |
695 | 58.2k | case PDF14_END_TRANS_TEXT_GROUP: |
696 | 58.2k | return 0; |
697 | 3.17k | case PDF14_BEGIN_TRANS_MASK: |
698 | 3.17k | return pdf_begin_transparency_mask(pgs, pdev, params); |
699 | 199 | case PDF14_END_TRANS_MASK: |
700 | 199 | return pdf_end_transparency_mask(pgs, pdev, params); |
701 | 0 | case PDF14_SET_BLEND_PARAMS: |
702 | 0 | return pdf_set_blend_params(pgs, pdev, params); |
703 | 0 | case PDF14_PUSH_TRANS_STATE: |
704 | 0 | return 0; |
705 | 112 | case PDF14_POP_TRANS_STATE: |
706 | 112 | return 0; |
707 | 199 | case PDF14_PUSH_SMASK_COLOR: |
708 | 199 | return 0; |
709 | 199 | case PDF14_POP_SMASK_COLOR: |
710 | 199 | return 0; |
711 | | |
712 | 0 | default : |
713 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
714 | 133k | } |
715 | 0 | return 0; |
716 | 133k | } |
717 | 211k | return psdf_composite(dev, pcdev, pct, pgs, memory, cdev); |
718 | 344k | } |
719 | | |
720 | | /* We're not sure why the folllowing device methods are never called. |
721 | | Stub them for a while. */ |
722 | | |
723 | | int |
724 | | gdev_pdf_begin_transparency_group(gx_device *dev, |
725 | | const gs_transparency_group_params_t *ptgp, |
726 | | const gs_rect *pbbox, |
727 | | gs_gstate *pgs, gs_memory_t *mem) |
728 | 0 | { |
729 | 0 | return 0; |
730 | 0 | } |
731 | | |
732 | | int |
733 | | gdev_pdf_end_transparency_group(gx_device *dev, |
734 | | gs_gstate *pgs) |
735 | 0 | { |
736 | 0 | return 0; |
737 | 0 | } |
738 | | |
739 | | int |
740 | | gdev_pdf_begin_transparency_mask(gx_device *dev, |
741 | | const gx_transparency_mask_params_t *ptmp, |
742 | | const gs_rect *pbbox, |
743 | | gs_gstate *pgs, gs_memory_t *mem) |
744 | 0 | { |
745 | 0 | return 0; |
746 | 0 | } |
747 | | |
748 | | int |
749 | | gdev_pdf_end_transparency_mask(gx_device *dev, |
750 | | gs_gstate *pgs) |
751 | 0 | { |
752 | 0 | return 0; |
753 | 0 | } |