1/*---------------------------------------------------------------------------*
2 |              PDFlib - A library for generating PDF on the fly             |
3 +---------------------------------------------------------------------------+
4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. |
5 +---------------------------------------------------------------------------+
6 |                                                                           |
7 |    This software is subject to the PDFlib license. It is NOT in the       |
8 |    public domain. Extended versions and commercial licenses are           |
9 |    available, please check http://www.pdflib.com.                         |
10 |                                                                           |
11 *---------------------------------------------------------------------------*/
12
13/* $Id: p_shading.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib routines for smooth shading
16 *
17 */
18
19#include "p_intern.h"
20#include "p_font.h"
21
22typedef enum
23{
24    shnone	= 0,
25    axial	= 2,
26    radial	= 3
27} pdf_shadingtype_e;
28
29struct pdf_shading_s {
30    pdc_id	obj_id;			/* object id of this shading */
31    pdc_bool	used_on_current_page;	/* this shading used on current page */
32};
33
34void
35pdf_init_shadings(PDF *p)
36{
37    int i;
38
39    p->shadings_number = 0;
40    p->shadings_capacity = SHADINGS_CHUNKSIZE;
41
42    p->shadings = (pdf_shading *) pdc_malloc(p->pdc,
43	sizeof(pdf_shading) * p->shadings_capacity, "pdf_init_shadings");
44
45    for (i = 0; i < p->shadings_capacity; i++) {
46	p->shadings[i].used_on_current_page = pdc_false;
47	p->shadings[i].obj_id = PDC_BAD_ID;
48    }
49}
50
51static void
52pdf_grow_shadings(PDF *p)
53{
54    int i;
55
56    p->shadings = (pdf_shading *) pdc_realloc(p->pdc, p->shadings,
57	sizeof(pdf_shading) * 2 * p->shadings_capacity, "pdf_grow_shadings");
58
59    for (i = p->shadings_capacity; i < 2 * p->shadings_capacity; i++) {
60	p->shadings[i].used_on_current_page = pdc_false;
61	p->shadings[i].obj_id = PDC_BAD_ID;
62    }
63
64    p->shadings_capacity *= 2;
65}
66
67void
68pdf_write_page_shadings(PDF *p)
69{
70    int i, total = 0;
71
72    for (i = 0; i < p->shadings_number; i++)
73	if (p->shadings[i].used_on_current_page)
74	    total++;
75
76    if (total > 0) {
77	pdc_puts(p->out, "/Shading");
78
79	pdc_begin_dict(p->out);			/* Shading */
80
81	for (i = 0; i < p->shadings_number; i++) {
82	    if (p->shadings[i].used_on_current_page) {
83		p->shadings[i].used_on_current_page = pdc_false; /* reset */
84		pdc_printf(p->out, "/Sh%d %ld 0 R\n", i, p->shadings[i].obj_id);
85	    }
86	}
87
88	pdc_end_dict(p->out);			/* Shading */
89    }
90}
91
92void
93pdf_cleanup_shadings(PDF *p)
94{
95    if (p->shadings) {
96	pdc_free(p->pdc, p->shadings);
97	p->shadings = NULL;
98    }
99}
100
101static const pdc_defopt pdf_shading_pattern_options[] =
102{
103    {"gstate", pdc_gstatehandle, 0, 1, 1, 0, 0, NULL},
104    PDC_OPT_TERMINATE
105};
106
107PDFLIB_API int PDFLIB_CALL
108PDF_shading_pattern(PDF *p, int shading, const char *optlist)
109{
110    static const char fn[] = "PDF_shading_pattern";
111    pdc_resopt *results;
112    pdc_clientdata data;
113    int gstate = -1;
114    int retval = -1;
115
116    if (!pdf_enter_api(p, fn,
117        (pdf_state) (pdf_state_document | pdf_state_page | pdf_state_font),
118	"(p[%p], %d, \"%s\")", (void *) p, shading, optlist)) {
119        PDF_RETURN_HANDLE(p, retval)
120    }
121
122    if (p->compatibility == PDC_1_3)
123	pdc_error(p->pdc, PDF_E_SHADING13, 0, 0, 0, 0);
124
125    PDF_INPUT_HANDLE(p, shading)
126    pdf_check_handle(p, shading, pdc_shadinghandle);
127
128    if (optlist && strlen(optlist)) {
129        data.maxgstate = p->extgstates_number - 1;
130        data.hastobepos = p->hastobepos;
131
132	results = pdc_parse_optionlist(p->pdc,
133            optlist, pdf_shading_pattern_options, &data, pdc_true);
134
135	(void) pdc_get_optvalues(p->pdc, "gstate", results, &gstate, NULL);
136
137	pdc_cleanup_optionlist(p->pdc, results);
138    }
139
140    if (p->pattern_number == p->pattern_capacity)
141	pdf_grow_pattern(p);
142
143    if (PDF_GET_STATE(p) == pdf_state_page)
144	pdf_end_contents_section(p);
145
146    							/* Pattern object */
147    p->pattern[p->pattern_number].obj_id = pdc_begin_obj(p->out, PDC_NEW_ID);
148
149    /* Shadings don't have a painttype, but this signals to the
150     * code which writes the pattern usage that no color values
151     * will be required when setting the pattern color space.
152     */
153    p->pattern[p->pattern_number].painttype = 1;
154
155    pdc_begin_dict(p->out);				/* Pattern dict*/
156
157    pdc_puts(p->out, "/PatternType 2\n");		/* shading pattern */
158
159    pdc_printf(p->out, "/Shading %ld 0 R\n", p->shadings[shading].obj_id);
160
161    p->shadings[shading].used_on_current_page = pdc_true;
162
163    if (gstate != -1) {
164	pdc_printf(p->out, "/ExtGState %ld 0 R\n",
165	    pdf_get_gstate_id(p, gstate));
166    }
167
168    pdc_end_dict(p->out);				/* Pattern dict*/
169    pdc_end_obj(p->out);				/* Pattern object */
170
171    if (PDF_GET_STATE(p) == pdf_state_page)
172	pdf_begin_contents_section(p);
173
174    retval = p->pattern_number;
175    p->pattern_number++;
176    PDF_RETURN_HANDLE(p, retval)
177}
178
179PDFLIB_API void PDFLIB_CALL
180PDF_shfill(PDF *p, int shading)
181{
182    static const char fn[] = "PDF_shfill";
183    int legal_states;
184
185    if (PDF_GET_STATE(p) == pdf_state_glyph && !pdf_get_t3colorized(p))
186        legal_states = pdf_state_page | pdf_state_pattern | pdf_state_template;
187
188    else if (PDF_GET_STATE(p) == pdf_state_pattern &&
189	p->pattern[p->pattern_number-1].painttype == 2)
190        legal_states = pdf_state_page | pdf_state_glyph | pdf_state_template;
191
192    else
193        legal_states = pdf_state_content;
194
195    if (!pdf_enter_api(p, fn, (pdf_state) legal_states,
196	"(p[%p], %d)\n", (void *) p, shading))
197	return;
198
199    if (p->compatibility == PDC_1_3)
200	pdc_error(p->pdc, PDF_E_SHADING13, 0, 0, 0, 0);
201
202    PDF_INPUT_HANDLE(p, shading)
203    pdf_check_handle(p, shading, pdc_shadinghandle);
204
205    if (PDF_GET_STATE(p) == pdf_state_path)
206	pdf_end_path(p);
207
208    pdf_end_text(p);
209
210    pdc_printf(p->out, "/Sh%d sh\n", shading);
211
212    p->shadings[shading].used_on_current_page = pdc_true;
213}
214
215static const pdc_defopt pdf_shading_options[] =
216{
217    {"N", pdc_floatlist, PDC_OPT_NOZERO, 1, 1, 0, PDC_FLOAT_MAX, NULL},
218    {"r0", pdc_floatlist, PDC_OPT_NONE, 1, 1, 0, PDC_FLOAT_MAX, NULL},
219    {"r1", pdc_floatlist, PDC_OPT_NONE, 1, 1, 0, PDC_FLOAT_MAX, NULL},
220    {"extend0", pdc_booleanlist, PDC_OPT_NONE, 0, 1, 0, 1, NULL},
221    {"extend1", pdc_booleanlist, PDC_OPT_NONE, 0, 1, 0, 0, NULL},
222    {"antialias", pdc_booleanlist, PDC_OPT_NONE, 0, 1, 0, 0, NULL},
223    PDC_OPT_TERMINATE
224};
225
226PDFLIB_API int PDFLIB_CALL
227PDF_shading(
228    PDF *p,
229    const char *type,
230    float x_0, float y_0,
231    float x_1, float y_1,
232    float c_1, float c_2, float c_3, float c_4,
233    const char *optlist)
234{
235    static const char fn[] = "PDF_shading";
236    pdf_shadingtype_e shtype = shnone;
237    pdf_color *color0, color1;
238    pdf_colorspace *cs;
239    pdc_resopt *results;
240    float N = (float) 1.0;
241    float r_0, r_1;
242    pdc_bool extend0 = pdc_false, extend1 = pdc_false, antialias = pdc_false;
243    int retval = -1;
244
245    if (!pdf_enter_api(p, fn,
246        (pdf_state) (pdf_state_document | pdf_state_page | pdf_state_font),
247	"(p[%p], \"%s\", \"%s\")",
248	(void *) p, type, optlist))
249    {
250        PDF_RETURN_HANDLE(p, retval)
251    }
252
253    if (p->compatibility == PDC_1_3)
254	pdc_error(p->pdc, PDF_E_SHADING13, 0, 0, 0, 0);
255
256    if (!strcmp(type, "axial")) {
257	shtype = axial;
258
259    } else if (!strcmp(type, "radial")) {
260	shtype = radial;
261
262    } else
263	pdc_error(p->pdc, PDC_E_ILLARG_STRING, "type", type, 0, 0);
264
265    color0 = &p->cstate[p->sl].fill;
266
267    color1.cs = color0->cs;
268
269    cs = &p->colorspaces[color0->cs];
270
271    switch (cs->type) {
272	case DeviceGray:
273	color1.val.gray = c_1;
274	break;
275
276	case DeviceRGB:
277	color1.val.rgb.r = c_1;
278	color1.val.rgb.g = c_2;
279	color1.val.rgb.b = c_3;
280	break;
281
282	case DeviceCMYK:
283	color1.val.cmyk.c = c_1;
284	color1.val.cmyk.m = c_2;
285	color1.val.cmyk.y = c_3;
286	color1.val.cmyk.k = c_4;
287	break;
288
289
290
291	default:
292	pdc_error(p->pdc, PDF_E_INT_BADCS,
293	    pdc_errprintf(p->pdc, "%d", color0->cs), 0, 0, 0);
294    }
295
296    if (optlist && strlen(optlist)) {
297	results = pdc_parse_optionlist(p->pdc,
298            optlist, pdf_shading_options, NULL, pdc_true);
299
300	(void) pdc_get_optvalues(p->pdc, "N", results, &N, NULL);
301
302	(void) pdc_get_optvalues(p->pdc, "antialias", results, &antialias,NULL);
303
304	if (shtype == radial) {
305	    if (pdc_get_optvalues(p->pdc, "r0", results, &r_0, NULL) != 1)
306		pdc_error(p->pdc, PDC_E_OPT_NOTFOUND, "r0", 0, 0, 0);
307
308	    if (pdc_get_optvalues(p->pdc, "r1", results, &r_1, NULL) != 1)
309		pdc_error(p->pdc, PDC_E_OPT_NOTFOUND, "r1", 0, 0, 0);
310	}
311
312	if (shtype == axial) {
313	    if (pdc_get_optvalues(p->pdc, "r0", results, &r_0, NULL) == 1)
314		pdc_warning(p->pdc, PDC_E_OPT_IGNORED, "r0", 0, 0, 0);
315
316	    if (pdc_get_optvalues(p->pdc, "r1", results, &r_1, NULL) == 1)
317		pdc_warning(p->pdc, PDC_E_OPT_IGNORED, "r1", 0, 0, 0);
318	}
319
320	if (shtype == radial || shtype == axial) {
321	    pdc_get_optvalues(p->pdc, "extend0", results, &extend0, NULL);
322	    pdc_get_optvalues(p->pdc, "extend1", results, &extend1, NULL);
323	}
324
325	pdc_cleanup_optionlist(p->pdc, results);
326    }
327
328    if (p->shadings_number == p->shadings_capacity)
329	pdf_grow_shadings(p);
330
331    if (PDF_GET_STATE(p) == pdf_state_page)
332	pdf_end_contents_section(p);
333
334    							/* Shading object */
335    p->shadings[p->shadings_number].obj_id = pdc_begin_obj(p->out, PDC_NEW_ID);
336
337    pdc_begin_dict(p->out);				/* Shading dict*/
338
339    pdc_printf(p->out, "/ShadingType %d\n", (int) shtype);
340
341    pdc_printf(p->out, "/ColorSpace");
342    pdf_write_colorspace(p, color1.cs, pdc_false);
343    pdc_puts(p->out, "\n");
344
345    if (antialias)
346	pdc_printf(p->out, "/AntiAlias true\n");
347
348    switch (shtype) {
349	case axial:	/* Type 2 */
350	pdc_printf(p->out, "/Coords[%f %f %f %f]\n", x_0, y_0, x_1, y_1);
351	if (extend0 || extend1)
352	    pdc_printf(p->out, "/Extend[%s %s]\n",
353		extend0 ? "true" : "false", extend1 ? "true" : "false");
354	pdc_puts(p->out, "/Function");
355	pdf_write_function_dict(p, color0, &color1, N);
356	break;
357
358	case radial:	/* Type 3 */
359	pdc_printf(p->out, "/Coords[%f %f %f %f %f %f]\n",
360	    x_0, y_0, r_0, x_1, y_1, r_1);
361	if (extend0 || extend1)
362	    pdc_printf(p->out, "/Extend[%s %s]\n",
363		extend0 ? "true" : "false", extend1 ? "true" : "false");
364	pdc_puts(p->out, "/Function");
365	pdf_write_function_dict(p, color0, &color1, N);
366	break;
367
368	default:
369	break;
370    }
371
372    pdc_end_dict(p->out);				/* Shading dict */
373    pdc_end_obj(p->out);				/* Shading object */
374
375    if (PDF_GET_STATE(p) == pdf_state_page)
376	pdf_begin_contents_section(p);
377
378    retval = p->shadings_number;
379    p->shadings_number++;
380    PDF_RETURN_HANDLE(p, retval)
381}
382