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