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_color.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib color routines
16 *
17 */
18
19#define P_COLOR_C
20
21#include "p_intern.h"
22#include "p_font.h"
23
24
25
26
27/* Avoid wrong error messages due to rounding artifacts.
28 * This doesn't do any harm since we truncate to 5 decimal places anyway
29 * when producing PDF output.
30 */
31#define EPSILON	((float) (1.0 + PDF_SMALLREAL))
32
33static void pdf_check_color_values(PDF *p, pdf_colorspacetype type,
34    float c1, float c2, float c3, float c4);
35
36int
37pdf_color_components(PDF *p, int slot)
38{
39    pdf_colorspace *cs;
40    int components = 0;
41
42    cs = &p->colorspaces[slot];
43
44    switch (cs->type) {
45	case DeviceGray:
46	case Indexed:
47	components = 1;
48	break;
49
50	case DeviceRGB:
51	components = 3;
52	break;
53
54	case DeviceCMYK:
55	components = 4;
56	break;
57
58	case PatternCS:
59	if (cs->val.pattern.base == pdc_undef)
60	    components = 0;	/* PaintType 1: colored pattern */
61	else
62	    components = pdf_color_components(p, cs->val.pattern.base);
63
64	default:
65	    pdc_error(p->pdc, PDF_E_INT_BADCS,
66		pdc_errprintf(p->pdc, "%d", cs->type), 0, 0, 0);
67    }
68
69    return components;
70} /* pdf_color_components */
71
72void
73pdf_write_color_values(PDF *p, pdf_color *color, pdf_drawmode drawmode)
74{
75    pdf_colorspace *cs = &p->colorspaces[color->cs];
76
77    switch (cs->type) {
78	case DeviceGray:
79	    pdc_printf(p->out, "%f", color->val.gray);
80
81	    if (drawmode == pdf_fill)
82		pdc_puts(p->out, " g\n");
83	    else if (drawmode == pdf_stroke)
84		pdc_puts(p->out, " G\n");
85	    break;
86
87	case DeviceRGB:
88	    pdc_printf(p->out, "%f %f %f",
89		    color->val.rgb.r, color->val.rgb.g, color->val.rgb.b);
90
91	    if (drawmode == pdf_fill)
92		pdc_puts(p->out, " rg\n");
93	    else if (drawmode == pdf_stroke)
94		pdc_puts(p->out, " RG\n");
95	    break;
96
97	case DeviceCMYK:
98	    pdc_printf(p->out, "%f %f %f %f",
99		    color->val.cmyk.c, color->val.cmyk.m,
100		    color->val.cmyk.y, color->val.cmyk.k);
101
102	    if (drawmode == pdf_fill)
103		pdc_puts(p->out, " k\n");
104	    else if (drawmode == pdf_stroke)
105		pdc_puts(p->out, " K\n");
106	    break;
107
108
109	case PatternCS:
110	    if (drawmode == pdf_fill) {
111		if (p->pattern[color->val.pattern].painttype == 1) {
112		    pdc_puts(p->out, "/Pattern cs");
113		} else if (p->pattern[color->val.pattern].painttype == 2) {
114		    pdc_printf(p->out, "/CS%d cs ", color->cs);
115		    pdf_write_color_values(p,
116			&p->cstate[p->sl].fill, pdf_none);
117		}
118		pdc_printf(p->out, "/P%d scn\n", color->val.pattern);
119
120	    } else if (drawmode == pdf_stroke) {
121		if (p->pattern[color->val.pattern].painttype == 1) {
122		    pdc_puts(p->out, "/Pattern CS");
123		} else if (p->pattern[color->val.pattern].painttype == 2) {
124		    pdc_printf(p->out, "/CS%d CS ", color->cs);
125		    pdf_write_color_values(p,
126			&p->cstate[p->sl].stroke, pdf_none);
127		}
128		pdc_printf(p->out, "/P%d SCN\n", color->val.pattern);
129	    }
130
131	    p->pattern[color->val.pattern].used_on_current_page = pdc_true;
132	    break;
133
134
135	case Indexed: /* LATER */
136	default:
137	    pdc_error(p->pdc, PDF_E_INT_BADCS,
138		pdc_errprintf(p->pdc, "%d",
139		    p->colorspaces[color->cs].type), 0, 0, 0);
140    }
141} /* pdf_write_color_values */
142
143static pdc_bool
144pdf_color_equal(PDF *p, pdf_color *c1, pdf_color *c2)
145{
146    pdf_colorspace *cs1;
147
148    cs1 = &p->colorspaces[c1->cs];
149
150    if (c1->cs != c2->cs)
151	return pdc_false;
152
153    switch (cs1->type) {
154	case DeviceGray:
155	    return (c1->val.gray == c2->val.gray);
156	    break;
157
158	case DeviceRGB:
159	    return (c1->val.rgb.r == c2->val.rgb.r &&
160		    c1->val.rgb.g == c2->val.rgb.g &&
161		    c1->val.rgb.b == c2->val.rgb.b);
162	    break;
163
164	case DeviceCMYK:
165	    return (c1->val.cmyk.c == c2->val.cmyk.c &&
166		    c1->val.cmyk.m == c2->val.cmyk.m &&
167		    c1->val.cmyk.y == c2->val.cmyk.y &&
168		    c1->val.cmyk.k == c2->val.cmyk.k);
169	    break;
170
171	case Indexed:
172	    return (c1->val.idx == c2->val.idx);
173	    break;
174
175	case PatternCS:
176	    return (c1->val.pattern == c2->val.pattern);
177	    break;
178
179
180	default:
181	    break;
182    }
183
184    return pdc_true;
185} /* pdf_color_equal */
186
187static pdc_bool
188pdf_colorspace_equal(PDF *p, pdf_colorspace *cs1, pdf_colorspace *cs2)
189{
190    if (cs1->type != cs2->type)
191	return pdc_false;
192
193    switch (cs1->type) {
194	case DeviceGray:
195	case DeviceRGB:
196	case DeviceCMYK:
197	return pdc_true;
198	break;
199
200
201	case Indexed:
202	return ((cs1->val.indexed.base == cs2->val.indexed.base) &&
203		(cs1->val.indexed.palette_size==cs2->val.indexed.palette_size)&&
204		(cs1->val.indexed.colormap_id != PDC_BAD_ID &&
205		 cs1->val.indexed.colormap_id == cs2->val.indexed.colormap_id));
206	break;
207
208	case PatternCS:
209	return (cs1->val.pattern.base == cs2->val.pattern.base);
210	break;
211
212
213	default:
214	    pdc_error(p->pdc, PDF_E_INT_BADCS,
215		pdc_errprintf(p->pdc, "%d", cs1->type), 0, 0, 0);
216    }
217
218    return pdc_false;
219} /* pdf_colorspace_equal */
220
221static void
222pdf_grow_colorspaces(PDF *p)
223{
224    int i;
225
226    p->colorspaces = (pdf_colorspace *) pdc_realloc(p->pdc, p->colorspaces,
227	sizeof(pdf_colorspace) * 2 * p->colorspaces_capacity,
228	"pdf_grow_colorspaces");
229
230    for (i = p->colorspaces_capacity; i < 2 * p->colorspaces_capacity; i++) {
231	p->colorspaces[i].used_on_current_page = pdc_false;
232    }
233
234    p->colorspaces_capacity *= 2;
235}
236
237int
238pdf_add_colorspace(PDF *p, pdf_colorspace *cs, pdc_bool inuse)
239{
240    pdf_colorspace *cs_new;
241    static const char fn[] = "pdf_add_colorspace";
242    int slot;
243
244    for (slot = 0; slot < p->colorspaces_number; slot++)
245    {
246	if (pdf_colorspace_equal(p, &p->colorspaces[slot], cs))
247	{
248	    if (inuse)
249		p->colorspaces[slot].used_on_current_page = pdc_true;
250	    return slot;
251	}
252    }
253
254    slot = p->colorspaces_number;
255
256    if (p->colorspaces_number >= p->colorspaces_capacity)
257	pdf_grow_colorspaces(p);
258
259    cs_new = &p->colorspaces[slot];
260
261    cs_new->type = cs->type;
262
263    /* don't allocate id for simple color spaces, since we don't write these */
264    if (PDF_SIMPLE_COLORSPACE(cs)) {
265	cs_new->obj_id = PDC_BAD_ID;
266	cs_new->used_on_current_page = pdc_false;
267
268    } else {
269	cs_new->obj_id = pdc_alloc_id(p->out);
270	cs_new->used_on_current_page = inuse;
271    }
272
273    switch (cs_new->type) {
274	case DeviceGray:
275	case DeviceRGB:
276	case DeviceCMYK:
277	break;
278
279
280	case Indexed:
281        cs_new->val.indexed.base = cs->val.indexed.base;
282        cs_new->val.indexed.palette_size = cs->val.indexed.palette_size;
283        cs_new->val.indexed.colormap_id = pdc_alloc_id(p->out);
284        cs_new->val.indexed.colormap =
285	    (pdf_colormap *) pdc_malloc(p->pdc, sizeof(pdf_colormap), fn);
286        memcpy(cs_new->val.indexed.colormap, cs->val.indexed.colormap,
287	    sizeof(pdf_colormap));
288        cs_new->val.indexed.colormap_done = pdc_false;
289	break;
290
291	case PatternCS:
292        cs_new->val.pattern.base = cs->val.pattern.base;
293	break;
294
295
296	default:
297	    pdc_error(p->pdc, PDF_E_INT_BADCS,
298                pdc_errprintf(p->pdc, "%d", cs_new->type), 0, 0, 0);
299    }
300
301    p->colorspaces_number++;
302
303    return slot;
304} /* pdf_add_colorspace */
305
306void
307pdf_init_cstate(PDF *p)
308{
309    pdf_cstate *cstate;
310
311    cstate = &p->cstate[p->sl];
312
313    cstate->fill.cs		= DeviceGray;
314    cstate->fill.val.gray	= (float) 0.0;
315
316    cstate->stroke.cs		= DeviceGray;
317    cstate->stroke.val.gray	= (float) 0.0;
318}
319
320void
321pdf_set_color_values(PDF *p, pdf_color *c, pdf_drawmode drawmode)
322{
323    pdf_color *current;
324    pdf_colorspace *cs;
325
326    cs = &p->colorspaces[c->cs];
327
328    if (drawmode == pdf_stroke || drawmode == pdf_fillstroke) {
329	current = &p->cstate[p->sl].stroke;
330
331	if (!pdf_color_equal(p, current, c) || PDF_FORCE_OUTPUT())
332	{
333	    if (PDF_GET_STATE(p) != pdf_state_document)
334		pdf_write_color_values(p, c, pdf_stroke);
335
336	    *current = *c;
337	}
338    }
339    if (drawmode == pdf_fill || drawmode == pdf_fillstroke) {
340	current = &p->cstate[p->sl].fill;
341
342	if (!pdf_color_equal(p, current, c) || PDF_FORCE_OUTPUT())
343	{
344	    if (PDF_GET_STATE(p) != pdf_state_document)
345		pdf_write_color_values(p, c, pdf_fill);
346
347	    *current = *c;
348	}
349    }
350
351    /* simple colorspaces don't get written */
352    if (!PDF_SIMPLE_COLORSPACE(cs))
353	cs->used_on_current_page = pdc_true;
354
355} /* pdf_set_color_values */
356
357PDFLIB_API void PDFLIB_CALL
358PDF_setgray_fill(PDF *p, float g)
359{
360    static const char fn[] = "PDF_setgray_fill";
361    pdf_color c;
362
363    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
364	(void *) p, g))
365	return;
366
367    pdf_check_color_values(p, DeviceGray, g, (float) 0, (float) 0, (float) 0);
368    c.cs = DeviceGray;
369    c.val.gray = g;
370    pdf_set_color_values(p, &c, pdf_fill);
371}
372
373PDFLIB_API void PDFLIB_CALL
374PDF_setgray_stroke(PDF *p, float g)
375{
376    static const char fn[] = "PDF_setgray_stroke";
377    pdf_color c;
378
379    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
380	(void *) p, g))
381	return;
382
383    pdf_check_color_values(p, DeviceGray, g, (float) 0, (float) 0, (float) 0);
384    c.cs = DeviceGray;
385    c.val.gray = g;
386    pdf_set_color_values(p, &c, pdf_stroke);
387}
388
389PDFLIB_API void PDFLIB_CALL
390PDF_setgray(PDF *p, float g)
391{
392    static const char fn[] = "PDF_setgray";
393    pdf_color c;
394
395    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
396	(void *) p, g))
397	return;
398
399    pdf_check_color_values(p, DeviceGray, g, (float) 0, (float) 0, (float) 0);
400    c.cs = DeviceGray;
401    c.val.gray = g;
402    pdf_set_color_values(p, &c, pdf_fillstroke);
403}
404
405/* RGB functions */
406
407PDFLIB_API void PDFLIB_CALL
408PDF_setrgbcolor_fill(PDF *p, float r, float g, float b)
409{
410    static const char fn[] = "PDF_setrgbcolor_fill";
411    pdf_color c;
412
413    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g, %g)\n",
414	(void *) p, r, g, b))
415	return;
416
417    pdf_check_color_values(p, DeviceRGB, r, g, b, 0);
418    c.cs = DeviceRGB;
419    c.val.rgb.r = r;
420    c.val.rgb.g = g;
421    c.val.rgb.b = b;
422    pdf_set_color_values(p, &c, pdf_fill);
423}
424
425PDFLIB_API void PDFLIB_CALL
426PDF_setrgbcolor_stroke(PDF *p, float r, float g, float b)
427{
428    static const char fn[] = "PDF_setrgbcolor_stroke";
429    pdf_color c;
430
431    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g, %g)\n",
432	(void *) p, r, g, b))
433	return;
434
435    pdf_check_color_values(p, DeviceRGB, r, g, b, 0);
436    c.cs = DeviceRGB;
437    c.val.rgb.r = r;
438    c.val.rgb.g = g;
439    c.val.rgb.b = b;
440    pdf_set_color_values(p, &c, pdf_stroke);
441}
442
443PDFLIB_API void PDFLIB_CALL
444PDF_setrgbcolor(PDF *p, float r, float g, float b)
445{
446    static const char fn[] = "PDF_setrgbcolor";
447    pdf_color c;
448
449    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g, %g)\n",
450	(void *) p, r, g, b))
451	return;
452
453    pdf_check_color_values(p, DeviceRGB, r, g, b, 0);
454    c.cs = DeviceRGB;
455    c.val.rgb.r = r;
456    c.val.rgb.g = g;
457    c.val.rgb.b = b;
458    pdf_set_color_values(p, &c, pdf_fillstroke);
459}
460
461void
462pdf_init_colorspaces(PDF *p)
463{
464    int i, slot;
465    pdf_colorspace cs;
466
467
468    p->colorspaces_number = 0;
469    p->colorspaces_capacity = COLORSPACES_CHUNKSIZE;
470
471    p->colorspaces = (pdf_colorspace *)
472	pdc_malloc(p->pdc, sizeof(pdf_colorspace) * p->colorspaces_capacity,
473	    "pdf_init_colorspaces");
474
475    for (i = 0; i < p->colorspaces_capacity; i++) {
476	p->colorspaces[i].used_on_current_page = pdc_false;
477    }
478
479    /*
480     * Initialize a few slots with simple color spaces for easier use.
481     * These can be used without further ado: the slot number is identical
482     * to the enum value due to the ordering below.
483     */
484
485    cs.type = DeviceGray;
486    slot = pdf_add_colorspace(p, &cs, pdc_false);
487    cs.type = DeviceRGB;
488    slot = pdf_add_colorspace(p, &cs, pdc_false);
489    cs.type = DeviceCMYK;
490    slot = pdf_add_colorspace(p, &cs, pdc_false);
491
492} /* pdf_init_colorspaces */
493
494void
495pdf_write_page_colorspaces(PDF *p)
496{
497    int i, total = 0;
498
499    for (i = 0; i < p->colorspaces_number; i++)
500	if (p->colorspaces[i].used_on_current_page)
501	    total++;
502
503    if (total > 0
504       ) {
505	pdc_puts(p->out, "/ColorSpace");
506
507	pdc_begin_dict(p->out);			/* color space names */
508
509	for (i = 0; i < p->colorspaces_number; i++) {
510	    pdf_colorspace *cs = &p->colorspaces[i];
511
512	    if (cs->used_on_current_page) {
513		cs->used_on_current_page = pdc_false; /* reset */
514
515		/* don't write simple color spaces as resource */
516		if (!PDF_SIMPLE_COLORSPACE(cs))
517		    pdc_printf(p->out, "/CS%d %ld 0 R\n", i, cs->obj_id);
518	    }
519	}
520
521
522	pdc_end_dict(p->out);			/* color space names */
523    }
524} /* pdf_write_page_colorspaces */
525
526void
527pdf_write_function_dict(PDF *p, pdf_color *c0, pdf_color *c1, float N)
528{
529    pdf_colorspace *cs;
530
531    cs = &p->colorspaces[c1->cs];
532
533    pdc_begin_dict(p->out);			/* function dict */
534
535    pdc_puts(p->out, "/FunctionType 2\n");
536    pdc_puts(p->out, "/Domain[0 1]\n");
537    pdc_printf(p->out, "/N %f\n", N);
538
539    switch (cs->type) {
540
541	case DeviceGray:
542	pdc_puts(p->out, "/Range[0 1]\n");
543	if (c0->val.gray != 0) pdc_printf(p->out, "/C0[%f]\n", c0->val.gray);
544	if (c1->val.gray != 1) pdc_printf(p->out, "/C1[%f]", c1->val.gray);
545	break;
546
547	case DeviceRGB:
548	pdc_puts(p->out, "/Range[0 1 0 1 0 1]\n");
549	pdc_printf(p->out, "/C0[%f %f %f]\n",
550		c0->val.rgb.r, c0->val.rgb.g, c0->val.rgb.b);
551	pdc_printf(p->out, "/C1[%f %f %f]",
552		c1->val.rgb.r, c1->val.rgb.g, c1->val.rgb.b);
553	break;
554
555	case DeviceCMYK:
556	pdc_puts(p->out, "/Range[0 1 0 1 0 1 0 1]\n");
557	pdc_printf(p->out, "/C0[%f %f %f %f]\n",
558		c0->val.cmyk.c, c0->val.cmyk.m, c0->val.cmyk.y, c0->val.cmyk.k);
559	pdc_printf(p->out, "/C1[%f %f %f %f]",
560		c1->val.cmyk.c, c1->val.cmyk.m, c1->val.cmyk.y, c1->val.cmyk.k);
561	break;
562
563
564	default:
565	pdc_error(p->pdc, PDF_E_INT_BADCS,
566	    pdc_errprintf(p->pdc, "%d", cs->type), 0, 0, 0);
567    }
568
569    pdc_end_dict(p->out);		/* function dict */
570} /* pdf_write_function_dict */
571
572void
573pdf_write_colormap(PDF *p, int slot)
574{
575    PDF_data_source src;
576    pdf_colorspace *cs, *base;
577    pdc_id length_id;
578
579    cs   = &p->colorspaces[slot];
580
581    if (cs->type != Indexed || cs->val.indexed.colormap_done == pdc_true)
582	return;
583
584    base = &p->colorspaces[cs->val.indexed.base];
585
586    pdc_begin_obj(p->out, cs->val.indexed.colormap_id);	/* colormap object */
587    pdc_begin_dict(p->out);
588
589    if (p->debug['a'])
590	pdc_puts(p->out, "/Filter/ASCIIHexDecode\n");
591    else if (pdc_get_compresslevel(p->out))
592	pdc_puts(p->out, "/Filter/FlateDecode\n");
593
594    /* Length of colormap object */
595    length_id = pdc_alloc_id(p->out);
596    pdc_printf(p->out,"/Length %ld 0 R\n", length_id);
597    pdc_end_dict(p->out);
598
599    src.init		= NULL;
600    src.fill		= pdf_data_source_buf_fill;
601    src.terminate	= NULL;
602
603    src.buffer_start	= (unsigned char *) cs->val.indexed.colormap;
604
605    src.buffer_length = (size_t) (cs->val.indexed.palette_size *
606				pdf_color_components(p, cs->val.indexed.base));
607
608    src.bytes_available = 0;
609    src.next_byte	= NULL;
610
611    /* Write colormap data */
612    if (p->debug['a'])
613	pdf_ASCIIHexEncode(p, &src);
614    else {
615	pdf_copy_stream(p, &src, pdc_true);	/* colormap data */
616    }
617
618    pdc_end_obj(p->out);				/* colormap object */
619
620    pdc_put_pdfstreamlength(p->out, length_id);
621
622    /* free the colormap now that it's written */
623    pdc_free(p->pdc, cs->val.indexed.colormap);
624    cs->val.indexed.colormap = NULL;
625    cs->val.indexed.colormap_done = pdc_true;
626} /* pdf_write_colormap */
627
628void
629pdf_write_colorspace(PDF *p, int slot, pdc_bool direct)
630{
631    pdf_colorspace *cs;
632    int base;
633
634    if (slot < 0 || slot >= p->colorspaces_number)
635	pdc_error(p->pdc, PDF_E_INT_BADCS,
636	    pdc_errprintf(p->pdc, "%d", slot), 0, 0, 0);
637
638    cs = &p->colorspaces[slot];
639
640    /* we always write simple colorspaces directly */
641    if (PDF_SIMPLE_COLORSPACE(cs))
642	direct = pdc_true;
643
644    if (!direct) {
645	pdc_printf(p->out, " %ld 0 R", cs->obj_id);
646	return;
647    }
648
649    switch (cs->type) {
650	case DeviceGray:
651	pdc_printf(p->out, "/DeviceGray");
652	break;
653
654	case DeviceRGB:
655	pdc_printf(p->out, "/DeviceRGB");
656	break;
657
658	case DeviceCMYK:
659	pdc_printf(p->out, "/DeviceCMYK");
660	break;
661
662
663
664	case Indexed:
665	base = cs->val.indexed.base;
666
667	pdc_puts(p->out, "[/Indexed");
668
669	pdf_write_colorspace(p, base, pdc_false);
670	pdc_puts(p->out, " ");
671
672	pdc_printf(p->out, "%d %ld 0 R", cs->val.indexed.palette_size - 1,
673	                cs->val.indexed.colormap_id);
674	pdc_puts(p->out, "]");
675	break;
676
677	case PatternCS:
678	pdc_printf(p->out, "[/Pattern");
679	pdf_write_colorspace(p, cs->val.pattern.base, pdc_false);
680	pdc_puts(p->out, "]");
681	break;
682
683	default:
684	pdc_error(p->pdc, PDF_E_INT_BADCS,
685	    pdc_errprintf(p->pdc, "%d", cs->type), 0, 0, 0);
686    }
687} /* pdf_write_colorspace */
688
689void
690pdf_write_doc_colorspaces(PDF *p)
691{
692    int i;
693
694    for (i = 0; i < p->colorspaces_number; i++) {
695	pdf_colorspace *cs = &p->colorspaces[i];
696
697	/* don't write simple color spaces as resource */
698	if (PDF_SIMPLE_COLORSPACE(cs))
699	    continue;
700
701	pdc_begin_obj(p->out, cs->obj_id);
702	pdf_write_colorspace(p, i, pdc_true);
703	pdc_puts(p->out, "\n");
704	pdc_end_obj(p->out);			/* color space resource */
705
706	pdf_write_colormap(p, i);		/* write pending colormaps */
707    }
708}
709
710void
711pdf_cleanup_colorspaces(PDF *p)
712{
713    int i;
714
715    if (!p->colorspaces)
716	return;
717
718    for (i = 0; i < p->colorspaces_number; i++) {
719	pdf_colorspace *cs = &p->colorspaces[i];;
720
721	switch (cs->type) {
722	    case DeviceGray:
723	    case DeviceRGB:
724	    case DeviceCMYK:
725	    case PatternCS:
726	    break;
727
728	    case Indexed:
729	    if (cs->val.indexed.colormap)
730		pdc_free(p->pdc, cs->val.indexed.colormap);
731	    break;
732
733
734	    default:
735		pdc_error(p->pdc, PDF_E_INT_BADCS,
736		    pdc_errprintf(p->pdc, "%d", cs->type), 0, 0, 0);
737	}
738    }
739
740    pdc_free(p->pdc, p->colorspaces);
741    p->colorspaces = NULL;
742}
743
744
745
746
747
748PDFLIB_API int PDFLIB_CALL
749PDF_makespotcolor(PDF *p, const char *spotname, int reserved)
750{
751    static const char fn[] = "PDF_makespotcolor";
752    int slot = -1;
753
754    if (!pdf_enter_api(p, fn,
755        (pdf_state) (pdf_state_content | pdf_state_document),
756        "(p[%p], \"%s\", %d)", (void *) p, spotname, reserved))
757    {
758        PDF_RETURN_HANDLE(p, slot)
759    }
760
761    pdc_error(p->pdc, PDF_E_UNSUPP_SPOTCOLOR, 0, 0, 0, 0);
762
763    PDF_RETURN_HANDLE(p, slot)
764}
765
766
767
768static void
769pdf_check_color_values(
770    PDF *p,
771    pdf_colorspacetype type,
772    float c1, float c2, float c3, float c4)
773{
774    switch (type) {
775	case DeviceGray:
776	if (c1 < 0.0 || c1 > EPSILON )
777	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
778		"c1", pdc_errprintf(p->pdc, "%f", c1), 0, 0);
779	break;
780
781	case DeviceRGB:
782	if (c1 < 0.0 || c1 > EPSILON )
783	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
784		"c1", pdc_errprintf(p->pdc, "%f", c1), 0, 0);
785	if (c2 < 0.0 || c2 > EPSILON )
786	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
787		"c2", pdc_errprintf(p->pdc, "%f", c2), 0, 0);
788	if (c3 < 0.0 || c3 > EPSILON )
789	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
790		"c3", pdc_errprintf(p->pdc, "%f", c3), 0, 0);
791
792	break;
793
794	case DeviceCMYK:
795	if (c1 < 0.0 || c1 > EPSILON )
796	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
797		"c1", pdc_errprintf(p->pdc, "%f", c1), 0, 0);
798	if (c2 < 0.0 || c2 > EPSILON )
799	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
800		"c2", pdc_errprintf(p->pdc, "%f", c2), 0, 0);
801	if (c3 < 0.0 || c3 > EPSILON )
802	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
803		"c3", pdc_errprintf(p->pdc, "%f", c3), 0, 0);
804
805	if (c4 < 0.0 || c4 > EPSILON )
806	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
807		"c4", pdc_errprintf(p->pdc, "%f", c4), 0, 0);
808	break;
809
810
811
812	case PatternCS:
813        pdf_check_handle(p, (int) c1, pdc_patternhandle);
814	break;
815
816	case Separation:
817        pdf_check_handle(p, (int) c1, pdc_colorhandle);
818	if (c2 < 0.0 || c2 > EPSILON )
819	    pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
820		"c2", pdc_errprintf(p->pdc, "%f", c2), 0, 0);
821	break;
822
823	case Indexed:
824	default:
825	    break;
826    }
827} /* pdf_check_color_values */
828
829static const pdc_keyconn pdf_fstype_keylist[] =
830{
831    {"stroke",     pdf_stroke},
832    {"fill",       pdf_fill},
833    {"fillstroke", pdf_stroke | pdf_fill},
834    {"both",       pdf_stroke | pdf_fill},
835    {NULL, 0}
836};
837
838void
839pdf__setcolor(
840    PDF *p,
841    const char *fstype,
842    const char *colorspace,
843    float c1, float c2, float c3, float c4)
844{
845    pdf_color c;
846    pdf_drawmode drawmode = pdf_none;
847    pdf_colorspace cs;
848    int k;
849
850    k = pdc_get_keycode_ci(fstype, pdf_fstype_keylist);
851    if (k == PDC_KEY_NOTFOUND)
852        pdc_error(p->pdc, PDC_E_ILLARG_STRING, "fstype", fstype, 0, 0);
853    drawmode = (pdf_drawmode) k;
854
855    k = pdc_get_keycode_ci(colorspace, pdf_colorspace_keylist);
856    if (k == PDC_KEY_NOTFOUND)
857        pdc_error(p->pdc, PDC_E_ILLARG_STRING, "colorspace", colorspace, 0, 0);
858    cs.type = (pdf_colorspacetype) k;
859
860    switch (cs.type) {
861
862        case DeviceGray:
863	c.cs = cs.type;
864	c.val.gray = c1;
865	pdf_check_color_values(p, cs.type, c1, c2, c3, c4);
866        break;
867
868        case DeviceRGB:
869	c.cs = cs.type;
870	c.val.rgb.r = c1;
871	c.val.rgb.g = c2;
872	c.val.rgb.b = c3;
873	pdf_check_color_values(p, cs.type, c1, c2, c3, c4);
874        break;
875
876        case DeviceCMYK:
877	c.cs = cs.type;
878	c.val.cmyk.c = c1;
879	c.val.cmyk.m = c2;
880	c.val.cmyk.y = c3;
881	c.val.cmyk.k = c4;
882	pdf_check_color_values(p, cs.type, c1, c2, c3, c4);
883        break;
884
885
886        case PatternCS:
887
888        c.val.pattern = (int) c1;
889        PDF_INPUT_HANDLE(p, c.val.pattern)
890        pdf_check_color_values(p, cs.type, c1, c2, c3, c4);
891
892        if (p->pattern[c.val.pattern].painttype == 1) {
893	    cs.val.pattern.base = pdc_undef;
894	    c.cs = pdf_add_colorspace(p, &cs, pdc_false);
895	} else {
896	    cs.val.pattern.base = p->cstate[p->sl].fill.cs;
897	    c.cs = pdf_add_colorspace(p, &cs, pdc_true);
898	}
899        break;
900
901
902        default:
903        pdc_error(p->pdc, PDC_E_OPT_UNSUPP, colorspace, 0, 0, 0);
904    }
905
906    pdf_set_color_values(p, &c, drawmode);
907}
908
909PDFLIB_API void PDFLIB_CALL
910PDF_setcolor(
911    PDF *p,
912    const char *fstype,
913    const char *colorspace,
914    float c1, float c2, float c3, float c4)
915{
916    static const char fn[] = "PDF_setcolor";
917    int legal_states;
918
919    if (PDF_GET_STATE(p) == pdf_state_glyph && !pdf_get_t3colorized(p))
920        legal_states = pdf_state_page | pdf_state_pattern | pdf_state_template;
921    else
922        legal_states = pdf_state_content | pdf_state_document;
923
924    if (!pdf_enter_api(p, fn, (pdf_state) legal_states,
925        "(p[%p], \"%s\", \"%s\", %g, %g, %g, %g)\n",
926        (void *) p, fstype, colorspace, c1, c2, c3, c4))
927        return;
928
929    if (!fstype || !*fstype)
930        pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "fstype", 0, 0, 0);
931
932    if (!colorspace || !*colorspace)
933        pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "colorspace", 0, 0, 0);
934
935    pdf__setcolor(p, fstype, colorspace, c1, c2, c3, c4);
936}
937