OSDN Git Service

updated with TeX Live 2014.
[putex/putex.git] / src / dvipdfmx-pu / src / pdfcolor.c
1 /*  
2     
3     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
4
5     Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata,
6     the dvipdfmx project team.
7     
8     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
9
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14     
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19     
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 */
24
25 /* No page independence here...
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "system.h"
33 #include "mem.h"
34 #include "error.h"
35
36 #include "dpxfile.h"
37
38 #include "pdfdoc.h"
39 #include "pdfdraw.h"
40
41 #include "pdfcolor.h"
42
43
44 static int verbose = 0;
45 void
46 pdf_color_set_verbose (void)
47 {
48   verbose++;
49 }
50
51 /* This function returns PDF_COLORSPACE_TYPE_GRAY,
52  * PDF_COLORSPACE_TYPE_RGB or PDF_COLORSPACE_TYPE_CMYK.
53  */
54 int
55 pdf_color_type (const pdf_color *color)
56 {
57   ASSERT(color);
58
59   return -color->num_components;
60 }
61
62 int
63 pdf_color_rgbcolor (pdf_color *color, double r, double g, double b)
64 {
65   ASSERT(color);
66
67   if (r < 0.0 || r > 1.0) {
68     WARN("Invalid color value specified: red=%g",   r);
69     return -1;
70   }
71   if (g < 0.0 || g > 1.0) {
72     WARN("Invalid color value specified: green=%g", g);
73     return -1;
74   }
75   if (b < 0.0 || b > 1.0) {
76     WARN("Invalid color value specified: blue=%g", b);
77     return -1;
78   }
79   color->values[0] = r;
80   color->values[1] = g;
81   color->values[2] = b;
82
83   color->num_components = 3;
84
85   return 0;
86 }
87
88 int
89 pdf_color_cmykcolor (pdf_color *color,
90                      double c, double m, double y, double k)
91 {
92   ASSERT(color);
93
94   if (c < 0.0 || c > 1.0) {
95     WARN("Invalid color value specified: cyan=%g", c);
96     return -1;
97   }
98   if (m < 0.0 || m > 1.0) {
99     WARN("Invalid color value specified: magenta=%g", m);
100     return -1;
101   }
102   if (y < 0.0 || y > 1.0) {
103     WARN("Invalid color value specified: yellow=%g", y);
104     return -1;
105   }
106   if (k < 0.0 || k > 1.0) {
107     WARN("Invalid color value specified: black=%g", k);
108     return -1;
109   }
110
111   color->values[0] = c;
112   color->values[1] = m;
113   color->values[2] = y;
114   color->values[3] = k;
115
116   color->num_components = 4;
117
118   return 0;
119 }
120
121 int
122 pdf_color_graycolor (pdf_color *color, double g)
123 {
124   ASSERT(color);
125
126   if (g < 0.0 || g > 1.0) {
127     WARN("Invalid color value specified: gray=%g", g);
128     return -1;
129   }
130
131   color->values[0] = g;
132
133   color->num_components = 1;
134
135   return 0;
136 }
137
138
139 void
140 pdf_color_copycolor (pdf_color *color1, const pdf_color *color2)
141 {
142   ASSERT(color1 && color2);
143
144   memcpy(color1, color2, sizeof(pdf_color));
145 }
146
147 /* Brighten up a color. f == 0 means no change, f == 1 means white. */
148 void
149 pdf_color_brighten_color (pdf_color *dst, const pdf_color *src, double f)
150 {
151   ASSERT(dst && src);
152
153   if (f == 1.0) {
154     pdf_color_white(dst);
155   } else {
156     double f0, f1;
157     int n;
158
159     n = dst->num_components = src->num_components;
160     f1 = n == 4 ? 0.0 : f;  /* n == 4 is CMYK, others are RGB and Gray */
161     f0 = 1.0-f;
162
163     while (n--)
164       dst->values[n] = f0 * src->values[n] + f1;
165   }
166 }
167
168 int
169 pdf_color_is_white (const pdf_color *color)
170 {
171   int n;
172   double f;
173
174   ASSERT(color);
175
176   n = color->num_components;
177   f = n == 4 ? 0.0 : 1.0;  /* n == 4 is CMYK, others are RGB and Gray */
178
179   while (n--)
180     if (color->values[n] != f)
181       return 0;
182
183   return 1;
184 }
185
186 int
187 pdf_color_to_string (const pdf_color *color, char *buffer)
188 {
189   int i, len = 0;
190
191   for (i = 0; i < color->num_components; i++) {
192     len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001));
193   }
194   return len;
195 }
196
197 pdf_color current_fill   = {
198   1,
199   {0.0, 0.0, 0.0, 0.0}
200 };
201
202 pdf_color current_stroke = {
203   1,
204   {0.0, 0.0, 0.0, 0.0}
205 };
206
207 /*
208  * This routine is not a real color matching.
209  */
210 int
211 pdf_color_compare (const pdf_color *color1, const pdf_color *color2)
212 {
213   int n = color1->num_components;
214
215   if (n != color2->num_components)
216     return -1;
217
218   while (n--)
219     if (color1->values[n] != color2->values[n])
220       return -1;
221
222   return 0;
223 }
224
225 int
226 pdf_color_is_valid (const pdf_color *color)
227 {
228   int  n;
229
230   n = color->num_components;
231   if (n != 1 && n != 3 && n != 4)
232     return 0;
233
234   while (n--)
235     if (color->values[n] < 0.0 || color->values[n] > 1.0)
236       return 0;
237
238   return 1;
239 }
240
241 /* Dvipdfm special */
242 #define DEV_COLOR_STACK_MAX 128
243
244 static struct {
245   int       current;
246   pdf_color stroke[DEV_COLOR_STACK_MAX];
247   pdf_color fill[DEV_COLOR_STACK_MAX];
248 } color_stack;
249
250 void
251 pdf_color_clear_stack (void)
252 {
253   if (color_stack.current > 0) {
254     WARN("You've mistakenly made a global color change within nested colors.");
255   }
256   color_stack.current = 0;
257   pdf_color_black(color_stack.stroke);
258   pdf_color_black(color_stack.fill);
259   return;
260 }
261
262 void
263 pdf_color_set (pdf_color *sc, pdf_color *fc)
264 {
265   pdf_color_copycolor(&color_stack.stroke[color_stack.current], sc);
266   pdf_color_copycolor(&color_stack.fill[color_stack.current], fc);
267   pdf_dev_reset_color(0);
268 }
269
270 void
271 pdf_color_push (pdf_color *sc, pdf_color *fc)
272 {
273   if (color_stack.current >= DEV_COLOR_STACK_MAX-1) {
274     WARN("Color stack overflow. Just ignore.");
275   } else {
276     color_stack.current++;
277     pdf_color_set(sc, fc);
278   }
279   return;
280 }
281
282 void
283 pdf_color_pop (void)
284 {
285   if (color_stack.current <= 0) {
286     WARN("Color stack underflow. Just ignore.");
287   } else {
288     color_stack.current--;
289     pdf_dev_reset_color(0);
290   }
291   return;
292 }
293
294 void
295 pdf_color_get_current (pdf_color **sc, pdf_color **fc)
296 {
297   *sc = &color_stack.stroke[color_stack.current];
298   *fc = &color_stack.fill[color_stack.current];
299   return;
300 }
301
302 /***************************** COLOR SPACE *****************************/
303
304 static int pdf_colorspace_defineresource (const char *ident,
305                                           int   subtype,
306                                           void *cdata, pdf_obj *resource);
307
308 static int pdf_colorspace_findresource   (const char *ident,
309                                           int   subtype, const void *cdata);
310
311 #if 0
312 struct calgray_cdata
313 {
314   double white_point[3]; /* required, second component must
315                           * be equal to 1.0
316                           */
317   double black_point[3]; /* optional, default: [0 0 0] */
318   double gamma;          /* optional, default: 1.0     */
319 };
320
321 struct calrgb_cdata
322 {
323   double white_point[3]; /* required, second component must
324                           * be equal to 1.0
325                           */
326   double black_point[3]; /* optional, default: [0 0 0] */
327   double gamma[3];       /* optional, default: [1 1 1] */
328   double matrix[9];      /* optional, default: identity
329                           * [1 0 0 0 1 0 0 0 1]
330                           */
331 };
332
333 static void
334 release_calrgb (void *cdata)
335 {
336   struct calrgb_cdata *calrgb;
337
338   if (cdata) {
339     calrgb = (struct calrgb_cdata *) cdata;
340     RELEASE(calrgb);
341   }
342 }
343
344 static int
345 compare_calrgb (const char *ident1, const void *cdata1,
346                 const char *ident2, const void *cdata2)
347 {
348   struct calrgb_cdata *calrgb1;
349   struct calrgb_cdata *calrgb2;
350
351   if (ident1 && ident2 &&
352       !strcmp(ident1, ident2)) {
353     return 0;
354   }
355 }
356
357 static void
358 init_calrgb (struct calrgb_cdata *calrgb)
359 {
360   ASSERT(calrgb);
361
362   calrgb->white_point[0] = 1.0;
363   calrgb->white_point[1] = 1.0;
364   calrgb->white_point[2] = 1.0;
365
366   calrgb->black_point[0] = 0.0;
367   calrgb->black_point[1] = 0.0;
368   calrgb->black_point[2] = 0.0;
369
370   calrgb->gamma[0]  = 1.0;
371   calrgb->gamma[1]  = 1.0;
372   calrgb->gamma[2]  = 1.0;
373
374   calrgb->matrix[0] = 1.0;
375   calrgb->matrix[1] = 0.0;
376   calrgb->matrix[2] = 0.0;
377
378   calrgb->matrix[3] = 0.0;
379   calrgb->matrix[4] = 1.0;
380   calrgb->matrix[5] = 0.0;
381
382   calrgb->matrix[6] = 0.0;
383   calrgb->matrix[7] = 0.0;
384   calrgb->matrix[8] = 1.0;
385 }
386
387 static int
388 valid_calrgb (struct calrgb_cdata *calrgb)
389 {
390   if (calrgb->white_point[1] != 1.0 ||
391       calrgb->white_point[0] <= 0.0 ||
392       calrgb->white_point[2] <= 0.0)
393     return 0;
394
395   if (calrgb->black_point[0] < 0.0 ||
396       calrgb->black_point[1] < 0.0 ||
397       calrgb->black_point[2] < 0.0)
398     return 0;
399
400   if (calrgb->gamma[0] < 0.0 ||
401       calrgb->gamma[1] < 0.0 ||
402       calrgb->gamma[2] < 0.0)
403     return 0;
404
405   /* matrix should be invertible? */
406
407   return 1;
408 }
409
410 static pdf_obj *
411 pdf_color_make_calrgb_resource (struct calrgb_cdata *calrgb)
412 {
413   pdf_obj *colorspace;
414   pdf_obj *calparams, *tmp_array;
415
416   ASSERT(calrgb);
417
418   if (!valid_calrgb(calrgb))
419     return NULL;
420
421   colorspace = pdf_new_array();
422   calparams  = pdf_new_dict();
423
424   tmp_array  = pdf_new_array();
425   pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->white_point[0], 0.001)));
426   pdf_add_array(tmp_array, pdf_new_number(1.0));
427   pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->white_point[2], 0.001)));
428   pdf_add_dict(calparams, pdf_new_name("WhitePoint"), tmp_array);
429
430   if (calrgb->black_point[0] != 0.0 ||
431       calrgb->black_point[1] != 0.0 ||
432       calrgb->black_point[2] != 0.0) {
433     tmp_array  = pdf_new_array();
434     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[0], 0.001)));
435     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[1], 0.001)));
436     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[2], 0.001)));
437     pdf_add_dict(calparams, pdf_new_name("BlackPoint"), tmp_array);
438   }
439
440   if (calrgb->gamma[0] != 1.0 ||
441       calrgb->gamma[1] != 1.0 ||
442       calrgb->gamma[2] != 1.0) {
443     tmp_array  = pdf_new_array();
444     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[0], 0.001)));
445     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[1], 0.001)));
446     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[2], 0.001)));
447     pdf_add_dict(calparams, pdf_new_name("Gamma"), tmp_array);
448   }
449
450   if (calrgb->matrix[0] != 1.0 ||
451       calrgb->matrix[1] != 0.0 ||
452       calrgb->matrix[2] != 0.0 ||
453       calrgb->matrix[3] != 0.0 ||
454       calrgb->matrix[4] != 1.0 ||
455       calrgb->matrix[5] != 0.0 ||
456       calrgb->matrix[6] != 0.0 ||
457       calrgb->matrix[7] != 0.0 ||
458       calrgb->matrix[8] != 1.0) {
459     tmp_array  = pdf_new_array();
460     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[0], 0.001)));
461     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[1], 0.001)));
462     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[2], 0.001)));
463     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[3], 0.001)));
464     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[4], 0.001)));
465     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[5], 0.001)));
466     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[6], 0.001)));
467     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[7], 0.001)));
468     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[8], 0.001)));
469     pdf_add_dict(calparams,  pdf_new_name("Matrix"), tmp_array);
470   }
471
472   pdf_add_array(colorspace, pdf_new_name("CalRGB"));
473   pdf_add_array(colorspace, calparams);
474
475   return colorspace;
476 }
477 #endif
478
479 static unsigned char  nullbytes16[16] = {
480   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
481 };
482
483 static struct
484 {
485   int  major;
486   int  minor;
487 } icc_versions[] = {
488   {0, 0}, /* PDF-1.0, we don't support them */
489   {0, 0}, /* PDF-1.1, we don't support them */
490   {0, 0}, /* PDF-1.2, we don't support them */
491   {0x02, 0x10}, /* PDF-1.3 */
492   {0x02, 0x20}, /* PDF-1.4 */
493   {0x04, 0x00}  /* PDF-1.5 */
494 };
495
496 static int
497 iccp_version_supported (int major, int minor)
498 {
499   int  pdf_ver;
500
501   pdf_ver = pdf_get_version();
502   if (pdf_ver < 6) {
503     if (icc_versions[pdf_ver].major < major)
504       return 0;
505     else if (icc_versions[pdf_ver].major == major &&
506              icc_versions[pdf_ver].minor <  minor)
507       return 0;
508     else {
509       return 1;
510     }
511   }
512
513   return 0;
514 }
515
516 typedef unsigned long iccSig;
517 static iccSig
518 str2iccSig (const void *s)
519 {
520   const char  *p;
521
522   p = (const char *) s;
523
524   return (iccSig) ((p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]);
525 }
526
527 typedef struct
528 {
529   long X, Y, Z; /* s15Fixed16Numeber */
530 } iccXYZNumber;
531
532 typedef struct
533 {
534   long          size;
535   iccSig        CMMType;
536   long          version;
537   iccSig        devClass;
538   iccSig        colorSpace;
539   iccSig        PCS;    /* Profile Connection Space */
540   char          creationDate[12];
541   iccSig        acsp;
542   iccSig        platform;
543   char          flags[4];
544   iccSig        devMnfct;
545   iccSig        devModel;
546   char          devAttr[8];
547   long          intent;
548   iccXYZNumber  illuminant;
549   iccSig        creator;
550   unsigned char ID[16]; /* MD5 checksum with Rendering intent,
551                          * Header attrs, Profile ID fields are
552                          * set to zeros.
553                          */
554   /* 28 bytes reserved - must be set to zeros */
555 } iccHeader;
556
557 #define iccNullSig 0
558 static void
559 iccp_init_iccHeader (iccHeader *icch)
560 {
561   ASSERT(icch);
562
563   icch->size       = 0;
564   icch->CMMType    = iccNullSig;
565   icch->version    = 0xFFFFFF;
566   icch->devClass   = iccNullSig;
567   icch->colorSpace = iccNullSig;
568   icch->PCS        = iccNullSig;
569   memset(icch->creationDate, 0, 12);
570   icch->acsp       = str2iccSig("ascp");
571   icch->platform   = iccNullSig;
572   memset(icch->flags, 0, 4);
573   icch->devMnfct   = iccNullSig;
574   icch->devModel   = iccNullSig;
575   memset(icch->devAttr, 0, 8);
576   icch->intent     = 0;
577   icch->illuminant.X = 0;
578   icch->illuminant.Y = 0;
579   icch->illuminant.Z = 0;
580   icch->creator      = iccNullSig;
581   memset(icch->ID, 0, 16);
582 }
583
584 #define ICC_INTENT_TYPE(n) ((int) (((n) >> 16) & 0xff))
585 #define ICC_INTENT_PERCEPTUAL 0
586 #define ICC_INTENT_RELATIVE   1
587 #define ICC_INTENT_SATURATION 2
588 #define ICC_INTENT_ABSOLUTE   3
589
590 /*
591  * In ICC profile stream dicrionary, there is /Range whose values must
592  * "match the information in the profile". But where is those values in?
593  *
594  * How should I treat rendering intent?
595  */
596 struct iccbased_cdata
597 {
598   long           sig; /* 'i' 'c' 'c' 'b' */
599
600   unsigned char  checksum[16]; /* 16 bytes MD5 Checksum   */
601   int            colorspace;   /* input colorspace:
602                                 *   RGB, Gray, CMYK, (Lab?)
603                                 */
604   int            alternate;    /* alternate colorspace (id), unused */
605 };
606
607 #define check_sig(d,p,q,r,s) ((d) && (d)->sig == ((p)<<24|(q)<<16|(r)<<8|(s)))
608
609 static void
610 init_iccbased_cdata (struct iccbased_cdata *cdata)
611 {
612   ASSERT(cdata);
613
614   cdata->sig = ('i' << 24|'c' << 16|'c' << 8|'b');
615   memset(cdata->checksum, 0, 16);
616   cdata->colorspace = PDF_COLORSPACE_TYPE_INVALID;
617   cdata->alternate  = -1;
618
619   return;
620 }
621
622 static void
623 release_iccbased_cdata (struct iccbased_cdata *cdata)
624 {
625   ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b'));
626
627   RELEASE(cdata);
628 }
629
630 static int
631 get_num_components_iccbased (const struct iccbased_cdata *cdata)
632 {
633   int  num_components = 0;
634
635   ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b'));
636
637   switch (cdata->colorspace) {
638   case PDF_COLORSPACE_TYPE_RGB:
639     num_components = 3;
640     break;
641   case PDF_COLORSPACE_TYPE_CMYK:
642     num_components = 4;
643     break;
644   case PDF_COLORSPACE_TYPE_GRAY:
645     num_components = 1;
646     break;
647   case PDF_COLORSPACE_TYPE_CIELAB:
648     num_components = 3;
649     break;
650   }
651
652   return num_components;
653 }
654
655 static int
656 compare_iccbased (const char *ident1, const struct iccbased_cdata *cdata1,
657                   const char *ident2, const struct iccbased_cdata *cdata2)
658 {
659   if (cdata1 && cdata2) {
660
661     ASSERT(check_sig(cdata1, 'i', 'c', 'c', 'b'));
662     ASSERT(check_sig(cdata2, 'i', 'c', 'c', 'b'));
663
664     if (memcmp(cdata1->checksum, nullbytes16, 16) &&
665         memcmp(cdata2->checksum, nullbytes16, 16)) {
666       return memcmp(cdata1->checksum, cdata2->checksum, 16);
667     }
668     if (cdata1->colorspace != cdata2->colorspace) {
669       return (cdata1->colorspace - cdata2->colorspace);
670     }
671
672     /* Continue if checksum unknown and colorspace is same. */
673   }
674
675   if (ident1 && ident2)
676     return strcmp(ident1, ident2);
677
678   /* No way to compare */
679   return -1;
680 }
681
682 int
683 iccp_check_colorspace (int colortype, const void *profile, long proflen)
684 {
685   iccSig  colorspace;
686   const unsigned char  *p;
687
688   if (!profile || proflen < 128)
689     return -1;
690
691   p = (const unsigned char *) profile;
692
693   colorspace = str2iccSig(p + 16);
694
695   switch (colortype) {
696   case PDF_COLORSPACE_TYPE_CALRGB:
697   case PDF_COLORSPACE_TYPE_RGB:
698     if (colorspace != str2iccSig("RGB ")) {
699       return -1;
700     }
701     break;
702   case PDF_COLORSPACE_TYPE_CALGRAY:
703   case PDF_COLORSPACE_TYPE_GRAY:
704     if (colorspace != str2iccSig("GRAY")) {
705       return -1;
706     }
707     break;
708   case PDF_COLORSPACE_TYPE_CMYK:
709     if (colorspace != str2iccSig("CMYK")) {
710       return -1;
711     }
712     break;
713   default:
714     return -1;
715     break;
716   }
717
718   return 0;
719 }
720
721 pdf_obj *
722 iccp_get_rendering_intent (const void *profile, long proflen)
723 {
724   pdf_obj       *ri = NULL;
725   const unsigned char *p;
726   long           intent;
727
728   if (!profile || proflen < 128)
729     return NULL;
730
731   p = (const unsigned char *) profile;
732
733   intent = (p[64] << 24)|(p[65] << 16)|(p[66] << 8)|p[67];
734   switch (ICC_INTENT_TYPE(intent)) {
735   case ICC_INTENT_SATURATION:
736     ri = pdf_new_name("Saturation");
737     break;
738   case ICC_INTENT_PERCEPTUAL:
739     ri = pdf_new_name("Perceptual");
740     break;
741   case ICC_INTENT_ABSOLUTE:
742     ri = pdf_new_name("AbsoluteColorimetric");
743     break;
744   case ICC_INTENT_RELATIVE:
745     ri = pdf_new_name("RelativeColorimetric");
746     break;
747   default:
748     WARN("Invalid rendering intent type: %d", ICC_INTENT_TYPE(intent));
749     ri = NULL;
750   }
751
752   return ri;
753 }
754
755 #define sget_signed_long(p)  ((long)   ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3]))
756 #define sget_signed_short(p) ((short)  ((p)[0] << 8|(p)[1]))
757 #define get_iccSig(p)        ((iccSig) ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3]))
758
759 static int
760 iccp_unpack_header (iccHeader *icch,
761                     const void *profile, long proflen, int check_size)
762 {
763   const unsigned char *p, *endptr;
764
765   if (check_size) {
766     if (!profile || proflen < 128 ||
767         proflen % 4 != 0) {
768       WARN("Profile size: %ld", proflen);
769       return -1;
770     }
771   }
772
773   p      = (const unsigned char *) profile;
774   endptr = p + 128;
775
776   icch->size = sget_signed_long(p);
777   if (check_size) {
778     if (icch->size != proflen) {
779       WARN("ICC Profile size: %ld(header) != %ld", icch->size, proflen);
780       return -1;
781     }
782   }
783   p += 4;
784
785   icch->CMMType    = str2iccSig(p);
786   p += 4;
787   icch->version    = sget_signed_long(p);
788   p += 4;
789   icch->devClass   = str2iccSig(p);
790   p += 4;
791   icch->colorSpace = str2iccSig(p);
792   p += 4;
793   icch->PCS        = str2iccSig(p);
794   p += 4;
795   memcpy(icch->creationDate, p, 12);
796   p += 12;
797   icch->acsp = str2iccSig(p); /* acsp */
798   if (icch->acsp != str2iccSig("acsp")) {
799     WARN("Invalid ICC profile: not \"acsp\" - %c%c%c%c ",
800          p[0], p[1], p[2], p[3]);
801     return -1;
802   }
803   p += 4;
804   icch->platform = str2iccSig(p);
805   p += 4;
806   memcpy(icch->flags, p, 4);
807   p += 4;
808   icch->devMnfct = str2iccSig(p);
809   p += 4;
810   icch->devModel = str2iccSig(p);
811   p += 4;
812   memcpy(icch->devAttr,  p, 8);
813   p += 8;
814   icch->intent = (p[0] << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
815   p += 4;
816   icch->illuminant.X = sget_signed_long(p);
817   p += 4;
818   icch->illuminant.Y = sget_signed_long(p);
819   p += 4;
820   icch->illuminant.Z = sget_signed_long(p);
821   p += 4;
822   icch->creator = str2iccSig(p);
823   p += 4;
824   memcpy(icch->ID, p, 16);
825   p += 16;
826
827   /* 28 bytes reserved - must be set to zeros */
828   for (; p < endptr; p++) {
829     if (*p != '\0') {
830       WARN("Reserved pad not zero: %02x (at offset %ld in ICC profile header.)",
831            *p, 128 - ((long) (endptr - p)));
832       return -1;
833     }
834   }
835
836   return 0;
837 }
838
839 /* MD5 checksum with Rendering intent,
840  * Header attrs, Profile ID fields are
841  * set to zeros.
842  */
843 #define ICC_HEAD_SECT1_START  0
844 #define ICC_HEAD_SECT1_LENGTH 56
845 /* 8 bytes devAttr, 4 bytes intent */
846 #define ICC_HEAD_SECT2_START  68
847 #define ICC_HEAD_SECT2_LENGTH 16
848 /* 16 bytes ID (checksum) */
849 #define ICC_HEAD_SECT3_START  100
850 #define ICC_HEAD_SECT3_LENGTH 28
851
852 #include "dpxcrypt.h"
853 static void
854 iccp_get_checksum (unsigned char *checksum, const void *profile, long proflen)
855 {
856   const unsigned char *p;
857   MD5_CONTEXT    md5;
858
859   p = (const unsigned char *) profile;
860
861   MD5_init (&md5);
862   MD5_write(&md5, p + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH);
863   MD5_write(&md5, nullbytes16, 12);
864   MD5_write(&md5, p + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH);
865   MD5_write(&md5, nullbytes16, 16);
866   MD5_write(&md5, p + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH);
867
868   /* body */
869   MD5_write(&md5, p + 128, proflen - 128);
870
871   MD5_final(checksum, &md5);
872 }
873
874 static void
875 print_iccp_header (iccHeader *icch, unsigned char *checksum)
876 {
877   int   i;
878
879   ASSERT(icch);
880
881 #define print_iccSig(s,t) if ((s) == 0) {\
882     MESG("pdf_color>> %s:\t(null)\n", (t)); \
883   } else if (!isprint(((s) >> 24) & 0xff) || \
884              !isprint(((s) >> 16) & 0xff) || \
885              !isprint(((s) >>  8) & 0xff) || \
886              !isprint((s) & 0xff)) { \
887     MESG("pdf_color>> %s:\t(invalid)\n", (t)); \
888   } else { \
889     MESG("pdf_color>> %s:\t%c%c%c%c\n",  (t), \
890          ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, \
891          ((s) >>  8) & 0xff, (s) & 0xff); \
892 }
893
894   MESG("\n");
895   MESG("pdf_color>> ICC Profile Info\n");
896   MESG("pdf_color>> Profile Size:\t%ld bytes\n", icch->size);
897   print_iccSig(icch->CMMType, "CMM Type");
898   MESG("pdf_color>> Profile Version:\t%d.%01d.%01d\n",
899        (icch->version >> 24) & 0xff,
900        (icch->version >> 20) & 0x0f,
901        (icch->version >> 16) & 0x0f);
902   print_iccSig(icch->devClass,   "Device Class");
903   print_iccSig(icch->colorSpace, "Color Space");
904   print_iccSig(icch->PCS, "Connection Space");
905   MESG("pdf_color>> Creation Date:\t");
906   for (i = 0; i < 12; i += 2) {
907     if (i == 0)
908       MESG("%04u",
909            sget_unsigned_pair((unsigned char *) icch->creationDate));
910     else {
911       MESG(":%02u",
912            sget_unsigned_pair((unsigned char *) (&icch->creationDate[i])));
913     }
914   }
915   MESG("\n");
916   print_iccSig(icch->platform, "Primary Platform");
917   MESG("pdf_color>> Profile Flags:\t%02x:%02x:%02x:%02x\n",
918        icch->flags[0], icch->flags[1], icch->flags[2], icch->flags[3]);
919   print_iccSig(icch->devMnfct, "Device Mnfct");
920   print_iccSig(icch->devModel, "Device Model");
921   MESG("pdf_color>> Device Attr:\t");
922   for (i = 0; i < 8; i++) {
923     if (i == 0)
924       MESG("%02x",  icch->devAttr[i]);
925     else
926       MESG(":%02x", icch->devAttr[i]);
927   }
928   MESG("\n");
929   MESG("pdf_color>> Rendering Intent:\t");
930   switch (ICC_INTENT_TYPE(icch->intent)) {
931   case ICC_INTENT_SATURATION:
932     MESG("Saturation");
933     break;
934   case ICC_INTENT_PERCEPTUAL:
935     MESG("Perceptual");
936     break;
937   case ICC_INTENT_ABSOLUTE:
938     MESG("Absolute Colorimetric");
939     break;
940   case ICC_INTENT_RELATIVE:
941     MESG("Relative Colorimetric");
942     break;
943   default:
944     MESG("(invalid)");
945     break;
946   }
947   MESG("\n");
948   print_iccSig(icch->creator, "Creator");
949   MESG("pdf_color>> Illuminant (XYZ):\t");
950   MESG("%.3f %.3f %.3f\n",
951        (double) icch->illuminant.X / 0x10000,
952        (double) icch->illuminant.Y / 0x10000,
953        (double) icch->illuminant.Z / 0x10000);
954   MESG("pdf_color>> Checksum:\t");
955   if (!memcmp(icch->ID, nullbytes16, 16)) {
956     MESG("(null)");
957   } else {
958     for (i = 0; i < 16; i++) {
959       if (i == 0)
960         MESG("%02x",  icch->ID[i]);
961       else
962         MESG(":%02x", icch->ID[i]);
963     }
964   }
965   MESG("\n");
966   if (checksum) {
967     MESG("pdf_color>> Calculated:\t");
968     for (i = 0; i < 16; i++) {
969       if (i == 0)
970         MESG("%02x", checksum[i]);
971       else
972         MESG(":%02x", checksum[i]);
973     }
974     MESG("\n");
975   }
976
977   return;
978 }
979
980
981 static int
982 iccp_devClass_allowed (int dev_class)
983 {
984   int    colormode;
985
986   colormode = pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE);
987
988   switch (colormode) {
989 #if 0
990   case PDF_DEV_COLORMODE_PDFX1:
991     break;
992   case PDF_DEV_COLORMODE_PDFX3:
993     if (dev_class != str2iccSig("prtr")) {
994       return 0;
995     }
996     break;
997 #endif
998   default:
999     if (dev_class != str2iccSig("scnr") &&
1000         dev_class != str2iccSig("mntr") &&
1001         dev_class != str2iccSig("prtr") &&
1002         dev_class != str2iccSig("spac")) {
1003       return 0;
1004     }
1005     break;
1006   }
1007
1008
1009   return 1;
1010 }
1011
1012 int
1013 iccp_load_profile (const char *ident,
1014                    const void *profile, long proflen)
1015 {
1016   int       cspc_id;
1017   pdf_obj  *resource;
1018   pdf_obj  *stream;
1019   pdf_obj  *stream_dict;
1020   iccHeader icch;
1021   int       colorspace;
1022   unsigned char checksum[16];
1023   struct iccbased_cdata *cdata;
1024
1025   iccp_init_iccHeader(&icch);
1026   if (iccp_unpack_header(&icch, profile, proflen, 1) < 0) { /* check size */
1027     WARN("Invalid ICC profile header in \"%s\"", ident);
1028     print_iccp_header(&icch, NULL);
1029     return -1;
1030   }
1031
1032   if (!iccp_version_supported((icch.version >> 24) & 0xff,
1033                               (icch.version >> 16) & 0xff)) {
1034     WARN("ICC profile format spec. version %d.%01d.%01d"
1035          " not supported in current PDF version setting.",
1036          (icch.version >> 24) & 0xff,
1037          (icch.version >> 20) & 0x0f,
1038          (icch.version >> 16) & 0x0f);
1039     WARN("ICC profile not embedded.");
1040     print_iccp_header(&icch, NULL);
1041     return -1;
1042   }
1043
1044   if (!iccp_devClass_allowed(icch.devClass)) {
1045     WARN("Unsupported ICC Profile Device Class:");
1046     print_iccp_header(&icch, NULL);
1047     return -1;
1048   }
1049
1050   if (icch.colorSpace == str2iccSig("RGB ")) {
1051     colorspace = PDF_COLORSPACE_TYPE_RGB;
1052   } else if (icch.colorSpace == str2iccSig("GRAY")) {
1053     colorspace = PDF_COLORSPACE_TYPE_GRAY;
1054   } else if (icch.colorSpace == str2iccSig("CMYK")) {
1055     colorspace = PDF_COLORSPACE_TYPE_CMYK;
1056   } else {
1057     WARN("Unsupported input color space.");
1058     print_iccp_header(&icch, NULL);
1059     return -1;
1060   }
1061
1062   iccp_get_checksum(checksum, profile, proflen);
1063   if (memcmp(icch.ID,  nullbytes16, 16) &&
1064       memcmp(icch.ID,  checksum, 16)) {
1065     WARN("Invalid ICC profile: Inconsistent checksum.");
1066     print_iccp_header(&icch, checksum);
1067     return -1;
1068   }
1069
1070   cdata = NEW(1, struct iccbased_cdata);
1071   init_iccbased_cdata(cdata);
1072   cdata->colorspace = colorspace;
1073   memcpy(cdata->checksum, checksum, 16);
1074
1075   cspc_id = pdf_colorspace_findresource(ident,
1076                                         PDF_COLORSPACE_TYPE_ICCBASED, cdata);
1077   if (cspc_id >= 0) {
1078     if (verbose)
1079       MESG("(ICCP:[id=%d])", cspc_id);
1080     release_iccbased_cdata(cdata);
1081     return cspc_id;
1082   }
1083   if (verbose > 1) {
1084     print_iccp_header(&icch, checksum);
1085   }
1086
1087   resource = pdf_new_array();
1088
1089   stream = pdf_new_stream(STREAM_COMPRESS);
1090   pdf_add_array(resource, pdf_new_name("ICCBased"));
1091   pdf_add_array(resource, pdf_ref_obj (stream));
1092
1093   stream_dict = pdf_stream_dict(stream);
1094   pdf_add_dict(stream_dict, pdf_new_name("N"),
1095                pdf_new_number(get_num_components_iccbased(cdata)));
1096
1097   pdf_add_stream (stream, profile, proflen);
1098   pdf_release_obj(stream);
1099
1100   cspc_id = pdf_colorspace_defineresource(ident,
1101                                           PDF_COLORSPACE_TYPE_ICCBASED,
1102                                           cdata, resource);
1103
1104   return cspc_id;
1105 }
1106
1107 #if 0
1108 #define WBUF_SIZE 4096
1109 static unsigned char wbuf[WBUF_SIZE];
1110
1111 static pdf_obj *
1112 iccp_load_file_stream (unsigned char *checksum, long length, FILE *fp)
1113 {
1114   pdf_obj       *stream;
1115   MD5_CONTEXT    md5;
1116   long           nb_read;
1117
1118   rewind(fp);
1119
1120   if (fread(wbuf, 1, 128, fp) != 128) {
1121     return NULL;
1122   }
1123   length -= 128;
1124
1125   stream = pdf_new_stream(STREAM_COMPRESS);
1126
1127   MD5_init (&md5);
1128   MD5_write(&md5, wbuf + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH);
1129   MD5_write(&md5, nullbytes16, 12);
1130   MD5_write(&md5, wbuf + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH);
1131   MD5_write(&md5, nullbytes16, 16);
1132   MD5_write(&md5, wbuf + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH);
1133
1134   pdf_add_stream(stream, wbuf, 128);
1135
1136   /* body */
1137   while (length > 0) {
1138     nb_read = fread(wbuf, 1, MIN(length, WBUF_SIZE), fp);
1139     MD5_write(&md5, wbuf, nb_read);
1140     pdf_add_stream(stream, wbuf, nb_read);
1141     length -= nb_read;
1142   }
1143
1144   MD5_final(checksum, &md5);
1145
1146
1147   return stream;
1148 }
1149
1150 int
1151 pdf_colorspace_load_ICCBased (const char *ident, const char *filename)
1152 {
1153   int       cspc_id;
1154   FILE     *fp;
1155   pdf_obj  *resource;
1156   pdf_obj  *stream;
1157   pdf_obj  *stream_dict;
1158   iccHeader icch;
1159   int       colorspace;
1160   long      size;
1161   unsigned char checksum[16];
1162   struct iccbased_cdata *cdata;
1163
1164
1165   fp = DPXFOPEN(filename, DPX_RES_TYPE_ICCPROFILE);
1166   if (!fp)
1167     return -1;
1168
1169   size = file_size(fp);
1170   if (size < 128) {
1171     MFCLOSE(fp);
1172     return -1;
1173   }
1174   if (fread(wbuf, 1, 128, fp) != 128) {
1175     DPXFCLOSE(fp);
1176     return -1;
1177   }
1178
1179   iccp_init_iccHeader(&icch);
1180   if (iccp_unpack_header(&icch, wbuf, 128, 0) < 0) {
1181     WARN("Invalid ICC profile header in \"%s\"", ident);
1182     print_iccp_header(&icch, NULL);
1183     DPXFCLOSE(fp);
1184     return -1;
1185   }
1186   if (icch.size > size) {
1187     WARN("File size smaller than recorded in header: %ld %ld",
1188          icch.size, size);
1189     DPXFCLOSE(fp);
1190     return -1;
1191   }
1192
1193   if (!iccp_version_supported((icch.version >> 24) & 0xff,
1194                               (icch.version >> 16) & 0xff)) {
1195     WARN("ICC profile format spec. version %d.%01d.%01d"
1196          " not supported in current PDF version setting.",
1197          (icch.version >> 24) & 0xff,
1198          (icch.version >> 20) & 0x0f,
1199          (icch.version >> 16) & 0x0f);
1200     WARN("ICC profile not embedded.");
1201     print_iccp_header(&icch, NULL);
1202     DPXFCLOSE(fp);
1203     return -1;
1204   }
1205
1206   if (!iccp_devClass_allowed(icch.devClass)) {
1207     WARN("Unsupported ICC Profile Device Class:");
1208     print_iccp_header(&icch, NULL);
1209     DPXFCLOSE(fp);
1210     return -1;
1211   }
1212
1213   if (icch.colorSpace == str2iccSig("RGB ")) {
1214     colorspace = PDF_COLORSPACE_TYPE_RGB;
1215   } else if (icch.colorSpace == str2iccSig("GRAY")) {
1216     colorspace = PDF_COLORSPACE_TYPE_GRAY;
1217   } else if (icch.colorSpace == str2iccSig("CMYK")) {
1218     colorspace = PDF_COLORSPACE_TYPE_CMYK;
1219   } else {
1220     WARN("Unsupported input color space.");
1221     print_iccp_header(&icch, NULL);
1222     DPXFCLOSE(fp);
1223     return -1;
1224   }
1225
1226   stream = iccp_load_file_stream(checksum, icch.size, fp);
1227   DPXFCLOSE(fp);
1228
1229   if (!stream) {
1230     WARN("Loading ICC Profile failed...: %s", filename);
1231     return -1;
1232   }
1233
1234   if (memcmp(icch.ID,  nullbytes16, 16) &&
1235       memcmp(icch.ID,  checksum, 16)) {
1236     WARN("Invalid ICC profile: Inconsistent checksum.");
1237     print_iccp_header(&icch, NULL);
1238     pdf_release_obj(stream);
1239     return -1;
1240   }
1241
1242   cdata = NEW(1, struct iccbased_cdata);
1243   init_iccbased_cdata(cdata);
1244   cdata->colorspace = colorspace;
1245   memcpy(cdata->checksum, checksum, 16);
1246
1247   cspc_id = pdf_colorspace_findresource(ident,
1248                                         PDF_COLORSPACE_TYPE_ICCBASED, cdata);
1249   if (cspc_id >= 0) {
1250     if (verbose)
1251       MESG("(ICCP:[id=%d])", cspc_id);
1252     release_iccbased_cdata(cdata);
1253     pdf_release_obj(stream);
1254     return cspc_id;
1255   }
1256   if (verbose > 1) {
1257     print_iccp_header(&icch, checksum);
1258   }
1259
1260   resource = pdf_new_array();
1261
1262   pdf_add_array(resource, pdf_new_name("ICCBased"));
1263   pdf_add_array(resource, pdf_ref_obj (stream));
1264
1265   stream_dict = pdf_stream_dict(stream);
1266   pdf_add_dict(stream_dict, pdf_new_name("N"),
1267                pdf_new_number(get_num_components_iccbased(cdata)));
1268   pdf_release_obj(stream);
1269
1270   cspc_id = pdf_colorspace_defineresource(ident,
1271                                           PDF_COLORSPACE_TYPE_ICCBASED,
1272                                           cdata, resource);
1273
1274   return cspc_id;
1275 }
1276 #endif
1277
1278 typedef struct {
1279   char    *ident;
1280   int      subtype;
1281
1282   pdf_obj *resource;
1283   pdf_obj *reference;
1284
1285   void    *cdata;
1286 } pdf_colorspace;
1287
1288 static struct {
1289   int  count;
1290   int  capacity;
1291   pdf_colorspace *colorspaces;
1292 } cspc_cache = {
1293   0, 0, NULL
1294 };
1295
1296 int
1297 pdf_colorspace_findresource (const char *ident,
1298                              int type, const void *cdata)
1299 {
1300   pdf_colorspace *colorspace;
1301   int  cspc_id, cmp = -1;
1302
1303   for (cspc_id = 0;
1304        cmp && cspc_id < cspc_cache.count; cspc_id++) {
1305     colorspace = &cspc_cache.colorspaces[cspc_id];
1306     if (colorspace->subtype != type)
1307       continue;
1308
1309     switch (colorspace->subtype) {
1310     case PDF_COLORSPACE_TYPE_ICCBASED:
1311       cmp = compare_iccbased(ident, cdata,
1312                              colorspace->ident, colorspace->cdata);
1313       break;
1314     }
1315     if (!cmp)
1316       return cspc_id;
1317   }
1318
1319   return -1; /* not found */
1320 }
1321
1322 static void
1323 pdf_init_colorspace_struct (pdf_colorspace *colorspace)
1324 {
1325   ASSERT(colorspace);
1326
1327   colorspace->ident     = NULL;
1328   colorspace->subtype   = PDF_COLORSPACE_TYPE_INVALID;
1329
1330   colorspace->resource  = NULL;
1331   colorspace->reference = NULL;
1332   colorspace->cdata     = NULL;
1333
1334   return;
1335 }
1336
1337 static void
1338 pdf_clean_colorspace_struct (pdf_colorspace *colorspace)
1339 {
1340   ASSERT(colorspace);
1341
1342   if (colorspace->ident)
1343     RELEASE(colorspace->ident);
1344   if (colorspace->resource)
1345     pdf_release_obj(colorspace->resource);
1346   if (colorspace->reference)
1347     pdf_release_obj(colorspace->reference);
1348   colorspace->resource  = NULL;
1349   colorspace->reference = NULL;
1350
1351   if (colorspace->cdata) {
1352     switch (colorspace->subtype) {
1353     case PDF_COLORSPACE_TYPE_ICCBASED:
1354       release_iccbased_cdata(colorspace->cdata);
1355       break;
1356     }
1357   }
1358   colorspace->cdata     = NULL;
1359   colorspace->subtype   = PDF_COLORSPACE_TYPE_INVALID;
1360
1361   return;
1362 }
1363
1364 static void
1365 pdf_flush_colorspace (pdf_colorspace *colorspace)
1366 {
1367   ASSERT(colorspace);
1368
1369   if (colorspace->resource)
1370     pdf_release_obj(colorspace->resource);
1371   if (colorspace->reference)
1372     pdf_release_obj(colorspace->reference);
1373
1374   colorspace->resource  = NULL;
1375   colorspace->reference = NULL;
1376 }
1377
1378 int
1379 pdf_colorspace_defineresource (const char *ident,
1380                                int subtype, void *cdata, pdf_obj *resource)
1381 {
1382   int  cspc_id;
1383   pdf_colorspace *colorspace;
1384
1385   if (cspc_cache.count >= cspc_cache.capacity) {
1386     cspc_cache.capacity   += 16;
1387     cspc_cache.colorspaces = RENEW(cspc_cache.colorspaces,
1388                                    cspc_cache.capacity, pdf_colorspace);
1389   }
1390   cspc_id    = cspc_cache.count;
1391   colorspace = &cspc_cache.colorspaces[cspc_id];
1392
1393   pdf_init_colorspace_struct(colorspace);
1394   if (ident) {
1395     colorspace->ident = NEW(strlen(ident) + 1, char);
1396     strcpy(colorspace->ident, ident);
1397   }
1398   colorspace->subtype  = subtype;
1399   colorspace->cdata    = cdata;
1400   colorspace->resource = resource;
1401
1402   if (verbose) {
1403     MESG("(ColorSpace:%s", ident);
1404     if (verbose > 1) {
1405       switch (subtype) {
1406       case PDF_COLORSPACE_TYPE_ICCBASED:
1407         MESG("[ICCBased]");
1408         break;
1409       case PDF_COLORSPACE_TYPE_CALRGB:
1410         MESG("[CalRGB]");
1411         break;
1412       case PDF_COLORSPACE_TYPE_CALGRAY:
1413         MESG("[CalGray]");
1414         break;
1415       }
1416     }
1417     MESG(")");
1418   }
1419
1420   cspc_cache.count++;
1421
1422   return cspc_id;
1423 }
1424
1425 pdf_obj *
1426 pdf_get_colorspace_reference (int cspc_id)
1427 {
1428   pdf_colorspace *colorspace;
1429
1430   colorspace = &cspc_cache.colorspaces[cspc_id];
1431   if (!colorspace->reference) {
1432     colorspace->reference = pdf_ref_obj(colorspace->resource);
1433     pdf_release_obj(colorspace->resource); /* .... */
1434     colorspace->resource = NULL;
1435   }
1436
1437   return pdf_link_obj(colorspace->reference);
1438 }
1439
1440 #if 0
1441 int
1442 pdf_get_colorspace_num_components (int cspc_id)
1443 {
1444   pdf_colorspace *colorspace;
1445   int  num_components;
1446
1447   colorspace = &cspc_cache.colorspaces[cspc_id];
1448
1449   switch (colorspace->subtype) {
1450   case PDF_COLORSPACE_TYPE_ICCBASED:
1451     num_components = get_num_components_iccbased(colorspace->cdata);
1452     break;
1453   case PDF_COLORSPACE_TYPE_DEVICEGRAY:
1454     num_components = 1;
1455     break;
1456   case PDF_COLORSPACE_TYPE_DEVICERGB:
1457     num_components = 3;
1458     break;
1459   case PDF_COLORSPACE_TYPE_DEVICECMYK:
1460     num_components = 4;
1461     break;
1462   case PDF_COLORSPACE_TYPE_CALRGB:
1463     num_components = 3;
1464     break;
1465   case PDF_COLORSPACE_TYPE_CALGRAY:
1466     num_components = 1;
1467     break;
1468   default:
1469     num_components = 0;
1470     break;
1471   }
1472
1473   return num_components;
1474 }
1475
1476 int
1477 pdf_get_colorspace_subtype (int cspc_id)
1478 {
1479   pdf_colorspace *colorspace;
1480
1481   colorspace = &cspc_cache.colorspaces[cspc_id];
1482
1483   return colorspace->subtype;
1484 }
1485 #endif
1486
1487 void
1488 pdf_init_colors (void)
1489 {
1490   cspc_cache.count    = 0;
1491   cspc_cache.capacity = 0;
1492   cspc_cache.colorspaces = NULL;
1493 }
1494
1495 void
1496 pdf_close_colors (void)
1497 {
1498   int  i;
1499
1500   for (i = 0; i < cspc_cache.count; i++) {
1501     pdf_colorspace *colorspace;
1502
1503     colorspace = &cspc_cache.colorspaces[i];
1504     pdf_flush_colorspace(colorspace);
1505     pdf_clean_colorspace_struct(colorspace);
1506   }
1507   RELEASE(cspc_cache.colorspaces);
1508   cspc_cache.colorspaces = NULL;
1509   cspc_cache.count = cspc_cache.capacity = 0;
1510
1511 }
1512
1513 #define PDF_COLORSPACE_FAMILY_DEVICE   0
1514 #define PDF_COLORSPACE_FAMILY_CIEBASED 1
1515 #define PDF_COLORSPACE_FAMILY_SPECIAL  2