$NetBSD: patch-ca,v 1.2 2009/02/26 20:23:02 drochner Exp $

--- poppler/GfxState.cc.orig	2008-11-05 22:49:26.000000000 +0100
+++ poppler/GfxState.cc
@@ -39,6 +39,7 @@
 #include "Page.h"
 #include "GfxState.h"
 #include "GfxFont.h"
+#include "GlobalParams.h"
 
 //------------------------------------------------------------------------
 
@@ -224,6 +225,225 @@ void GfxColorSpace::getRGBLine(Guchar *i
   }
 }
 
+#ifdef USE_CMS
+cmsHPROFILE GfxColorSpace::RGBProfile = NULL;
+cmsHPROFILE GfxColorSpace::displayProfile = NULL;
+GooString *GfxColorSpace::displayProfileName = NULL;
+unsigned int GfxColorSpace::displayPixelType = 0;
+GfxColorTransform *GfxColorSpace::XYZ2DisplayTransform = NULL;
+
+cmsHPROFILE GfxColorSpace::loadColorProfile(const char *fileName)
+{
+  cmsHPROFILE hp = NULL;
+  FILE *fp;
+
+  if (fileName[0] == '/') {
+    // full path
+    // check if open the file
+    if ((fp = fopen(fileName,"r")) != NULL) {
+      fclose(fp);
+      hp = cmsOpenProfileFromFile(fileName,"r");
+    }
+    return hp;
+  }
+  // try to load from user directory
+  GooString *path = globalParams->getBaseDir();
+  path->append(COLOR_PROFILE_DIR);
+  path->append(fileName);
+  // check if open the file
+  if ((fp = fopen(path->getCString(),"r")) != NULL) {
+    fclose(fp);
+    hp = cmsOpenProfileFromFile(path->getCString(),"r");
+  }
+  delete path;
+  if (hp == NULL) {
+    // load from global directory
+    path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
+    path->append(fileName);
+    // check if open the file
+    if ((fp = fopen(path->getCString(),"r")) != NULL) {
+      fclose(fp);
+      hp = cmsOpenProfileFromFile(path->getCString(),"r");
+    }
+    delete path;
+  }
+  return hp;
+}
+
+static int CMSError(int ecode, const char *msg)
+{
+    error(-1,const_cast<char *>(msg));
+    return 1;
+}
+
+int GfxColorSpace::setupColorProfiles()
+{
+  static GBool initialized = gFalse;
+  cmsHTRANSFORM transform;
+  unsigned int nChannels;
+
+  // do only once
+  if (initialized) return 0;
+  initialized = gTrue;
+
+  // set error handlor
+  cmsSetErrorHandler(CMSError);
+
+  if (displayProfile == NULL) {
+    // load display profile if it was not already loaded.
+    if (displayProfileName == NULL) {
+      displayProfile = loadColorProfile("display.icc");
+    } else if (displayProfileName->getLength() > 0) {
+      displayProfile = loadColorProfile(displayProfileName->getCString());
+    }
+  }
+  // load RGB profile
+  RGBProfile = loadColorProfile("RGB.icc");
+  if (RGBProfile == NULL) {
+    /* use built in sRGB profile */
+    RGBProfile = cmsCreate_sRGBProfile();
+  }
+  // create transforms
+  if (displayProfile != NULL) {
+    displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
+    nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
+    // create transform from XYZ
+    cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
+    if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
+	   displayProfile, 
+	   COLORSPACE_SH(displayPixelType) |
+	     CHANNELS_SH(nChannels) | BYTES_SH(0),
+	  INTENT_RELATIVE_COLORIMETRIC,0)) == 0) {
+      error(-1, "Can't create Lab transform");
+    } else {
+      XYZ2DisplayTransform = new GfxColorTransform(transform);
+    }
+    cmsCloseProfile(XYZProfile);
+  }
+  return 0;
+}
+
+unsigned int GfxColorSpace::getCMSColorSpaceType(icColorSpaceSignature cs)
+{
+    switch (cs) {
+    case icSigXYZData:
+      return PT_XYZ;
+      break;
+    case icSigLabData:
+      return PT_Lab;
+      break;
+    case icSigLuvData:
+      return PT_YUV;
+      break;
+    case icSigYCbCrData:
+      return PT_YCbCr;
+      break;
+    case icSigYxyData:
+      return PT_Yxy;
+      break;
+    case icSigRgbData:
+      return PT_RGB;
+      break;
+    case icSigGrayData:
+      return PT_GRAY;
+      break;
+    case icSigHsvData:
+      return PT_HSV;
+      break;
+    case icSigHlsData:
+      return PT_HLS;
+      break;
+    case icSigCmykData:
+      return PT_CMYK;
+      break;
+    case icSigCmyData:
+      return PT_CMY;
+      break;
+    case icSig2colorData:
+    case icSig3colorData:
+    case icSig4colorData:
+    case icSig5colorData:
+    case icSig6colorData:
+    case icSig7colorData:
+    case icSig8colorData:
+    case icSig9colorData:
+    case icSig10colorData:
+    case icSig11colorData:
+    case icSig12colorData:
+    case icSig13colorData:
+    case icSig14colorData:
+    case icSig15colorData:
+    default:
+      break;
+    }
+    return PT_RGB;
+}
+
+unsigned int GfxColorSpace::getCMSNChannels(icColorSpaceSignature cs)
+{
+    switch (cs) {
+    case icSigXYZData:
+    case icSigLuvData:
+    case icSigLabData:
+    case icSigYCbCrData:
+    case icSigYxyData:
+    case icSigRgbData:
+    case icSigHsvData:
+    case icSigHlsData:
+    case icSigCmyData:
+    case icSig3colorData:
+      return 3;
+      break;
+    case icSigGrayData:
+      return 1;
+      break;
+    case icSigCmykData:
+    case icSig4colorData:
+      return 4;
+      break;
+    case icSig2colorData:
+      return 2;
+      break;
+    case icSig5colorData:
+      return 5;
+      break;
+    case icSig6colorData:
+      return 6;
+      break;
+    case icSig7colorData:
+      return 7;
+      break;
+    case icSig8colorData:
+      return 8;
+      break;
+    case icSig9colorData:
+      return 9;
+      break;
+    case icSig10colorData:
+      return 10;
+      break;
+    case icSig11colorData:
+      return 11;
+      break;
+    case icSig12colorData:
+      return 12;
+      break;
+    case icSig13colorData:
+      return 13;
+      break;
+    case icSig14colorData:
+      return 14;
+      break;
+    case icSig15colorData:
+      return 15;
+    default:
+      break;
+    }
+    return 3;
+}
+
+#endif
+
 void GfxColorSpace::getGrayLine(Guchar *in, unsigned char *out, int length) {
   int i, j, n;
   GfxColor color;
@@ -311,6 +531,14 @@ GfxColorSpace *GfxCalGrayColorSpace::cop
   return cs;
 }
 
+// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
+// Language Reference, Third Edition.
+static const double xyzrgb[3][3] = {
+  {  3.240449, -1.537136, -0.498531 },
+  { -0.969265,  1.876011,  0.041556 },
+  {  0.055643, -0.204026,  1.057229 }
+};
+
 GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
   GfxCalGrayColorSpace *cs;
   Object obj1, obj2, obj3;
@@ -353,32 +581,127 @@ GfxColorSpace *GfxCalGrayColorSpace::par
   }
   obj2.free();
   obj1.free();
+
+  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
+		xyzrgb[0][1] * cs->whiteY +
+		xyzrgb[0][2] * cs->whiteZ);
+  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
+		xyzrgb[1][1] * cs->whiteY +
+		xyzrgb[1][2] * cs->whiteZ);
+  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
+		xyzrgb[2][1] * cs->whiteY +
+		xyzrgb[2][2] * cs->whiteZ);
+
   return cs;
 }
 
-void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
-  *gray = clip01(color->c[0]);
+// convert CalGray to media XYZ color space
+// (not multiply by the white point)
+void GfxCalGrayColorSpace::getXYZ(GfxColor *color, 
+  double *pX, double *pY, double *pZ) {
+  double A;
+
+  A = colToDbl(color->c[0]);
+  *pX = pow(A,gamma);
+  *pY = pow(A,gamma);
+  *pZ = pow(A,gamma);
 }
 
-void GfxCalGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
-  memcpy (out, in, length);
-}
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxRGB rgb;
 
-void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    double X, Y, Z;
+    
+    getXYZ(color,&X,&Y,&Z);
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    *gray = dblToCol(out[0]);
+    return;
+  }
+#endif
+  getRGB(color, &rgb);
+  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+				0.587 * rgb.g +
+				0.114 * rgb.b + 0.5));
 }
 
-void GfxCalGrayColorSpace::getRGBLine(Guchar *in, unsigned int *out,
-				      int length) {
-  int i;
+void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double X, Y, Z;
+  double r, g, b;
 
-  for (i = 0; i < length; i++)
-    out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0);
+  getXYZ(color,&X,&Y,&Z);
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    rgb->r = dblToCol(out[0]);
+    rgb->g = dblToCol(out[1]);
+    rgb->b = dblToCol(out[2]);
+    return;
+  }
+#endif
+  X *= whiteX;
+  Y *= whiteY;
+  Z *= whiteZ;
+  // convert XYZ to RGB, including gamut mapping and gamma correction
+  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
+  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
+  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
+  rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
+  rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
+  rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
+  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
 }
 
 void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
-  cmyk->c = cmyk->m = cmyk->y = 0;
-  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+  GfxRGB rgb;
+  GfxColorComp c, m, y, k;
+
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    double X, Y, Z;
+    
+    getXYZ(color,&X,&Y,&Z);
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    cmyk->c = dblToCol(out[0]);
+    cmyk->m = dblToCol(out[1]);
+    cmyk->y = dblToCol(out[2]);
+    cmyk->k = dblToCol(out[3]);
+    return;
+  }
+#endif
+  getRGB(color, &rgb);
+  c = clip01(gfxColorComp1 - rgb.r);
+  m = clip01(gfxColorComp1 - rgb.g);
+  y = clip01(gfxColorComp1 - rgb.b);
+  k = c;
+  if (m < k) {
+    k = m;
+  }
+  if (y < k) {
+    k = y;
+  }
+  cmyk->c = c - k;
+  cmyk->m = m - k;
+  cmyk->y = y - k;
+  cmyk->k = k;
 }
 
 void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
@@ -553,47 +876,112 @@ GfxColorSpace *GfxCalRGBColorSpace::pars
   }
   obj2.free();
   obj1.free();
+
+  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
+		xyzrgb[0][1] * cs->whiteY +
+		xyzrgb[0][2] * cs->whiteZ);
+  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
+		xyzrgb[1][1] * cs->whiteY +
+		xyzrgb[1][2] * cs->whiteZ);
+  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
+		xyzrgb[2][1] * cs->whiteY +
+		xyzrgb[2][2] * cs->whiteZ);
+
   return cs;
 }
 
-void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
-  *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
-		 0.587 * color->c[1] +
-		 0.114 * color->c[2] + 0.5));
+// convert CalRGB to XYZ color space
+void GfxCalRGBColorSpace::getXYZ(GfxColor *color, 
+  double *pX, double *pY, double *pZ) {
+  double A, B, C;
+
+  A = colToDbl(color->c[0]);
+  B = colToDbl(color->c[1]);
+  C = colToDbl(color->c[2]);
+  *pX = mat[0]*pow(A,gammaR)+mat[3]*pow(B,gammaG)+mat[6]*pow(C,gammaB);
+  *pY = mat[1]*pow(A,gammaR)+mat[4]*pow(B,gammaG)+mat[7]*pow(C,gammaB);
+  *pZ = mat[2]*pow(A,gammaR)+mat[5]*pow(B,gammaG)+mat[8]*pow(C,gammaB);
 }
 
-void GfxCalRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
-  int i;
+void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxRGB rgb;
 
-  for (i = 0; i < length; i++) {
-    out[i] = 
-      (in[i * 3 + 0] * 19595 + 
-       in[i * 3 + 1] * 38469 + 
-       in[i * 3 + 2] * 7472) / 65536;
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    double X, Y, Z;
+    
+    getXYZ(color,&X,&Y,&Z);
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    *gray = dblToCol(out[0]);
+    return;
   }
+#endif
+  getRGB(color, &rgb);
+  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+				0.587 * rgb.g +
+				0.114 * rgb.b + 0.5));
 }
 
 void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  rgb->r = clip01(color->c[0]);
-  rgb->g = clip01(color->c[1]);
-  rgb->b = clip01(color->c[2]);
-}
-
-void GfxCalRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out,
-				     int length) {
-  Guchar *p;
-  int i;
+  double X, Y, Z;
+  double r, g, b;
 
-  for (i = 0, p = in; i < length; i++, p += 3)
-    out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0);
+  getXYZ(color,&X,&Y,&Z);
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    
+    in[0] = clip01(X/whiteX);
+    in[1] = clip01(Y/whiteY);
+    in[2] = clip01(Z/whiteZ);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    rgb->r = dblToCol(out[0]);
+    rgb->g = dblToCol(out[1]);
+    rgb->b = dblToCol(out[2]);
+    return;
+  }
+#endif
+  // convert XYZ to RGB, including gamut mapping and gamma correction
+  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
+  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
+  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
+  rgb->r = dblToCol(pow(clip01(r), 0.5));
+  rgb->g = dblToCol(pow(clip01(g), 0.5));
+  rgb->b = dblToCol(pow(clip01(b), 0.5));
 }
 
 void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  GfxRGB rgb;
   GfxColorComp c, m, y, k;
 
-  c = clip01(gfxColorComp1 - color->c[0]);
-  m = clip01(gfxColorComp1 - color->c[1]);
-  y = clip01(gfxColorComp1 - color->c[2]);
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    double X, Y, Z;
+    
+    getXYZ(color,&X,&Y,&Z);
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    cmyk->c = dblToCol(out[0]);
+    cmyk->m = dblToCol(out[1]);
+    cmyk->y = dblToCol(out[2]);
+    cmyk->k = dblToCol(out[3]);
+    return;
+  }
+#endif
+  getRGB(color, &rgb);
+  c = clip01(gfxColorComp1 - rgb.r);
+  m = clip01(gfxColorComp1 - rgb.g);
+  y = clip01(gfxColorComp1 - rgb.b);
   k = c;
   if (m < k) {
     k = m;
@@ -714,14 +1102,6 @@ void GfxDeviceCMYKColorSpace::getDefault
 // GfxLabColorSpace
 //------------------------------------------------------------------------
 
-// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
-// Language Reference, Third Edition.
-static const double xyzrgb[3][3] = {
-  {  3.240449, -1.537136, -0.498531 },
-  { -0.969265,  1.876011,  0.041556 },
-  {  0.055643, -0.204026,  1.057229 }
-};
-
 GfxLabColorSpace::GfxLabColorSpace() {
   whiteX = whiteY = whiteZ = 1;
   blackX = blackY = blackZ = 0;
@@ -823,18 +1203,30 @@ GfxColorSpace *GfxLabColorSpace::parse(A
 void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   GfxRGB rgb;
 
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    
+    getXYZ(color, &in[0], &in[1], &in[2]);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    *gray = dblToCol(out[0]);
+    return;
+  }
+#endif
   getRGB(color, &rgb);
   *gray = clip01((GfxColorComp)(0.299 * rgb.r +
 				0.587 * rgb.g +
 				0.114 * rgb.b + 0.5));
 }
 
-void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+// convert L*a*b* to media XYZ color space
+// (not multiply by the white point)
+void GfxLabColorSpace::getXYZ(GfxColor *color, 
+  double *pX, double *pY, double *pZ) {
   double X, Y, Z;
   double t1, t2;
-  double r, g, b;
 
-  // convert L*a*b* to CIE 1931 XYZ color space
   t1 = (colToDbl(color->c[0]) + 16) / 116;
   t2 = t1 + colToDbl(color->c[1]) / 500;
   if (t2 >= (6.0 / 29.0)) {
@@ -842,21 +1234,45 @@ void GfxLabColorSpace::getRGB(GfxColor *
   } else {
     X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
   }
-  X *= whiteX;
   if (t1 >= (6.0 / 29.0)) {
     Y = t1 * t1 * t1;
   } else {
     Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
   }
-  Y *= whiteY;
   t2 = t1 - colToDbl(color->c[2]) / 200;
   if (t2 >= (6.0 / 29.0)) {
     Z = t2 * t2 * t2;
   } else {
     Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
   }
-  Z *= whiteZ;
+  *pX = X;
+  *pY = Y;
+  *pZ = Z;
+}
+
+void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double X, Y, Z;
+  double r, g, b;
 
+  getXYZ(color, &X, &Y, &Z);
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
+    double out[gfxColorMaxComps];
+    double in[gfxColorMaxComps];
+    
+    in[0] = clip01(X);
+    in[1] = clip01(Y);
+    in[2] = clip01(Z);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    rgb->r = dblToCol(out[0]);
+    rgb->g = dblToCol(out[1]);
+    rgb->b = dblToCol(out[2]);
+    return;
+  }
+#endif
+  X *= whiteX;
+  Y *= whiteY;
+  Z *= whiteZ;
   // convert XYZ to RGB, including gamut mapping and gamma correction
   r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
   g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
@@ -870,6 +1286,20 @@ void GfxLabColorSpace::getCMYK(GfxColor 
   GfxRGB rgb;
   GfxColorComp c, m, y, k;
 
+#ifdef USE_CMS
+  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    
+    getXYZ(color, &in[0], &in[1], &in[2]);
+    XYZ2DisplayTransform->doTransform(in,out,1);
+    cmyk->c = dblToCol(out[0]);
+    cmyk->m = dblToCol(out[1]);
+    cmyk->y = dblToCol(out[2]);
+    cmyk->k = dblToCol(out[3]);
+    return;
+  }
+#endif
   getRGB(color, &rgb);
   c = clip01(gfxColorComp1 - rgb.r);
   m = clip01(gfxColorComp1 - rgb.g);
@@ -926,10 +1356,22 @@ GfxICCBasedColorSpace::GfxICCBasedColorS
   iccProfileStream = *iccProfileStreamA;
   rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
   rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
+#ifdef USE_CMS
+  transform = NULL;
+  lineTransform = NULL;
+#endif
 }
 
 GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
   delete alt;
+#ifdef USE_CMS
+  if (transform != NULL) {
+    if (transform->unref() == 0) delete transform;
+  }
+  if (lineTransform != NULL) {
+    if (lineTransform->unref() == 0) delete lineTransform;
+  }
+#endif
 }
 
 GfxColorSpace *GfxICCBasedColorSpace::copy() {
@@ -941,6 +1383,12 @@ GfxColorSpace *GfxICCBasedColorSpace::co
     cs->rangeMin[i] = rangeMin[i];
     cs->rangeMax[i] = rangeMax[i];
   }
+#ifdef USE_CMS
+  cs->transform = transform;
+  if (transform != NULL) transform->ref();
+  cs->lineTransform = lineTransform;
+  if (lineTransform != NULL) lineTransform->ref();
+#endif
   return cs;
 }
 
@@ -961,6 +1409,12 @@ GfxColorSpace *GfxICCBasedColorSpace::pa
     iccProfileStreamA.gen = 0;
   }
   obj1.free();
+#ifdef USE_CMS
+  // check cache
+  if (iccProfileStreamA.num > 0
+     && (cs = GfxICCBasedCache::lookup(iccProfileStreamA.num,
+          iccProfileStreamA.gen)) != NULL) return cs;
+#endif
   arr->get(1, &obj1);
   if (!obj1.isStream()) {
     error(-1, "Bad ICCBased color space (stream)");
@@ -1015,24 +1469,167 @@ GfxColorSpace *GfxICCBasedColorSpace::pa
   }
   obj2.free();
   obj1.free();
+
+#ifdef USE_CMS
+  arr->get(1, &obj1);
+  dict = obj1.streamGetDict();
+  Guchar *profBuf;
+  unsigned int bufSize;
+  Stream *iccStream = obj1.getStream();
+  int c;
+  unsigned int size = 0;
+
+  bufSize = 65536;
+  profBuf = (Guchar *)gmallocn(bufSize,1);
+  iccStream->reset();
+  while ((c = iccStream->getChar()) != EOF) {
+    if (bufSize <= size) {
+      bufSize += 65536;
+      profBuf = (Guchar *)greallocn(profBuf,bufSize,1);
+    }
+    profBuf[size++] = c;
+  }
+  cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,size);
+  gfree(profBuf);
+  if (hp == 0) {
+    error(-1, "read ICCBased color space profile error");
+  } else {
+    cmsHPROFILE dhp = displayProfile;
+    if (dhp == NULL) dhp = RGBProfile;
+    unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(hp));
+    unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp));
+    unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp));
+    cmsHTRANSFORM transform;
+    if ((transform = cmsCreateTransform(hp,
+	   COLORSPACE_SH(cst) |CHANNELS_SH(nCompsA) | BYTES_SH(0),
+	   dhp,
+	   COLORSPACE_SH(dcst) |
+	     CHANNELS_SH(dNChannels) | BYTES_SH(0),
+	  INTENT_RELATIVE_COLORIMETRIC,0)) == 0) {
+      error(-1, "Can't create transform");
+    }
+    cs->transform = new GfxColorTransform(transform);
+    if (dcst == PT_RGB) {
+       // create line transform only when the display is RGB type color space 
+      if ((transform = cmsCreateTransform(hp,
+	    CHANNELS_SH(nCompsA) | BYTES_SH(1),dhp,
+	    TYPE_RGB_8,INTENT_RELATIVE_COLORIMETRIC,0)) == 0) {
+	error(-1, "Can't create transform");
+      }
+      cs->lineTransform = new GfxColorTransform(transform);
+    }
+    cmsCloseProfile(hp);
+  }
+  obj1.free();
+  // put this colorSpace into cache
+  if (iccProfileStreamA.num > 0) {
+    GfxICCBasedCache::put(iccProfileStreamA.num,iccProfileStreamA.gen,cs);
+  }
+#endif
   return cs;
 }
 
 void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+#ifdef USE_CMS
+  if (transform != 0 && displayPixelType == PT_GRAY) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    
+    for (int i = 0;i < nComps;i++) {
+	in[i] = colToDbl(color->c[i]);
+    }
+    transform->doTransform(in,out,1);
+    *gray = dblToCol(out[0]);
+  } else {
+    GfxRGB rgb;
+    getRGB(color,&rgb);
+    *gray = clip01((GfxColorComp)(0.3 * rgb.r +
+		   0.59 * rgb.g +
+		   0.11 * rgb.b + 0.5));
+  }
+#else
   alt->getGray(color, gray);
+#endif
 }
 
 void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+#ifdef USE_CMS
+  if (transform != 0
+       && (displayProfile == NULL || displayPixelType == PT_RGB)) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    
+    for (int i = 0;i < nComps;i++) {
+	in[i] = colToDbl(color->c[i]);
+    }
+    transform->doTransform(in,out,1);
+    rgb->r = dblToCol(out[0]);
+    rgb->g = dblToCol(out[1]);
+    rgb->b = dblToCol(out[2]);
+  } else {
+    alt->getRGB(color, rgb);
+  }
+#else
   alt->getRGB(color, rgb);
+#endif
 }
 
 void GfxICCBasedColorSpace::getRGBLine(Guchar *in, unsigned int *out,
 				       int length) {
+#ifdef USE_CMS
+  if (lineTransform != 0) {
+    for (int i = 0;i < length;i++) {
+	Guchar tmp[gfxColorMaxComps];
+
+	lineTransform->doTransform(in,tmp,1);
+	in += nComps;
+	out[i] = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2];
+    }
+  } else {
+    alt->getRGBLine(in, out, length);
+  }
+#else
   alt->getRGBLine(in, out, length);
+#endif
 }
 
 void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+#ifdef USE_CMS
+  if (transform != NULL && displayPixelType == PT_CMYK) {
+    double in[gfxColorMaxComps];
+    double out[gfxColorMaxComps];
+    
+    for (int i = 0;i < nComps;i++) {
+	in[i] = colToDbl(color->c[i]);
+    }
+    transform->doTransform(in,out,1);
+    cmyk->c = dblToCol(out[0]);
+    cmyk->m = dblToCol(out[1]);
+    cmyk->y = dblToCol(out[2]);
+    cmyk->k = dblToCol(out[3]);
+  } else {
+    GfxRGB rgb;
+    GfxColorComp c, m, y, k;
+
+    getRGB(color,&rgb);
+    c = clip01(gfxColorComp1 - rgb.r);
+    m = clip01(gfxColorComp1 - rgb.g);
+    y = clip01(gfxColorComp1 - rgb.b);
+    k = c;
+    if (m < k) {
+      k = m;
+    }
+    if (y < k) {
+      k = y;
+    }
+    cmyk->c = c - k;
+    cmyk->m = m - k;
+    cmyk->y = y - k;
+    cmyk->k = k;
+  }
+#else
   alt->getCMYK(color, cmyk);
+#endif
 }
 
 void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
@@ -1066,6 +1663,56 @@ void GfxICCBasedColorSpace::getDefaultRa
 #endif
 }
 
+#ifdef USE_CMS
+GfxICCBasedCache
+   GfxICCBasedCache::cache[GFX_ICCBASED_CACHE_SIZE];
+
+GfxICCBasedCache::GfxICCBasedCache()
+{
+  num = 0;
+  gen = 0;
+  colorSpace = 0;
+}
+
+GfxICCBasedColorSpace *GfxICCBasedCache::lookup(int numA, int genA)
+{
+  int i;
+
+  if (cache[0].num == numA && cache[0].gen == genA) {
+    return (GfxICCBasedColorSpace *)cache[0].colorSpace->copy();
+  }
+  for (i = 1;i < GFX_ICCBASED_CACHE_SIZE && cache[i].num > 0;i++) {
+    if (cache[i].num == numA && cache[i].gen == genA) {
+      int j;
+      GfxICCBasedCache hit = cache[i];
+
+      for (j = i;j > 0;j--) {
+       if (cache[j - 1].num > 0) cache[j] = cache[j-1];
+      }
+      cache[0] = hit;
+      return (GfxICCBasedColorSpace *)hit.colorSpace->copy();
+    }
+  }
+  return NULL;
+}
+
+void GfxICCBasedCache::put(int numA, int genA,
+  GfxICCBasedColorSpace *cs)
+{
+  int i;
+
+  if (cache[GFX_ICCBASED_CACHE_SIZE-1].num > 0) {
+    delete cache[GFX_ICCBASED_CACHE_SIZE-1].colorSpace;
+  }
+  for (i = GFX_ICCBASED_CACHE_SIZE-1; i > 0; i--) {
+    if (cache[i - 1].num > 0) cache[i] = cache[i - 1];
+  }
+  cache[0].num = numA;
+  cache[0].gen = genA;
+  cache[0].colorSpace = (GfxICCBasedColorSpace *)cs->copy();
+}
+#endif
+
 //------------------------------------------------------------------------
 // GfxIndexedColorSpace
 //------------------------------------------------------------------------
@@ -3938,6 +4585,9 @@ GfxState::GfxState(double hDPIA, double 
   clipYMax = pageHeight;
 
   saved = NULL;
+#ifdef USE_CMS
+  GfxColorSpace::setupColorProfiles();
+#endif
 }
 
 GfxState::~GfxState() {
