xref: /plan9/sys/src/cmd/gs/src/gdevpdft.c (revision 8deabd962e84f51c67a12f970084955d97d8a8f2)
1 /* Copyright (C) 1996, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevpdft.c,v 1.52 2005/09/06 22:21:14 leonardo Exp $ */
18 /* transparency processing for PDF-writing driver */
19 #include "gx.h"
20 #include "string_.h"
21 #include "gserrors.h"
22 #include "gstrans.h"
23 #include "gscolor2.h"
24 #include "gzstate.h"
25 #include "gdevpdfx.h"
26 #include "gdevpdfg.h"
27 #include "gdevpdfo.h"
28 
29 private int
pdf_make_soft_mask_dict(gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams)30 pdf_make_soft_mask_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams)
31 {
32     pdf_resource_t *pres_soft_mask_dict = 0;
33     cos_dict_t *soft_mask_dict;
34     int code;
35 
36     /* Fixme : merge redundant objects. */
37     code = pdf_alloc_resource(pdev, resourceSoftMaskDict, gs_no_id, &pres_soft_mask_dict, -1);
38     if (code < 0)
39 	return code;
40     cos_become(pres_soft_mask_dict->object, cos_type_dict);
41     pdev->pres_soft_mask_dict = pres_soft_mask_dict;
42     soft_mask_dict = (cos_dict_t *)pres_soft_mask_dict->object;
43     code = cos_dict_put_c_key_string(soft_mask_dict, "/S",
44 	    pparams->subtype == TRANSPARENCY_MASK_Alpha ? (byte *)"/Alpha" : (byte *)"/Luminosity",
45 	    pparams->subtype == TRANSPARENCY_MASK_Alpha ? 6 : 11);
46     if (code < 0)
47 	return code;
48     if (pparams->Background_components) {
49 	cos_array_t *Background;
50 
51 	Background = cos_array_from_floats(pdev, pparams->Background,
52 		    pparams->Background_components, "pdf_write_soft_mask_dict");
53 	if (Background == NULL)
54 	    return_error(gs_error_VMerror);
55 	code = cos_dict_put_c_key_object(soft_mask_dict, "/BC", (cos_object_t *)Background);
56 	if (code < 0)
57 	    return code;
58     }
59     if (pparams->transfer_function != NULL) {
60 	long id;
61 	char buf[20];
62 
63 	code = pdf_write_function(pdev, pparams->transfer_function, &id);
64 	if (code < 0)
65 	    return code;
66 	sprintf(buf, " %ld 0 R", id);
67 	code = cos_dict_put_c_key_string(soft_mask_dict, "/TR", (const byte *)buf, strlen(buf));
68 	if (code < 0)
69 	    return code;
70     }
71     return 0;
72 
73 }
74 
75 private int
pdf_make_group_dict(gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams,const gs_imager_state * pis,cos_dict_t ** pdict)76 pdf_make_group_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
77 			    const gs_imager_state * pis, cos_dict_t **pdict)
78 {
79     pdf_resource_t *pres_group;
80     cos_dict_t *group_dict;
81     int code;
82     const gs_state *gstate = gx_hld_get_gstate_ptr(pis);
83     cos_value_t cs_value;
84 
85     code = pdf_alloc_resource(pdev, resourceGroup, gs_no_id, &pres_group, -1);
86     if (code < 0)
87 	return code;
88     cos_become(pres_group->object, cos_type_dict);
89     group_dict = (cos_dict_t *)pres_group->object;
90     code = cos_dict_put_c_key_string(group_dict, "/Type", (const byte *)"/Group", 6);
91     if (code < 0)
92 	return code;
93     code = cos_dict_put_c_key_string(group_dict, "/S", (const byte *)"/Transparency", 13);
94     if (code < 0)
95 	return code;
96     if (pparams->Isolated) {
97 	code = cos_dict_put_c_key_bool(group_dict, "/I", true);
98 	if (code < 0)
99 	    return code;
100     }
101     if (pparams->Knockout) {
102 	code = cos_dict_put_c_key_bool(group_dict, "/K", true);
103 	if (code < 0)
104 	    return code;
105     }
106     if (gstate != NULL) {
107 	const gs_color_space *cs = gstate->color_space;
108 
109 	code = pdf_color_space(pdev, &cs_value, NULL, cs,
110 		&pdf_color_space_names, false);
111 	if (code < 0)
112 	    return code;
113 	code = cos_dict_put_c_key(group_dict, "/CS", &cs_value);
114 	if (code < 0)
115 	    return code;
116     }
117     group_dict = NULL; /* The next line invalidates it. */
118     code = pdf_substitute_resource(pdev, &pres_group, resourceGroup, NULL, false);
119     if (code < 0)
120 	return code;
121     *pdict = (cos_dict_t *)pres_group->object;
122     return 0;
123 }
124 
125 private int
pdf_make_form_dict(gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams,const gs_imager_state * pis,const cos_dict_t * group_dict,cos_dict_t * form_dict)126 pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
127 			    const gs_imager_state * pis,
128 			    const cos_dict_t *group_dict, cos_dict_t *form_dict)
129 {
130     cos_array_t *bbox_array;
131     float bbox[4];
132     gs_rect bbox_rect;
133     int code;
134 
135 
136     code = gs_bbox_transform(&pparams->bbox, &ctm_only(pis), &bbox_rect);
137     if (code < 0)
138 	return code;
139     bbox[0] = bbox_rect.p.x;
140     bbox[1] = bbox_rect.p.y;
141     bbox[2] = bbox_rect.q.x;
142     bbox[3] = bbox_rect.q.y;
143     code = cos_dict_put_c_key_string(form_dict, "/Type", (const byte *)"/XObject", 8);
144     if (code < 0)
145 	return code;
146     code = cos_dict_put_c_key_string(form_dict, "/Subtype", (const byte *)"/Form", 5);
147     if (code < 0)
148 	return code;
149     code = cos_dict_put_c_key_int(form_dict, "/FormType", 1);
150     if (code < 0)
151 	return code;
152     code = cos_dict_put_c_key_string(form_dict, "/Matrix", (const byte *)"[1 0 0 1 0 0]", 13);
153     if (code < 0)
154 	return code;
155     bbox_array = cos_array_from_floats(pdev, bbox, 4, "pdf_begin_transparency_group");
156     if (bbox_array == NULL)
157 	return_error(gs_error_VMerror);
158     code = cos_dict_put_c_key_object(form_dict, "/BBox", (cos_object_t *)bbox_array);
159     if (code < 0)
160 	return code;
161     return cos_dict_put_c_key_object(form_dict, "/Group", (cos_object_t *)group_dict);
162 }
163 
164 private int
pdf_begin_transparency_group(gs_imager_state * pis,gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams)165 pdf_begin_transparency_group(gs_imager_state * pis, gx_device_pdf * pdev,
166 				const gs_pdf14trans_params_t * pparams)
167 {
168     cos_dict_t *group_dict;
169     bool in_page = is_in_page(pdev);
170     const gs_state *gstate = gx_hld_get_gstate_ptr(pis);
171     int code;
172 
173     if (gstate == NULL)
174 	return_error(gs_error_unregistered); /* Must not happen. */
175     code = pdf_make_group_dict(pdev, pparams, pis, &group_dict);
176     if (code < 0)
177 	return code;
178     code = pdf_open_page(pdev, PDF_IN_STREAM);
179     if (code < 0)
180 	return code;
181     if (pdf_must_put_clip_path(pdev, gstate->clip_path)) {
182 	code = pdf_put_clip_path(pdev, gstate->clip_path);
183 	if (code < 0)
184 	    return code;
185     }
186     if (!in_page)
187 	pdev->pages[pdev->next_page].group_id = group_dict->id;
188     else {
189 	pdf_resource_t *pres, *pres_gstate = NULL;
190 
191 	code = pdf_prepare_drawing(pdev, pis, &pres_gstate);
192 	if (code < 0)
193 	    return code;
194 	code = pdf_end_gstate(pdev, pres_gstate);
195 	if (code < 0)
196 	    return code;
197 	code = pdf_enter_substream(pdev, resourceXObject,
198 		gs_no_id, &pres, false, pdev->params.CompressPages);
199 	if (code < 0)
200 	    return code;
201 	return pdf_make_form_dict(pdev, pparams, pis, group_dict, (cos_dict_t *)pres->object);
202     }
203     return 0;
204 }
205 
206 private int
pdf_end_transparency_group(gs_imager_state * pis,gx_device_pdf * pdev)207 pdf_end_transparency_group(gs_imager_state * pis, gx_device_pdf * pdev)
208 {
209     int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
210 
211     if (pdev->sbstack_depth == bottom) {
212 	/* We're closing the page group. */
213 	if (pdev->pages[pdev->next_page].group_id == 0)
214 	    return_error(gs_error_unregistered); /* Must not happen. */
215 	return 0;
216     } else {
217 	pdf_resource_t *pres = pdev->accumulating_substream_resource;
218 	int code;
219 	uint ignore;
220 
221 	code = pdf_exit_substream(pdev);
222 	if (code < 0)
223 	    return code;
224 	code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
225 	if (code < 0)
226 	    return code;
227 	sputc(pdev->strm,'/');
228 	sputs(pdev->strm, (const byte *)pres->rname, strlen(pres->rname), &ignore);
229 	sputs(pdev->strm, (const byte *)" Do\n", 4, &ignore);
230 	return 0;
231     }
232 }
233 
234 private int
pdf_begin_transparency_mask(gs_imager_state * pis,gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams)235 pdf_begin_transparency_mask(gs_imager_state * pis, gx_device_pdf * pdev,
236 				const gs_pdf14trans_params_t * pparams)
237 {
238     if (pparams->mask_is_image) {
239 	/* HACK :
240 	    The control comes here when
241 	    the PDF interpreter will make the PS interpreter
242 	    to interprete the mask for filling the transparency buffer
243     	    with an SMask image.
244 	    Since we handle Type 3 images as a high level objects,
245 	    we don't install the transparency buffer here
246 	    and need to skip the image enumeration for the SMask.
247 	    However we have no right method for skipping
248 	    an image enumeration due to possible side effect
249 	    of the image data proc in Postscript language.
250 	    Therefore we do enumerate the image mask and accumulate
251 	    it as a PDF stream, but don't create a reference to it.
252 	    Later it will be enumerated once again as a part of SMask-ed image,
253 	    and the pdfwrite image handler will recognize duplicated images
254 	    and won't create the second stream for same image.
255 
256 	    We could make a special workaround for
257 	    skipping mask images either in the graphics library or
258 	    in the PS code of the PDF interpreter,
259 	    but we don't want to complicate things now.
260 	    The performance leak for the second enumeration
261 	    shouldn't be harmful.
262 
263 	    So now just set a flag for pdf_end_and_do_image.
264 	*/
265 	pdev->image_mask_skip = true;
266 	return 0;
267     } else {
268 	int code;
269 
270 	code = pdf_make_soft_mask_dict(pdev, pparams);
271 	if (code < 0)
272 	    return code;
273 	code = pdf_open_page(pdev, PDF_IN_STREAM);
274 	if (code < 0)
275 	    return code;
276 	return pdf_begin_transparency_group(pis, pdev, pparams);
277     }
278 }
279 
280 private int
pdf_end_transparency_mask(gs_imager_state * pis,gx_device_pdf * pdev,const gs_pdf14trans_params_t * pparams)281 pdf_end_transparency_mask(gs_imager_state * pis, gx_device_pdf * pdev,
282 				const gs_pdf14trans_params_t * pparams)
283 {
284     if (pdev->image_mask_skip)
285 	pdev->image_mask_skip = false;
286     else {
287 	pdf_resource_t *pres = pdev->accumulating_substream_resource;
288 	int code;
289 	char buf[20];
290 
291 	code = pdf_exit_substream(pdev);
292 	if (code < 0)
293 	    return code;
294 	code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
295 	if (code < 0)
296 	    return 0;
297 	sprintf(buf, "%ld 0 R", pdf_resource_id(pres));
298 	code = cos_dict_put_c_key_string((cos_dict_t *)pdev->pres_soft_mask_dict->object,
299 		"/G", (const byte *)buf, strlen(buf));
300 	if (code < 0)
301 	    return code;
302 	code = pdf_substitute_resource(pdev, &pdev->pres_soft_mask_dict,
303 					resourceSoftMaskDict, NULL, false);
304 	if (code < 0)
305 	    return code;
306 	pis->soft_mask_id = pdev->pres_soft_mask_dict->object->id;
307 	pdev->pres_soft_mask_dict = NULL;
308     }
309     return 0;
310 }
311 
312 private int
pdf_set_blend_params(gs_imager_state * pis,gx_device_pdf * dev,const gs_pdf14trans_params_t * pparams)313 pdf_set_blend_params(gs_imager_state * pis, gx_device_pdf * dev,
314 				const gs_pdf14trans_params_t * pparams)
315 {
316     return 0;
317 }
318 
319 int
gdev_pdf_create_compositor(gx_device * dev,gx_device ** pcdev,const gs_composite_t * pct,gs_imager_state * pis,gs_memory_t * memory)320 gdev_pdf_create_compositor(gx_device *dev,
321     gx_device **pcdev, const gs_composite_t *pct,
322     gs_imager_state *pis, gs_memory_t *memory)
323 {
324     gx_device_pdf *pdev = (gx_device_pdf *)dev;
325 
326     if (pdev->HaveTransparency && pdev->CompatibilityLevel >= 1.4 &&
327 	    pct->type->comp_id == GX_COMPOSITOR_PDF14_TRANS) {
328 	gs_pdf14trans_t *pcte = (gs_pdf14trans_t *)pct;
329 	gs_pdf14trans_params_t *params = &pcte->params;
330 
331 	*pcdev = dev;
332 	switch(params->pdf14_op) {
333 	    case PDF14_PUSH_DEVICE:
334 		return 0;
335 	    case PDF14_POP_DEVICE:
336 		return 0;
337 	    case PDF14_BEGIN_TRANS_GROUP:
338 		return pdf_begin_transparency_group(pis, pdev, params);
339 	    case PDF14_END_TRANS_GROUP:
340 		return pdf_end_transparency_group(pis, pdev);
341 	    case PDF14_INIT_TRANS_MASK:
342 		return gx_init_transparency_mask(pis, params);
343 	    case PDF14_BEGIN_TRANS_MASK:
344 		return pdf_begin_transparency_mask(pis, pdev, params);
345 	    case PDF14_END_TRANS_MASK:
346 		return pdf_end_transparency_mask(pis, pdev, params);
347 	    case PDF14_SET_BLEND_PARAMS:
348 		return pdf_set_blend_params(pis, pdev, params);
349 	    default :
350 		return_error(gs_error_unregistered); /* Must not happen. */
351 	}
352     }
353     return psdf_create_compositor(dev, pcdev, pct, pis, memory);
354 }
355 
356 /* We're not sure why the folllowing device methods are never called.
357    Stub them for a while. */
358 
359 int
gdev_pdf_begin_transparency_group(gx_device * dev,const gs_transparency_group_params_t * ptgp,const gs_rect * pbbox,gs_imager_state * pis,gs_transparency_state_t ** ppts,gs_memory_t * mem)360 gdev_pdf_begin_transparency_group(gx_device *dev,
361     const gs_transparency_group_params_t *ptgp,
362     const gs_rect *pbbox,
363     gs_imager_state *pis,
364     gs_transparency_state_t **ppts,
365     gs_memory_t *mem)
366 {
367     return 0;
368 }
369 
370 int
gdev_pdf_end_transparency_group(gx_device * dev,gs_imager_state * pis,gs_transparency_state_t ** ppts)371 gdev_pdf_end_transparency_group(gx_device *dev,
372     gs_imager_state *pis,
373     gs_transparency_state_t **ppts)
374 {
375     return 0;
376 }
377 
378 int
gdev_pdf_begin_transparency_mask(gx_device * dev,const gx_transparency_mask_params_t * ptmp,const gs_rect * pbbox,gs_imager_state * pis,gs_transparency_state_t ** ppts,gs_memory_t * mem)379 gdev_pdf_begin_transparency_mask(gx_device *dev,
380     const gx_transparency_mask_params_t *ptmp,
381     const gs_rect *pbbox,
382     gs_imager_state *pis,
383     gs_transparency_state_t **ppts,
384     gs_memory_t *mem)
385 {
386     return 0;
387 }
388 
389 int
gdev_pdf_end_transparency_mask(gx_device * dev,gs_transparency_mask_t ** pptm)390 gdev_pdf_end_transparency_mask(gx_device *dev,
391     gs_transparency_mask_t **pptm)
392 {
393     return 0;
394 }
395 
396 int
gdev_pdf_discard_transparency_layer(gx_device * dev,gs_transparency_state_t ** ppts)397 gdev_pdf_discard_transparency_layer(gx_device *dev,
398     gs_transparency_state_t **ppts)
399 {
400     return 0;
401 }
402