Back to index

texmacs  1.0.7.15
pdfcolor.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/pdfcolor.c,v 1.17 2008/12/11 16:03:04 matthias Exp $
00002     
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014     
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019     
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00023 */
00024 
00025 /* No page independence here...
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include "config.h"
00030 #endif
00031 
00032 #include "system.h"
00033 #include "mem.h"
00034 #include "error.h"
00035 
00036 #include "dpxfile.h"
00037 
00038 #include "pdfdoc.h"
00039 #include "pdfdraw.h"
00040 
00041 #include "pdfcolor.h"
00042 
00043 
00044 static int verbose = 0;
00045 void
00046 pdf_color_set_verbose (void)
00047 {
00048   verbose++;
00049 }
00050 
00051 /* This function returns PDF_COLORSPACE_TYPE_GRAY,
00052  * PDF_COLORSPACE_TYPE_RGB or PDF_COLORSPACE_TYPE_CMYK.
00053  */
00054 int
00055 pdf_color_type (const pdf_color *color)
00056 {
00057   ASSERT(color);
00058 
00059   return -color->num_components;
00060 }
00061 
00062 int
00063 pdf_color_rgbcolor (pdf_color *color, double r, double g, double b)
00064 {
00065   ASSERT(color);
00066 
00067   if (r < 0.0 || r > 1.0) {
00068     WARN("Invalid color value specified: red=%g",   r);
00069     return -1;
00070   }
00071   if (g < 0.0 || g > 1.0) {
00072     WARN("Invalid color value specified: green=%g", g);
00073     return -1;
00074   }
00075   if (b < 0.0 || b > 1.0) {
00076     WARN("Invalid color value specified: blue=%g", b);
00077     return -1;
00078   }
00079   color->values[0] = r;
00080   color->values[1] = g;
00081   color->values[2] = b;
00082 
00083   color->num_components = 3;
00084 
00085   return 0;
00086 }
00087 
00088 int
00089 pdf_color_cmykcolor (pdf_color *color,
00090                    double c, double m, double y, double k)
00091 {
00092   ASSERT(color);
00093 
00094   if (c < 0.0 || c > 1.0) {
00095     WARN("Invalid color value specified: cyan=%g", c);
00096     return -1;
00097   }
00098   if (m < 0.0 || m > 1.0) {
00099     WARN("Invalid color value specified: magenta=%g", m);
00100     return -1;
00101   }
00102   if (y < 0.0 || y > 1.0) {
00103     WARN("Invalid color value specified: yellow=%g", y);
00104     return -1;
00105   }
00106   if (k < 0.0 || k > 1.0) {
00107     WARN("Invalid color value specified: black=%g", k);
00108     return -1;
00109   }
00110 
00111   color->values[0] = c;
00112   color->values[1] = m;
00113   color->values[2] = y;
00114   color->values[3] = k;
00115 
00116   color->num_components = 4;
00117 
00118   return 0;
00119 }
00120 
00121 int
00122 pdf_color_graycolor (pdf_color *color, double g)
00123 {
00124   ASSERT(color);
00125 
00126   if (g < 0.0 || g > 1.0) {
00127     WARN("Invalid color value specified: gray=%g", g);
00128     return -1;
00129   }
00130 
00131   color->values[0] = g;
00132 
00133   color->num_components = 1;
00134 
00135   return 0;
00136 }
00137 
00138 
00139 void
00140 pdf_color_copycolor (pdf_color *color1, const pdf_color *color2)
00141 {
00142   ASSERT(color1 && color2);
00143 
00144   memcpy(color1, color2, sizeof(pdf_color));
00145 }
00146 
00147 /* Brighten up a color. f == 0 means no change, f == 1 means white. */
00148 void
00149 pdf_color_brighten_color (pdf_color *dst, const pdf_color *src, double f)
00150 {
00151   ASSERT(dst && src);
00152 
00153   if (f == 1.0) {
00154     pdf_color_white(dst);
00155   } else {
00156     double f0, f1;
00157     int n;
00158 
00159     n = dst->num_components = src->num_components;
00160     f1 = n == 4 ? 0.0 : f;  /* n == 4 is CMYK, others are RGB and Gray */
00161     f0 = 1.0-f;
00162 
00163     while (n--)
00164       dst->values[n] = f0 * src->values[n] + f1;
00165   }
00166 }
00167 
00168 int
00169 pdf_color_is_white (const pdf_color *color)
00170 {
00171   int n;
00172   double f;
00173 
00174   ASSERT(color);
00175 
00176   n = color->num_components;
00177   f = n == 4 ? 0.0 : 1.0;  /* n == 4 is CMYK, others are RGB and Gray */
00178 
00179   while (n--)
00180     if (color->values[n] != f)
00181       return 0;
00182 
00183   return 1;
00184 }
00185 
00186 int
00187 pdf_color_to_string (const pdf_color *color, char *buffer)
00188 {
00189   int i, len = 0;
00190 
00191   for (i = 0; i < color->num_components; i++) {
00192     len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001));
00193   }
00194   return len;
00195 }
00196 
00197 pdf_color current_fill   = {
00198   1,
00199   {0.0, 0.0, 0.0, 0.0}
00200 };
00201 
00202 pdf_color current_stroke = {
00203   1,
00204   {0.0, 0.0, 0.0, 0.0}
00205 };
00206 
00207 /*
00208  * This routine is not a real color matching.
00209  */
00210 int
00211 pdf_color_compare (const pdf_color *color1, const pdf_color *color2)
00212 {
00213   int n = color1->num_components;
00214 
00215   if (n != color2->num_components)
00216     return -1;
00217 
00218   while (n--)
00219     if (color1->values[n] != color2->values[n])
00220       return -1;
00221 
00222   return 0;
00223 }
00224 
00225 int
00226 pdf_color_is_valid (const pdf_color *color)
00227 {
00228   int  n;
00229 
00230   n = color->num_components;
00231   if (n != 1 && n != 3 && n != 4)
00232     return 0;
00233 
00234   while (n--)
00235     if (color->values[n] < 0.0 || color->values[n] > 1.0)
00236       return 0;
00237 
00238   return 1;
00239 }
00240 
00241 /* Dvipdfm special */
00242 #define DEV_COLOR_STACK_MAX 128
00243 
00244 static struct {
00245   int       current;
00246   pdf_color stroke[DEV_COLOR_STACK_MAX];
00247   pdf_color fill[DEV_COLOR_STACK_MAX];
00248 } color_stack;
00249 
00250 void
00251 pdf_color_clear_stack (void)
00252 {
00253   if (color_stack.current > 0) {
00254     WARN("You've mistakenly made a global color change within nested colors.");
00255   }
00256   color_stack.current = 0;
00257   pdf_color_black(color_stack.stroke);
00258   pdf_color_black(color_stack.fill);
00259   return;
00260 }
00261 
00262 void
00263 pdf_color_set (pdf_color *sc, pdf_color *fc)
00264 {
00265   pdf_color_copycolor(&color_stack.stroke[color_stack.current], sc);
00266   pdf_color_copycolor(&color_stack.fill[color_stack.current], fc);
00267   pdf_dev_reset_color(0);
00268 }
00269 
00270 void
00271 pdf_color_push (pdf_color *sc, pdf_color *fc)
00272 {
00273   if (color_stack.current >= DEV_COLOR_STACK_MAX-1) {
00274     WARN("Color stack overflow. Just ignore.");
00275   } else {
00276     color_stack.current++;
00277     pdf_color_set(sc, fc);
00278   }
00279   return;
00280 }
00281 
00282 void
00283 pdf_color_pop (void)
00284 {
00285   if (color_stack.current <= 0) {
00286     WARN("Color stack underflow. Just ignore.");
00287   } else {
00288     color_stack.current--;
00289     pdf_dev_reset_color(0);
00290   }
00291   return;
00292 }
00293 
00294 void
00295 pdf_color_get_current (pdf_color **sc, pdf_color **fc)
00296 {
00297   *sc = &color_stack.stroke[color_stack.current];
00298   *fc = &color_stack.fill[color_stack.current];
00299   return;
00300 }
00301 
00302 /***************************** COLOR SPACE *****************************/
00303 
00304 static int pdf_colorspace_defineresource (const char *ident,
00305                                      int   subtype,
00306                                      void *cdata, pdf_obj *resource);
00307 
00308 static int pdf_colorspace_findresource   (const char *ident,
00309                                      int   subtype, const void *cdata);
00310 
00311 #if 0
00312 struct calgray_cdata
00313 {
00314   double white_point[3]; /* required, second component must
00315                        * be equal to 1.0
00316                        */
00317   double black_point[3]; /* optional, default: [0 0 0] */
00318   double gamma;          /* optional, default: 1.0     */
00319 };
00320 
00321 struct calrgb_cdata
00322 {
00323   double white_point[3]; /* required, second component must
00324                        * be equal to 1.0
00325                        */
00326   double black_point[3]; /* optional, default: [0 0 0] */
00327   double gamma[3];       /* optional, default: [1 1 1] */
00328   double matrix[9];      /* optional, default: identity
00329                        * [1 0 0 0 1 0 0 0 1]
00330                        */
00331 };
00332 
00333 static void
00334 release_calrgb (void *cdata)
00335 {
00336   struct calrgb_cdata *calrgb;
00337 
00338   if (cdata) {
00339     calrgb = (struct calrgb_cdata *) cdata;
00340     RELEASE(calrgb);
00341   }
00342 }
00343 
00344 static int
00345 compare_calrgb (const char *ident1, const void *cdata1,
00346               const char *ident2, const void *cdata2)
00347 {
00348   struct calrgb_cdata *calrgb1;
00349   struct calrgb_cdata *calrgb2;
00350 
00351   if (ident1 && ident2 &&
00352       !strcmp(ident1, ident2)) {
00353     return 0;
00354   }
00355 }
00356 
00357 static void
00358 init_calrgb (struct calrgb_cdata *calrgb)
00359 {
00360   ASSERT(calrgb);
00361 
00362   calrgb->white_point[0] = 1.0;
00363   calrgb->white_point[1] = 1.0;
00364   calrgb->white_point[2] = 1.0;
00365 
00366   calrgb->black_point[0] = 0.0;
00367   calrgb->black_point[1] = 0.0;
00368   calrgb->black_point[2] = 0.0;
00369 
00370   calrgb->gamma[0]  = 1.0;
00371   calrgb->gamma[1]  = 1.0;
00372   calrgb->gamma[2]  = 1.0;
00373 
00374   calrgb->matrix[0] = 1.0;
00375   calrgb->matrix[1] = 0.0;
00376   calrgb->matrix[2] = 0.0;
00377 
00378   calrgb->matrix[3] = 0.0;
00379   calrgb->matrix[4] = 1.0;
00380   calrgb->matrix[5] = 0.0;
00381 
00382   calrgb->matrix[6] = 0.0;
00383   calrgb->matrix[7] = 0.0;
00384   calrgb->matrix[8] = 1.0;
00385 }
00386 
00387 static int
00388 valid_calrgb (struct calrgb_cdata *calrgb)
00389 {
00390   if (calrgb->white_point[1] != 1.0 ||
00391       calrgb->white_point[0] <= 0.0 ||
00392       calrgb->white_point[2] <= 0.0)
00393     return 0;
00394 
00395   if (calrgb->black_point[0] < 0.0 ||
00396       calrgb->black_point[1] < 0.0 ||
00397       calrgb->black_point[2] < 0.0)
00398     return 0;
00399 
00400   if (calrgb->gamma[0] < 0.0 ||
00401       calrgb->gamma[1] < 0.0 ||
00402       calrgb->gamma[2] < 0.0)
00403     return 0;
00404 
00405   /* matrix should be invertible? */
00406 
00407   return 1;
00408 }
00409 
00410 static pdf_obj *
00411 pdf_color_make_calrgb_resource (struct calrgb_cdata *calrgb)
00412 {
00413   pdf_obj *colorspace;
00414   pdf_obj *calparams, *tmp_array;
00415 
00416   ASSERT(calrgb);
00417 
00418   if (!valid_calrgb(calrgb))
00419     return NULL;
00420 
00421   colorspace = pdf_new_array();
00422   calparams  = pdf_new_dict();
00423 
00424   tmp_array  = pdf_new_array();
00425   pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->white_point[0], 0.001)));
00426   pdf_add_array(tmp_array, pdf_new_number(1.0));
00427   pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->white_point[2], 0.001)));
00428   pdf_add_dict(calparams, pdf_new_name("WhitePoint"), tmp_array);
00429 
00430   if (calrgb->black_point[0] != 0.0 ||
00431       calrgb->black_point[1] != 0.0 ||
00432       calrgb->black_point[2] != 0.0) {
00433     tmp_array  = pdf_new_array();
00434     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[0], 0.001)));
00435     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[1], 0.001)));
00436     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->black_point[2], 0.001)));
00437     pdf_add_dict(calparams, pdf_new_name("BlackPoint"), tmp_array);
00438   }
00439 
00440   if (calrgb->gamma[0] != 1.0 ||
00441       calrgb->gamma[1] != 1.0 ||
00442       calrgb->gamma[2] != 1.0) {
00443     tmp_array  = pdf_new_array();
00444     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[0], 0.001)));
00445     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[1], 0.001)));
00446     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->gamma[2], 0.001)));
00447     pdf_add_dict(calparams, pdf_new_name("Gamma"), tmp_array);
00448   }
00449 
00450   if (calrgb->matrix[0] != 1.0 ||
00451       calrgb->matrix[1] != 0.0 ||
00452       calrgb->matrix[2] != 0.0 ||
00453       calrgb->matrix[3] != 0.0 ||
00454       calrgb->matrix[4] != 1.0 ||
00455       calrgb->matrix[5] != 0.0 ||
00456       calrgb->matrix[6] != 0.0 ||
00457       calrgb->matrix[7] != 0.0 ||
00458       calrgb->matrix[8] != 1.0) {
00459     tmp_array  = pdf_new_array();
00460     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[0], 0.001)));
00461     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[1], 0.001)));
00462     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[2], 0.001)));
00463     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[3], 0.001)));
00464     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[4], 0.001)));
00465     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[5], 0.001)));
00466     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[6], 0.001)));
00467     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[7], 0.001)));
00468     pdf_add_array(tmp_array, pdf_new_number(ROUND(calrgb->matrix[8], 0.001)));
00469     pdf_add_dict(calparams,  pdf_new_name("Matrix"), tmp_array);
00470   }
00471 
00472   pdf_add_array(colorspace, pdf_new_name("CalRGB"));
00473   pdf_add_array(colorspace, calparams);
00474 
00475   return colorspace;
00476 }
00477 #endif
00478 
00479 static unsigned char  nullbytes16[16] = {
00480   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
00481 };
00482 
00483 static struct
00484 {
00485   int  major;
00486   int  minor;
00487 } icc_versions[] = {
00488   {0, 0}, /* PDF-1.0, we don't support them */
00489   {0, 0}, /* PDF-1.1, we don't support them */
00490   {0, 0}, /* PDF-1.2, we don't support them */
00491   {0x02, 0x10}, /* PDF-1.3 */
00492   {0x02, 0x20}, /* PDF-1.4 */
00493   {0x04, 0x00}  /* PDF-1.5 */
00494 };
00495 
00496 static int
00497 iccp_version_supported (int major, int minor)
00498 {
00499   int  pdf_ver;
00500 
00501   pdf_ver = pdf_get_version();
00502   if (pdf_ver < 6) {
00503     if (icc_versions[pdf_ver].major < major)
00504       return 0;
00505     else if (icc_versions[pdf_ver].major == major &&
00506             icc_versions[pdf_ver].minor <  minor)
00507       return 0;
00508     else {
00509       return 1;
00510     }
00511   }
00512 
00513   return 0;
00514 }
00515 
00516 typedef unsigned long iccSig;
00517 static iccSig
00518 str2iccSig (const void *s)
00519 {
00520   char  *p;
00521 
00522   p = (char *) s;
00523 
00524   return (iccSig) ((p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]);
00525 }
00526 
00527 typedef struct
00528 {
00529   long X, Y, Z; /* s15Fixed16Numeber */
00530 } iccXYZNumber;
00531 
00532 typedef struct
00533 {
00534   long          size;
00535   iccSig        CMMType;
00536   long          version;
00537   iccSig        devClass;
00538   iccSig        colorSpace;
00539   iccSig        PCS;    /* Profile Connection Space */
00540   char          creationDate[12];
00541   iccSig        acsp;
00542   iccSig        platform;
00543   char          flags[4];
00544   iccSig        devMnfct;
00545   iccSig        devModel;
00546   char          devAttr[8];
00547   long          intent;
00548   iccXYZNumber  illuminant;
00549   iccSig        creator;
00550   unsigned char ID[16]; /* MD5 checksum with Rendering intent,
00551                       * Header attrs, Profile ID fields are
00552                       * set to zeros.
00553                       */
00554   /* 28 bytes reserved - must be set to zeros */
00555 } iccHeader;
00556 
00557 #define iccNullSig 0
00558 static void
00559 iccp_init_iccHeader (iccHeader *icch)
00560 {
00561   ASSERT(icch);
00562 
00563   icch->size       = 0;
00564   icch->CMMType    = iccNullSig;
00565   icch->version    = 0xFFFFFF;
00566   icch->devClass   = iccNullSig;
00567   icch->colorSpace = iccNullSig;
00568   icch->PCS        = iccNullSig;
00569   memset(icch->creationDate, 0, 12);
00570   icch->acsp       = str2iccSig("ascp");
00571   icch->platform   = iccNullSig;
00572   memset(icch->flags, 0, 4);
00573   icch->devMnfct   = iccNullSig;
00574   icch->devModel   = iccNullSig;
00575   memset(icch->devAttr, 0, 8);
00576   icch->intent     = 0;
00577   icch->illuminant.X = 0;
00578   icch->illuminant.Y = 0;
00579   icch->illuminant.Z = 0;
00580   icch->creator      = iccNullSig;
00581   memset(icch->ID, 0, 16);
00582 }
00583 
00584 #define ICC_INTENT_TYPE(n) ((int) (((n) >> 16) & 0xff))
00585 #define ICC_INTENT_PERCEPTUAL 0
00586 #define ICC_INTENT_RELATIVE   1
00587 #define ICC_INTENT_SATURATION 2
00588 #define ICC_INTENT_ABSOLUTE   3
00589 
00590 /*
00591  * In ICC profile stream dicrionary, there is /Range whose values must
00592  * "match the information in the profile". But where is those values in?
00593  *
00594  * How should I treat rendering intent?
00595  */
00596 struct iccbased_cdata
00597 {
00598   long           sig; /* 'i' 'c' 'c' 'b' */
00599 
00600   unsigned char  checksum[16]; /* 16 bytes MD5 Checksum   */
00601   int            colorspace;   /* input colorspace:
00602                             *   RGB, Gray, CMYK, (Lab?)
00603                             */
00604   int            alternate;    /* alternate colorspace (id), unused */
00605 };
00606 
00607 #define check_sig(d,p,q,r,s) ((d) && (d)->sig == ((p)<<24|(q)<<16|(r)<<8|(s)))
00608 
00609 static void
00610 init_iccbased_cdata (struct iccbased_cdata *cdata)
00611 {
00612   ASSERT(cdata);
00613 
00614   cdata->sig = ('i' << 24|'c' << 16|'c' << 8|'b');
00615   memset(cdata->checksum, 0, 16);
00616   cdata->colorspace = PDF_COLORSPACE_TYPE_INVALID;
00617   cdata->alternate  = -1;
00618 
00619   return;
00620 }
00621 
00622 static void
00623 release_iccbased_cdata (struct iccbased_cdata *cdata)
00624 {
00625   ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b'));
00626 
00627   RELEASE(cdata);
00628 }
00629 
00630 static int
00631 get_num_components_iccbased (const struct iccbased_cdata *cdata)
00632 {
00633   int  num_components = 0;
00634 
00635   ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b'));
00636 
00637   switch (cdata->colorspace) {
00638   case PDF_COLORSPACE_TYPE_RGB:
00639     num_components = 3;
00640     break;
00641   case PDF_COLORSPACE_TYPE_CMYK:
00642     num_components = 4;
00643     break;
00644   case PDF_COLORSPACE_TYPE_GRAY:
00645     num_components = 1;
00646     break;
00647   case PDF_COLORSPACE_TYPE_CIELAB:
00648     num_components = 3;
00649     break;
00650   }
00651 
00652   return num_components;
00653 }
00654 
00655 static int
00656 compare_iccbased (const char *ident1, const struct iccbased_cdata *cdata1,
00657                 const char *ident2, const struct iccbased_cdata *cdata2)
00658 {
00659   if (cdata1 && cdata2) {
00660 
00661     ASSERT(check_sig(cdata1, 'i', 'c', 'c', 'b'));
00662     ASSERT(check_sig(cdata2, 'i', 'c', 'c', 'b'));
00663 
00664     if (memcmp(cdata1->checksum, nullbytes16, 16) &&
00665        memcmp(cdata2->checksum, nullbytes16, 16)) {
00666       return memcmp(cdata1->checksum, cdata2->checksum, 16);
00667     }
00668     if (cdata1->colorspace != cdata2->colorspace) {
00669       return (cdata1->colorspace - cdata2->colorspace);
00670     }
00671 
00672     /* Continue if checksum unknown and colorspace is same. */
00673   }
00674 
00675   if (ident1 && ident2)
00676     return strcmp(ident1, ident2);
00677 
00678   /* No way to compare */
00679   return -1;
00680 }
00681 
00682 int
00683 iccp_check_colorspace (int colortype, const void *profile, long proflen)
00684 {
00685   iccSig  colorspace;
00686   unsigned char  *p;
00687 
00688   if (!profile || proflen < 128)
00689     return -1;
00690 
00691   p = (unsigned char *) profile;
00692 
00693   colorspace = str2iccSig(p + 16);
00694 
00695   switch (colortype) {
00696   case PDF_COLORSPACE_TYPE_CALRGB:
00697   case PDF_COLORSPACE_TYPE_RGB:
00698     if (colorspace != str2iccSig("RGB ")) {
00699       return -1;
00700     }
00701     break;
00702   case PDF_COLORSPACE_TYPE_CALGRAY:
00703   case PDF_COLORSPACE_TYPE_GRAY:
00704     if (colorspace != str2iccSig("GRAY")) {
00705       return -1;
00706     }
00707     break;
00708   case PDF_COLORSPACE_TYPE_CMYK:
00709     if (colorspace != str2iccSig("CMYK")) {
00710       return -1;
00711     }
00712     break;
00713   default:
00714     return -1;
00715     break;
00716   }
00717 
00718   return 0;
00719 }
00720 
00721 pdf_obj *
00722 iccp_get_rendering_intent (const void *profile, long proflen)
00723 {
00724   pdf_obj       *ri = NULL;
00725   unsigned char *p;
00726   long           intent;
00727 
00728   if (!profile || proflen < 128)
00729     return NULL;
00730 
00731   p = (unsigned char *) profile;
00732 
00733   intent = (p[64] << 24)|(p[65] << 16)|(p[66] << 8)|p[67];
00734   switch (ICC_INTENT_TYPE(intent)) {
00735   case ICC_INTENT_SATURATION:
00736     ri = pdf_new_name("Saturation");
00737     break;
00738   case ICC_INTENT_PERCEPTUAL:
00739     ri = pdf_new_name("Perceptual");
00740     break;
00741   case ICC_INTENT_ABSOLUTE:
00742     ri = pdf_new_name("AbsoluteColorimetric");
00743     break;
00744   case ICC_INTENT_RELATIVE:
00745     ri = pdf_new_name("RelativeColorimetric");
00746     break;
00747   default:
00748     WARN("Invalid rendering intent type: %d", ICC_INTENT_TYPE(intent));
00749     ri = NULL;
00750   }
00751 
00752   return ri;
00753 }
00754 
00755 #define sget_signed_long(p)  ((long)   ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3]))
00756 #define sget_signed_short(p) ((short)  ((p)[0] << 8|(p)[1]))
00757 #define get_iccSig(p)        ((iccSig) ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3]))
00758 
00759 static int
00760 iccp_unpack_header (iccHeader *icch,
00761                   const void *profile, long proflen, int check_size)
00762 {
00763   unsigned char *p, *endptr;
00764 
00765   if (check_size) {
00766     if (!profile || proflen < 128 ||
00767        proflen % 4 != 0) {
00768       WARN("Profile size: %ld", proflen);
00769       return -1;
00770     }
00771   }
00772 
00773   p      = (unsigned char *) profile;
00774   endptr = p + 128;
00775 
00776   icch->size = sget_signed_long(p);
00777   if (check_size) {
00778     if (icch->size != proflen) {
00779       WARN("ICC Profile size: %ld(header) != %ld", icch->size, proflen);
00780       return -1;
00781     }
00782   }
00783   p += 4;
00784 
00785   icch->CMMType    = str2iccSig(p);
00786   p += 4;
00787   icch->version    = sget_signed_long(p);
00788   p += 4;
00789   icch->devClass   = str2iccSig(p);
00790   p += 4;
00791   icch->colorSpace = str2iccSig(p);
00792   p += 4;
00793   icch->PCS        = str2iccSig(p);
00794   p += 4;
00795   memcpy(icch->creationDate, p, 12);
00796   p += 12;
00797   icch->acsp = str2iccSig(p); /* acsp */
00798   if (icch->acsp != str2iccSig("acsp")) {
00799     WARN("Invalid ICC profile: not \"acsp\" - %c%c%c%c ",
00800         p[0], p[1], p[2], p[3]);
00801     return -1;
00802   }
00803   p += 4;
00804   icch->platform = str2iccSig(p);
00805   p += 4;
00806   memcpy(icch->flags, p, 4);
00807   p += 4;
00808   icch->devMnfct = str2iccSig(p);
00809   p += 4;
00810   icch->devModel = str2iccSig(p);
00811   p += 4;
00812   memcpy(icch->devAttr,  p, 8);
00813   p += 8;
00814   icch->intent = (p[0] << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
00815   p += 4;
00816   icch->illuminant.X = sget_signed_long(p);
00817   p += 4;
00818   icch->illuminant.Y = sget_signed_long(p);
00819   p += 4;
00820   icch->illuminant.Z = sget_signed_long(p);
00821   p += 4;
00822   icch->creator = str2iccSig(p);
00823   p += 4;
00824   memcpy(icch->ID, p, 16);
00825   p += 16;
00826 
00827   /* 28 bytes reserved - must be set to zeros */
00828   for (; p < endptr; p++) {
00829     if (*p != '\0') {
00830       WARN("Reserved pad not zero: %02x (at offset %ld in ICC profile header.)",
00831           *p, 128 - ((long) (endptr - p)));
00832       return -1;
00833     }
00834   }
00835 
00836   return 0;
00837 }
00838 
00839 /* MD5 checksum with Rendering intent,
00840  * Header attrs, Profile ID fields are
00841  * set to zeros.
00842  */
00843 #define ICC_HEAD_SECT1_START  0
00844 #define ICC_HEAD_SECT1_LENGTH 56
00845 /* 8 bytes devAttr, 4 bytes intent */
00846 #define ICC_HEAD_SECT2_START  68
00847 #define ICC_HEAD_SECT2_LENGTH 16
00848 /* 16 bytes ID (checksum) */
00849 #define ICC_HEAD_SECT3_START  100
00850 #define ICC_HEAD_SECT3_LENGTH 28
00851 
00852 #include "dpxcrypt.h"
00853 static void
00854 iccp_get_checksum (unsigned char *checksum, const void *profile, long proflen)
00855 {
00856   unsigned char *p;
00857   MD5_CONTEXT    md5;
00858 
00859   p = (unsigned char *) profile;
00860 
00861   MD5_init (&md5);
00862   MD5_write(&md5, p + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH);
00863   MD5_write(&md5, nullbytes16, 12);
00864   MD5_write(&md5, p + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH);
00865   MD5_write(&md5, nullbytes16, 16);
00866   MD5_write(&md5, p + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH);
00867 
00868   /* body */
00869   MD5_write(&md5, p + 128, proflen - 128);
00870 
00871   MD5_final(checksum, &md5);
00872 }
00873 
00874 static void
00875 print_iccp_header (iccHeader *icch, unsigned char *checksum)
00876 {
00877   int   i;
00878 
00879   ASSERT(icch);
00880 
00881 #define print_iccSig(s,t) if ((s) == 0) {\
00882     MESG("pdf_color>> %s:\t(null)\n", (t)); \
00883   } else if (!isprint(((s) >> 24) & 0xff) || \
00884              !isprint(((s) >> 16) & 0xff) || \
00885              !isprint(((s) >>  8) & 0xff) || \
00886              !isprint((s) & 0xff)) { \
00887     MESG("pdf_color>> %s:\t(invalid)\n", (t)); \
00888   } else { \
00889     MESG("pdf_color>> %s:\t%c%c%c%c\n",  (t), \
00890          ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, \
00891          ((s) >>  8) & 0xff, (s) & 0xff); \
00892 }
00893 
00894   MESG("\n");
00895   MESG("pdf_color>> ICC Profile Info\n");
00896   MESG("pdf_color>> Profile Size:\t%ld bytes\n", icch->size);
00897   print_iccSig(icch->CMMType, "CMM Type");
00898   MESG("pdf_color>> Profile Version:\t%d.%01d.%01d\n",
00899        (icch->version >> 24) & 0xff,
00900        (icch->version >> 20) & 0x0f,
00901        (icch->version >> 16) & 0x0f);
00902   print_iccSig(icch->devClass,   "Device Class");
00903   print_iccSig(icch->colorSpace, "Color Space");
00904   print_iccSig(icch->PCS, "Connection Space");
00905   MESG("pdf_color>> Creation Date:\t");
00906   for (i = 0; i < 12; i += 2) {
00907     if (i == 0)
00908       MESG("%04u",
00909           sget_unsigned_pair((unsigned char *) icch->creationDate));
00910     else {
00911       MESG(":%02u",
00912           sget_unsigned_pair((unsigned char *) (&icch->creationDate[i])));
00913     }
00914   }
00915   MESG("\n");
00916   print_iccSig(icch->platform, "Primary Platform");
00917   MESG("pdf_color>> Profile Flags:\t%02x:%02x:%02x:%02x\n",
00918        icch->flags[0], icch->flags[1], icch->flags[2], icch->flags[3]);
00919   print_iccSig(icch->devMnfct, "Device Mnfct");
00920   print_iccSig(icch->devModel, "Device Model");
00921   MESG("pdf_color>> Device Attr:\t");
00922   for (i = 0; i < 8; i++) {
00923     if (i == 0)
00924       MESG("%02x",  icch->devAttr[i]);
00925     else
00926       MESG(":%02x", icch->devAttr[i]);
00927   }
00928   MESG("\n");
00929   MESG("pdf_color>> Rendering Intent:\t");
00930   switch (ICC_INTENT_TYPE(icch->intent)) {
00931   case ICC_INTENT_SATURATION:
00932     MESG("Saturation");
00933     break;
00934   case ICC_INTENT_PERCEPTUAL:
00935     MESG("Perceptual");
00936     break;
00937   case ICC_INTENT_ABSOLUTE:
00938     MESG("Absolute Colorimetric");
00939     break;
00940   case ICC_INTENT_RELATIVE:
00941     MESG("Relative Colorimetric");
00942     break;
00943   default:
00944     MESG("(invalid)");
00945     break;
00946   }
00947   MESG("\n");
00948   print_iccSig(icch->creator, "Creator");
00949   MESG("pdf_color>> Illuminant (XYZ):\t");
00950   MESG("%.3f %.3f %.3f\n",
00951        (double) icch->illuminant.X / 0x10000,
00952        (double) icch->illuminant.Y / 0x10000,
00953        (double) icch->illuminant.Z / 0x10000);
00954   MESG("pdf_color>> Checksum:\t");
00955   if (!memcmp(icch->ID, nullbytes16, 16)) {
00956     MESG("(null)");
00957   } else {
00958     for (i = 0; i < 16; i++) {
00959       if (i == 0)
00960        MESG("%02x",  icch->ID[i]);
00961       else
00962        MESG(":%02x", icch->ID[i]);
00963     }
00964   }
00965   MESG("\n");
00966   if (checksum) {
00967     MESG("pdf_color>> Calculated:\t");
00968     for (i = 0; i < 16; i++) {
00969       if (i == 0)
00970        MESG("%02x", checksum[i]);
00971       else
00972        MESG(":%02x", checksum[i]);
00973     }
00974     MESG("\n");
00975   }
00976 
00977   return;
00978 }
00979 
00980 
00981 static int
00982 iccp_devClass_allowed (int dev_class)
00983 {
00984   int    colormode;
00985 
00986   colormode = pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE);
00987 
00988   switch (colormode) {
00989 #if 0
00990   case PDF_DEV_COLORMODE_PDFX1:
00991     break;
00992   case PDF_DEV_COLORMODE_PDFX3:
00993     if (dev_class != str2iccSig("prtr")) {
00994       return 0;
00995     }
00996     break;
00997 #endif
00998   default:
00999     if (dev_class != str2iccSig("scnr") &&
01000        dev_class != str2iccSig("mntr") &&
01001        dev_class != str2iccSig("prtr") &&
01002        dev_class != str2iccSig("spac")) {
01003       return 0;
01004     }
01005     break;
01006   }
01007 
01008 
01009   return 1;
01010 }
01011 
01012 int
01013 iccp_load_profile (const char *ident,
01014                  const void *profile, long proflen)
01015 {
01016   int       cspc_id;
01017   pdf_obj  *resource;
01018   pdf_obj  *stream;
01019   pdf_obj  *stream_dict;
01020   iccHeader icch;
01021   int       colorspace;
01022   unsigned char checksum[16];
01023   struct iccbased_cdata *cdata;
01024 
01025   iccp_init_iccHeader(&icch);
01026   if (iccp_unpack_header(&icch, profile, proflen, 1) < 0) { /* check size */
01027     WARN("Invalid ICC profile header in \"%s\"", ident);
01028     print_iccp_header(&icch, NULL);
01029     return -1;
01030   }
01031 
01032   if (!iccp_version_supported((icch.version >> 24) & 0xff,
01033                            (icch.version >> 16) & 0xff)) {
01034     WARN("ICC profile format spec. version %d.%01d.%01d"
01035         " not supported in current PDF version setting.",
01036         (icch.version >> 24) & 0xff,
01037         (icch.version >> 20) & 0x0f,
01038         (icch.version >> 16) & 0x0f);
01039     WARN("ICC profile not embedded.");
01040     print_iccp_header(&icch, NULL);
01041     return -1;
01042   }
01043 
01044   if (!iccp_devClass_allowed(icch.devClass)) {
01045     WARN("Unsupported ICC Profile Device Class:");
01046     print_iccp_header(&icch, NULL);
01047     return -1;
01048   }
01049 
01050   if (icch.colorSpace == str2iccSig("RGB ")) {
01051     colorspace = PDF_COLORSPACE_TYPE_RGB;
01052   } else if (icch.colorSpace == str2iccSig("GRAY")) {
01053     colorspace = PDF_COLORSPACE_TYPE_GRAY;
01054   } else if (icch.colorSpace == str2iccSig("CMYK")) {
01055     colorspace = PDF_COLORSPACE_TYPE_CMYK;
01056   } else {
01057     WARN("Unsupported input color space.");
01058     print_iccp_header(&icch, NULL);
01059     return -1;
01060   }
01061 
01062   iccp_get_checksum(checksum, profile, proflen);
01063   if (memcmp(icch.ID,  nullbytes16, 16) &&
01064       memcmp(icch.ID,  checksum, 16)) {
01065     WARN("Invalid ICC profile: Inconsistent checksum.");
01066     print_iccp_header(&icch, checksum);
01067     return -1;
01068   }
01069 
01070   cdata = NEW(1, struct iccbased_cdata);
01071   init_iccbased_cdata(cdata);
01072   cdata->colorspace = colorspace;
01073   memcpy(cdata->checksum, checksum, 16);
01074 
01075   cspc_id = pdf_colorspace_findresource(ident,
01076                                    PDF_COLORSPACE_TYPE_ICCBASED, cdata);
01077   if (cspc_id >= 0) {
01078     if (verbose)
01079       MESG("(ICCP:[id=%d])", cspc_id);
01080     release_iccbased_cdata(cdata);
01081     return cspc_id;
01082   }
01083   if (verbose > 1) {
01084     print_iccp_header(&icch, checksum);
01085   }
01086 
01087   resource = pdf_new_array();
01088 
01089   stream = pdf_new_stream(STREAM_COMPRESS);
01090   pdf_add_array(resource, pdf_new_name("ICCBased"));
01091   pdf_add_array(resource, pdf_ref_obj (stream));
01092 
01093   stream_dict = pdf_stream_dict(stream);
01094   pdf_add_dict(stream_dict, pdf_new_name("N"),
01095               pdf_new_number(get_num_components_iccbased(cdata)));
01096 
01097   pdf_add_stream (stream, profile, proflen);
01098   pdf_release_obj(stream);
01099 
01100   cspc_id = pdf_colorspace_defineresource(ident,
01101                                      PDF_COLORSPACE_TYPE_ICCBASED,
01102                                      cdata, resource);
01103 
01104   return cspc_id;
01105 }
01106 
01107 #if 0
01108 #define WBUF_SIZE 4096
01109 static unsigned char wbuf[WBUF_SIZE];
01110 
01111 static pdf_obj *
01112 iccp_load_file_stream (unsigned char *checksum, long length, FILE *fp)
01113 {
01114   pdf_obj       *stream;
01115   MD5_CONTEXT    md5;
01116   long           nb_read;
01117 
01118   rewind(fp);
01119 
01120   if (fread(wbuf, 1, 128, fp) != 128) {
01121     return NULL;
01122   }
01123   length -= 128;
01124 
01125   stream = pdf_new_stream(STREAM_COMPRESS);
01126 
01127   MD5_init (&md5);
01128   MD5_write(&md5, wbuf + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH);
01129   MD5_write(&md5, nullbytes16, 12);
01130   MD5_write(&md5, wbuf + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH);
01131   MD5_write(&md5, nullbytes16, 16);
01132   MD5_write(&md5, wbuf + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH);
01133 
01134   pdf_add_stream(stream, wbuf, 128);
01135 
01136   /* body */
01137   while (length > 0) {
01138     nb_read = fread(wbuf, 1, MIN(length, WBUF_SIZE), fp);
01139     MD5_write(&md5, wbuf, nb_read);
01140     pdf_add_stream(stream, wbuf, nb_read);
01141     length -= nb_read;
01142   }
01143 
01144   MD5_final(checksum, &md5);
01145 
01146 
01147   return stream;
01148 }
01149 
01150 int
01151 pdf_colorspace_load_ICCBased (const char *ident, const char *filename)
01152 {
01153   int       cspc_id;
01154   FILE     *fp;
01155   pdf_obj  *resource;
01156   pdf_obj  *stream;
01157   pdf_obj  *stream_dict;
01158   iccHeader icch;
01159   int       colorspace;
01160   long      size;
01161   unsigned char checksum[16];
01162   struct iccbased_cdata *cdata;
01163 
01164 
01165   fp = DPXFOPEN(filename, DPX_RES_TYPE_ICCPROFILE);
01166   if (!fp)
01167     return -1;
01168 
01169   size = file_size(fp);
01170   if (size < 128) {
01171     MFCLOSE(fp);
01172     return -1;
01173   }
01174   if (fread(wbuf, 1, 128, fp) != 128) {
01175     DPXFCLOSE(fp);
01176     return -1;
01177   }
01178 
01179   iccp_init_iccHeader(&icch);
01180   if (iccp_unpack_header(&icch, wbuf, 128, 0) < 0) {
01181     WARN("Invalid ICC profile header in \"%s\"", ident);
01182     print_iccp_header(&icch, NULL);
01183     DPXFCLOSE(fp);
01184     return -1;
01185   }
01186   if (icch.size > size) {
01187     WARN("File size smaller than recorded in header: %ld %ld",
01188         icch.size, size);
01189     DPXFCLOSE(fp);
01190     return -1;
01191   }
01192 
01193   if (!iccp_version_supported((icch.version >> 24) & 0xff,
01194                            (icch.version >> 16) & 0xff)) {
01195     WARN("ICC profile format spec. version %d.%01d.%01d"
01196         " not supported in current PDF version setting.",
01197         (icch.version >> 24) & 0xff,
01198         (icch.version >> 20) & 0x0f,
01199         (icch.version >> 16) & 0x0f);
01200     WARN("ICC profile not embedded.");
01201     print_iccp_header(&icch, NULL);
01202     DPXFCLOSE(fp);
01203     return -1;
01204   }
01205 
01206   if (!iccp_devClass_allowed(icch.devClass)) {
01207     WARN("Unsupported ICC Profile Device Class:");
01208     print_iccp_header(&icch, NULL);
01209     DPXFCLOSE(fp);
01210     return -1;
01211   }
01212 
01213   if (icch.colorSpace == str2iccSig("RGB ")) {
01214     colorspace = PDF_COLORSPACE_TYPE_RGB;
01215   } else if (icch.colorSpace == str2iccSig("GRAY")) {
01216     colorspace = PDF_COLORSPACE_TYPE_GRAY;
01217   } else if (icch.colorSpace == str2iccSig("CMYK")) {
01218     colorspace = PDF_COLORSPACE_TYPE_CMYK;
01219   } else {
01220     WARN("Unsupported input color space.");
01221     print_iccp_header(&icch, NULL);
01222     DPXFCLOSE(fp);
01223     return -1;
01224   }
01225 
01226   stream = iccp_load_file_stream(checksum, icch.size, fp);
01227   DPXFCLOSE(fp);
01228 
01229   if (!stream) {
01230     WARN("Loading ICC Profile failed...: %s", filename);
01231     return -1;
01232   }
01233 
01234   if (memcmp(icch.ID,  nullbytes16, 16) &&
01235       memcmp(icch.ID,  checksum, 16)) {
01236     WARN("Invalid ICC profile: Inconsistent checksum.");
01237     print_iccp_header(&icch, NULL);
01238     pdf_release_obj(stream);
01239     return -1;
01240   }
01241 
01242   cdata = NEW(1, struct iccbased_cdata);
01243   init_iccbased_cdata(cdata);
01244   cdata->colorspace = colorspace;
01245   memcpy(cdata->checksum, checksum, 16);
01246 
01247   cspc_id = pdf_colorspace_findresource(ident,
01248                                    PDF_COLORSPACE_TYPE_ICCBASED, cdata);
01249   if (cspc_id >= 0) {
01250     if (verbose)
01251       MESG("(ICCP:[id=%d])", cspc_id);
01252     release_iccbased_cdata(cdata);
01253     pdf_release_obj(stream);
01254     return cspc_id;
01255   }
01256   if (verbose > 1) {
01257     print_iccp_header(&icch, checksum);
01258   }
01259 
01260   resource = pdf_new_array();
01261 
01262   pdf_add_array(resource, pdf_new_name("ICCBased"));
01263   pdf_add_array(resource, pdf_ref_obj (stream));
01264 
01265   stream_dict = pdf_stream_dict(stream);
01266   pdf_add_dict(stream_dict, pdf_new_name("N"),
01267               pdf_new_number(get_num_components_iccbased(cdata)));
01268   pdf_release_obj(stream);
01269 
01270   cspc_id = pdf_colorspace_defineresource(ident,
01271                                      PDF_COLORSPACE_TYPE_ICCBASED,
01272                                      cdata, resource);
01273 
01274   return cspc_id;
01275 }
01276 #endif
01277 
01278 typedef struct {
01279   char    *ident;
01280   int      subtype;
01281 
01282   pdf_obj *resource;
01283   pdf_obj *reference;
01284 
01285   void    *cdata;
01286 } pdf_colorspace;
01287 
01288 static struct {
01289   int  count;
01290   int  capacity;
01291   pdf_colorspace *colorspaces;
01292 } cspc_cache = {
01293   0, 0, NULL
01294 };
01295 
01296 int
01297 pdf_colorspace_findresource (const char *ident,
01298                           int type, const void *cdata)
01299 {
01300   pdf_colorspace *colorspace;
01301   int  cspc_id, cmp = -1;
01302 
01303   for (cspc_id = 0;
01304        cmp && cspc_id < cspc_cache.count; cspc_id++) {
01305     colorspace = &cspc_cache.colorspaces[cspc_id];
01306     if (colorspace->subtype != type)
01307       continue;
01308 
01309     switch (colorspace->subtype) {
01310     case PDF_COLORSPACE_TYPE_ICCBASED:
01311       cmp = compare_iccbased(ident, cdata,
01312                           colorspace->ident, colorspace->cdata);
01313       break;
01314     }
01315     if (!cmp)
01316       return cspc_id;
01317   }
01318 
01319   return -1; /* not found */
01320 }
01321 
01322 static void
01323 pdf_init_colorspace_struct (pdf_colorspace *colorspace)
01324 {
01325   ASSERT(colorspace);
01326 
01327   colorspace->ident     = NULL;
01328   colorspace->subtype   = PDF_COLORSPACE_TYPE_INVALID;
01329 
01330   colorspace->resource  = NULL;
01331   colorspace->reference = NULL;
01332   colorspace->cdata     = NULL;
01333 
01334   return;
01335 }
01336 
01337 static void
01338 pdf_clean_colorspace_struct (pdf_colorspace *colorspace)
01339 {
01340   ASSERT(colorspace);
01341 
01342   if (colorspace->ident)
01343     RELEASE(colorspace->ident);
01344   if (colorspace->resource)
01345     pdf_release_obj(colorspace->resource);
01346   if (colorspace->reference)
01347     pdf_release_obj(colorspace->reference);
01348   colorspace->resource  = NULL;
01349   colorspace->reference = NULL;
01350 
01351   if (colorspace->cdata) {
01352     switch (colorspace->subtype) {
01353     case PDF_COLORSPACE_TYPE_ICCBASED:
01354       release_iccbased_cdata(colorspace->cdata);
01355       break;
01356     }
01357   }
01358   colorspace->cdata     = NULL;
01359   colorspace->subtype   = PDF_COLORSPACE_TYPE_INVALID;
01360 
01361   return;
01362 }
01363 
01364 static void
01365 pdf_flush_colorspace (pdf_colorspace *colorspace)
01366 {
01367   ASSERT(colorspace);
01368 
01369   if (colorspace->resource)
01370     pdf_release_obj(colorspace->resource);
01371   if (colorspace->reference)
01372     pdf_release_obj(colorspace->reference);
01373 
01374   colorspace->resource  = NULL;
01375   colorspace->reference = NULL;
01376 }
01377 
01378 int
01379 pdf_colorspace_defineresource (const char *ident,
01380                             int subtype, void *cdata, pdf_obj *resource)
01381 {
01382   int  cspc_id;
01383   pdf_colorspace *colorspace;
01384 
01385   if (cspc_cache.count >= cspc_cache.capacity) {
01386     cspc_cache.capacity   += 16;
01387     cspc_cache.colorspaces = RENEW(cspc_cache.colorspaces,
01388                                cspc_cache.capacity, pdf_colorspace);
01389   }
01390   cspc_id    = cspc_cache.count;
01391   colorspace = &cspc_cache.colorspaces[cspc_id];
01392 
01393   pdf_init_colorspace_struct(colorspace);
01394   if (ident) {
01395     colorspace->ident = NEW(strlen(ident) + 1, char);
01396     strcpy(colorspace->ident, ident);
01397   }
01398   colorspace->subtype  = subtype;
01399   colorspace->cdata    = cdata;
01400   colorspace->resource = resource;
01401 
01402   if (verbose) {
01403     MESG("(ColorSpace:%s", ident);
01404     if (verbose > 1) {
01405       switch (subtype) {
01406       case PDF_COLORSPACE_TYPE_ICCBASED:
01407        MESG("[ICCBased]");
01408        break;
01409       case PDF_COLORSPACE_TYPE_CALRGB:
01410        MESG("[CalRGB]");
01411        break;
01412       case PDF_COLORSPACE_TYPE_CALGRAY:
01413        MESG("[CalGray]");
01414        break;
01415       }
01416     }
01417     MESG(")");
01418   }
01419 
01420   cspc_cache.count++;
01421 
01422   return cspc_id;
01423 }
01424 
01425 pdf_obj *
01426 pdf_get_colorspace_reference (int cspc_id)
01427 {
01428   pdf_colorspace *colorspace;
01429 
01430   colorspace = &cspc_cache.colorspaces[cspc_id];
01431   if (!colorspace->reference) {
01432     colorspace->reference = pdf_ref_obj(colorspace->resource);
01433     pdf_release_obj(colorspace->resource); /* .... */
01434     colorspace->resource = NULL;
01435   }
01436 
01437   return pdf_link_obj(colorspace->reference);
01438 }
01439 
01440 #if 0
01441 int
01442 pdf_get_colorspace_num_components (int cspc_id)
01443 {
01444   pdf_colorspace *colorspace;
01445   int  num_components;
01446 
01447   colorspace = &cspc_cache.colorspaces[cspc_id];
01448 
01449   switch (colorspace->subtype) {
01450   case PDF_COLORSPACE_TYPE_ICCBASED:
01451     num_components = get_num_components_iccbased(colorspace->cdata);
01452     break;
01453   case PDF_COLORSPACE_TYPE_DEVICEGRAY:
01454     num_components = 1;
01455     break;
01456   case PDF_COLORSPACE_TYPE_DEVICERGB:
01457     num_components = 3;
01458     break;
01459   case PDF_COLORSPACE_TYPE_DEVICECMYK:
01460     num_components = 4;
01461     break;
01462   case PDF_COLORSPACE_TYPE_CALRGB:
01463     num_components = 3;
01464     break;
01465   case PDF_COLORSPACE_TYPE_CALGRAY:
01466     num_components = 1;
01467     break;
01468   default:
01469     num_components = 0;
01470     break;
01471   }
01472 
01473   return num_components;
01474 }
01475 
01476 int
01477 pdf_get_colorspace_subtype (int cspc_id)
01478 {
01479   pdf_colorspace *colorspace;
01480 
01481   colorspace = &cspc_cache.colorspaces[cspc_id];
01482 
01483   return colorspace->subtype;
01484 }
01485 #endif
01486 
01487 void
01488 pdf_init_colors (void)
01489 {
01490   cspc_cache.count    = 0;
01491   cspc_cache.capacity = 0;
01492   cspc_cache.colorspaces = NULL;
01493 }
01494 
01495 void
01496 pdf_close_colors (void)
01497 {
01498   int  i;
01499 
01500   for (i = 0; i < cspc_cache.count; i++) {
01501     pdf_colorspace *colorspace;
01502 
01503     colorspace = &cspc_cache.colorspaces[i];
01504     pdf_flush_colorspace(colorspace);
01505     pdf_clean_colorspace_struct(colorspace);
01506   }
01507   RELEASE(cspc_cache.colorspaces);
01508   cspc_cache.colorspaces = NULL;
01509   cspc_cache.count = cspc_cache.capacity = 0;
01510 
01511 }
01512 
01513 #define PDF_COLORSPACE_FAMILY_DEVICE   0
01514 #define PDF_COLORSPACE_FAMILY_CIEBASED 1
01515 #define PDF_COLORSPACE_FAMILY_SPECIAL  2