[poppler] Color Management

Koji Otani sho at bbr.jp
Thu Dec 11 21:26:27 PST 2008


Very sorry, I attached a wrong file to the previous mail.
I attached a correct file to this mail.
---------
Koji Otani.

From: Koji Otani <sho at bbr.jp>
Subject: Re: [poppler] Color Management
Date: Fri, 12 Dec 2008 14:16:40 +0900 (JST)
Message-ID: <20081212.141640.112579191.sho at bbr.jp>

sho> I'm Koji Otani.
sho> 
sho> I found a mistake in my patch.
sho> (one #ifdef --> #if)
sho> So, in case not using CMS, compiling error occurs.
sho> 
sho> I attached fixed patch.
sho> 
sho> 
sho> 
sho> 
sho> 
sho> From: Koji Otani <sho at bbr.jp>
sho> Subject: Re: [poppler] Color Management
sho> Date: Thu, 27 Nov 2008 17:57:37 +0900 (JST)
sho> Message-ID: <20081127.175737.52202455.sho at bbr.jp>
sho> 
sho> sho> Hi, All
sho> sho> 
sho> sho> I made a patch to add Color Management to poppler.
sho> sho> I attached it to this mail.
sho> sho> This patch is one for current source in the GIT repository.
sho> sho> 
sho> sho> I'm not a CM expert. And this patch doesn't resolve all problems.
sho> sho> But, I hope to make some progress.
sho> sho> Please try this.
sho> sho> ------------
sho> sho> Koji Otani.
sho> sho> 
sho> sho> --------------
sho> sho> This patch adds Color Management feature to polper:
sho> sho> (1) Handling ICCBasedColorSpace (both V2 and V4 profiles)
sho> sho> (2) Handling CalRGB and CalGray
sho> sho> 
sho> sho> This doesn't handle Rendering Intent.
sho> sho>     This always renders with relative colorimetric intent (default in PDF).
sho> sho> This doesn't handle transparency.
sho> sho> 
sho> sho> This uses Little cms library, so when compiling this, lcms-devel package is
sho> sho>  needed. if lcms-devel package is not installed, disable this feature.
sho> sho> 
sho> sho> In default, ICCBased uses builin Standard RGB,
sho> sho>    Lab, CalRGB and CalGray don't use profile.
sho> sho> If you want to use other profile, put profile file to 
sho> sho>   $HOME/.xpdf/ColorProfiles/display.icc
sho> sho>     or
sho> sho>   /usr/share/poppler/ColorProfiles/display.icc
sho> sho> This profile is used by ICCBased, Lab, CalRGB, CalGray.
sho> sho> You can also change display profile from your application.
sho> sho>   Before calling PDFDoc::displayPage, call
sho> sho>    GfxColorSpace::setDisplayProfileName
sho> sho>     or
sho> sho>    GfxColorSpace::setDisplayProfile
sho> sho> 
sho> sho> static void setDisplayProfile(cmsHPROFILE displayProfileA)
sho> sho>     set profile handle
sho> sho> static void setDisplayProfileName(GooString *name)
sho> sho>     set profile file name
sho> sho>       If file name started with '/', it is handled as absolute path name.
sho> sho>       Otherwise, it is handled as relative path to
sho> sho>          $HOME/.xpdf/ColorProfiles/
sho> sho> 	 and 
sho> sho>          /usr/share/poppler/ColorProfiles/
sho> sho> -----------
sho> sho> 
sho> sho> 
sho> sho> From: "Hal V. Engel" <hvengel at astound.net>
sho> sho> Subject: Re: [poppler] Color Management
sho> sho> Date: Thu, 21 Aug 2008 18:51:59 -0700
sho> sho> Message-ID: <200808211852.00098.hvengel at astound.net>
sho> sho> 
sho> sho> hvengel> On Thursday 07 August 2008 06:58:13 pm Leonard Rosenthol wrote:
sho> sho> hvengel> > Correct.  You'd need to make MAJOR changes to the rendering
sho> sho> hvengel> > architecture of Poppler to do proper color management - especially
sho> sho> hvengel> > where transparency is involved.
sho> sho> hvengel> >
sho> sho> hvengel> > But it would be a very good thing...
sho> sho> hvengel> >
sho> sho> hvengel> > Leonard
sho> sho> hvengel> 
sho> sho> hvengel> (This is kind of long please bear with me).
sho> sho> hvengel> 
sho> sho> hvengel> I have been looking at the PDF spec. and at the poppler code.  Leonard is 
sho> sho> hvengel> correct that the code in it's present form is not designed to support color 
sho> sho> hvengel> management and it will require major changes.
sho> sho> hvengel> 
sho> sho> hvengel> Some exmples:
sho> sho> hvengel> 
sho> sho> hvengel> The PDF spec calls for all CIE based colorspaces (Lab, calRGB, calCMYK and 
sho> sho> hvengel> ICCBased) to undergo the following transformations:
sho> sho> hvengel> 
sho> sho> hvengel> original color space -> XYZ -> output color space
sho> sho> hvengel> 
sho> sho> hvengel> This implies that there is clear separation between the first conversion and 
sho> sho> hvengel> the second conversion and that one set of routines can handle the second 
sho> sho> hvengel> conversion for all CIE based color transforms.   
sho> sho> hvengel> 
sho> sho> hvengel> It also implies that there is some way for calling applications to specifiy a 
sho> sho> hvengel> CIE based output color space such as an ICC profile and related information 
sho> sho> hvengel> (rendering intents, black point comp. and output channel depth).  This is 
sho> sho> hvengel> currently not possible.
sho> sho> hvengel> 
sho> sho> hvengel> The code as it exists only does a direct conversion to XYZ in one location 
sho> sho> hvengel> GfxLabColorSpace::getRGB() and then does a second conversion to an arbitrary 
sho> sho> hvengel> RGB** color space in the same function.  None of the other CIE based color 
sho> sho> hvengel> space conversions produce an intermeadiate XYZ conversion and of course there 
sho> sho> hvengel> are no generic XYZ to output color space routines.   
sho> sho> hvengel> 
sho> sho> hvengel> The gray and cmyk code for Lab conversions uses the getRGB() function and then 
sho> sho> hvengel> does some kind of generic conversion using the RGB values.  Since these RGB 
sho> sho> hvengel> values are in an arbitrary RGB** color space the conversion to gray and CMYK 
sho> sho> hvengel> is also arbitrary.  
sho> sho> hvengel> 
sho> sho> hvengel> CalGray does not do gamma compensation in getGray() as called for in the PDF 
sho> sho> hvengel> spec.  In addition the CalGray functions only support 8 bit depth.  What 
sho> sho> hvengel> happens if there is a 16bit/channel gray image in a PDF file or if a user 
sho> sho> hvengel> wants 16 bit/channel output for his/her printer?
sho> sho> hvengel> 
sho> sho> hvengel> CalRGB passes RGB values directly through to the output and makes no attempt 
sho> sho> hvengel> to convert these into an intermeadiate XYZ color space or into the actual 
sho> sho> hvengel> output colorspace.  It also does not apply a gamma correction to the data as 
sho> sho> hvengel> per the PDF spec.
sho> sho> hvengel> 
sho> sho> hvengel> End examples:
sho> sho> hvengel> 
sho> sho> hvengel> I don't think that I am pointing out anything new to most on this list.  When 
sho> sho> hvengel> I first started to look at the code I was hoping that it would not be too 
sho> sho> hvengel> difficult to find the places where CM hooks could be put in place and that 
sho> sho> hvengel> perhaps I could spend a few days putting together a set of patches that would 
sho> sho> hvengel> provide a starting place.  But it appears that the code needs significant 
sho> sho> hvengel> restructuring in order to even start doing the actaul color management 
sho> sho> hvengel> specific work.  Fortunately the PDF specification has enough detail that it 
sho> sho> hvengel> should be possible for the poppler team to do much of the restructuring work 
sho> sho> hvengel> without too much involvement from someone with color management expertise.   
sho> sho> hvengel> 
sho> sho> hvengel> Mostly what is needed is:
sho> sho> hvengel> 
sho> sho> hvengel> 1. An API for applications to specify the output color space, rendering 
sho> sho> hvengel> intent, black point compensation and channel depth for a document.  These are 
sho> sho> hvengel> needed to correctly setup the XYZ to output color space transform.
sho> sho> hvengel> 
sho> sho> hvengel> 2. The CIE related code (calRGB, calGray, Lab and ICCBased) needs to be 
sho> sho> hvengel> restructured so that it has a clean division between producing the 
sho> sho> hvengel> intermeadeate XYZ values and the code that does the XYZ to output color space 
sho> sho> hvengel> conversion.  See the diagram on pages 238 and 239 of the version 1.7 PDF 
sho> sho> hvengel> Reference.
sho> sho> hvengel> 
sho> sho> hvengel> Initially the ICCBased code could be left alone and calRGB, calGray and Lab 
sho> sho> hvengel> would be setup to convert to XYZ but would still do the current simplistic 
sho> sho> hvengel> conversions to the output color space (IE. these would all look some what like 
sho> sho> hvengel> GfxLabColorSpace::getRGB() & friends) .  
sho> sho> hvengel> 
sho> sho> hvengel> Then the XYZ to output color space routines would be added and the calRGB, 
sho> sho> hvengel> calGray and Lab routines would call them to handle the output conversion in a 
sho> sho> hvengel> more correct way.  It is at this point where the code would start making use 
sho> sho> hvengel> of a CMS like LCMS as well as the API that was created in #1.  This 
sho> sho> hvengel> functionality is documented in the diagram on page 239 of the PDF version 1.7 
sho> sho> hvengel> spec as "Conversion from CIE-based to device color space (not specified by 
sho> sho> hvengel> PDF)".
sho> sho> hvengel> 
sho> sho> hvengel> 3. At this point setting up the ICCBased routines to create XYZ intermeadiate 
sho> sho> hvengel> results would be added.  Once this is in place poppler would have a 
sho> sho> hvengel> functioning color managed system for all of the CIE based color space types.
sho> sho> hvengel> 
sho> sho> hvengel> I have not looked at the PDF specs for "Special color spaces" such as 
sho> sho> hvengel> Separation, DeviceN, Indexed and Pattern so I do not know what implications 
sho> sho> hvengel> there are for these in a color managed system.  I have also not looked at 
sho> sho> hvengel> transparancy issues but I didn't see much in the spec that related to ICCBased 
sho> sho> hvengel> objects other than that it was nessassary to have to AtoB and BtoA tables in 
sho> sho> hvengel> the profiles to support this and that all blending must be either in device 
sho> sho> hvengel> space or in CIE space and that the spec advises that CIE is prefered.
sho> sho> hvengel> 
sho> sho> hvengel> Many of you are probably asking "Why should we give a rats behind about this?"   
sho> sho> hvengel> The reason I am looking at this is that the printing community is in the 
sho> sho> hvengel> process of converting from PostScript to PDF as the standard document type for 
sho> sho> hvengel> printing for *nix systems.  Because of this they are in the process of writing 
sho> sho> hvengel> a new pdftoraster filter for CUPS as well as putting together a PDF based 
sho> sho> hvengel> "Common Printing Dialog" (this is a Google Summer of Code project).  I asked 
sho> sho> hvengel> on the printing email lists about color management in the new printing 
sho> sho> hvengel> workflow.  Specifcally did the new pdftoraster filter have CM support?  I 
sho> sho> hvengel> didn't get an answer and so I found the code and had a look.  Guess what (most 
sho> sho> hvengel> of you probably know this) it is using poppler and as a result it does not 
sho> sho> hvengel> support color management.  
sho> sho> hvengel> 
sho> sho> hvengel> Printing is an important piece of the infastructure in general and 
sho> sho> hvengel> particularly for anyone doing color critical work.  It is currently badly 
sho> sho> hvengel> broken with respect to color managemnt and it appears that until poppler has 
sho> sho> hvengel> CM support or another similar library with CM support becomes available it 
sho> sho> hvengel> will remain broken.  So now you know what the story is and why this is 
sho> sho> hvengel> important.
sho> sho> hvengel> 
sho> sho> hvengel> I have color management expertise (I am the maintainer of LProf) and I have 
sho> sho> hvengel> connections to the open source color management community where there are many 
sho> sho> hvengel> others with CM expertise some of them with way more expertise than I have.  As 
sho> sho> hvengel> a community we have been working with the printing community, Xorg and the 
sho> sho> hvengel> DE's trying to get the missing CM components in place.  
sho> sho> hvengel> 
sho> sho> hvengel> There has been major progress with color management on monitors and we now 
sho> sho> hvengel> have calibration and profiling software for these devices as well as support 
sho> sho> hvengel> in XOrg (with more on the way) and work is underway to extend this to include 
sho> sho> hvengel> better user tools in KDE and GNOME.  We also have good open source tools for 
sho> sho> hvengel> profiling input devices like cameras and scanners and these profiles are now 
sho> sho> hvengel> well supported by things like XSane, UFRAW and other software in this area.  
sho> sho> hvengel> 
sho> sho> hvengel> We also have high quality profiling software for output devices like printers.     
sho> sho> hvengel> One of the major things that is missing is a viable color managed path for 
sho> sho> hvengel> printing.  That is I can create very high quality profiles for my printers but 
sho> sho> hvengel> using them with the existing printing tools is at best difficult even for 
sho> sho> hvengel> someone who really understands CM and nearly imposible for a normal user.   If 
sho> sho> hvengel> the CUPS pdftoraster filter had CM support much of the printing part of this 
sho> sho> hvengel> would start falling in place and would become accessable to a much wider 
sho> sho> hvengel> audiance.  So from the point of view of the CM community poppler is now a very 
sho> sho> hvengel> important piece of software.   
sho> sho> hvengel> 
sho> sho> hvengel> Hal
sho> sho> hvengel> 
sho> sho> hvengel> PS For those interested this web page from the ICC has a link to a PDF file 
sho> sho> hvengel> from Adobe that demonstrates CM or a lack of CM as the case may be:
sho> sho> hvengel> 
sho> sho> hvengel>   http://www.color.org/version4html.xalter
sho> sho> hvengel> 
sho> sho> hvengel> 
sho> sho> hvengel> **It looks like it might end up being sort of sRGB since it uses a matrix 
sho> sho> hvengel> conversion and then a gamma conversion that is too simple to give actual sRGB 
sho> sho> hvengel> results since sRGB has a compound gamma curve.
sho> sho> hvengel> 
-------------- next part --------------
diff --git a/configure.ac b/configure.ac
index 5fa5832..0ee2813 100644
--- a/configure.ac
+++ b/configure.ac
@@ -425,6 +425,24 @@ AC_ARG_ENABLE(compile-warnings,
                              [Turn on compiler warnings.]),,
               [enable_compile_warnings="yes"])
 
+dnl
+dnl   Color Management
+dnl
+
+AC_ARG_ENABLE(cms,
+	      AC_HELP_STRING([--disable-cms],
+	                     [Don't use color management system.]),
+              enable_cms=$enableval,
+              enable_cms="yes")
+if test x$enable_cms = xyes; then
+  AC_CHECK_LIB([lcms],cmsOpenProfileFromFile,,
+	       AC_MSG_ERROR("*** lcms library not found ***"))
+  AC_CHECK_HEADERS([lcms.h],,
+		   AC_MSG_ERROR("*** lcms headers not found ***"))
+  AC_DEFINE(USE_CMS, 1, [Defines if use cms])
+fi
+AM_CONDITIONAL(USE_CMS, test x$enable_cms = xyes)
+
 if test "x$GCC" != xyes; then
   enable_compile_warnings=no
 fi
@@ -489,6 +507,7 @@ echo "  use gtk-doc:        $enable_gtk_doc"
 echo "  use libjpeg:        $enable_libjpeg"
 echo "  use zlib:           $enable_zlib"
 echo "  use libopenjpeg:    $enable_libopenjpeg"
+echo "  use cms:            $enable_cms"
 echo "  command line utils: $enable_utils"
 echo ""
 
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 7bb0667..5948061 100644
--- a/poppler/GfxState.cc
+++ b/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 *in, unsigned int *out, int length) {
   }
 }
 
+#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::copy() {
   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::parse(Array *arr) {
   }
   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;
 
-void GfxCalGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
-  memcpy (out, in, length);
+  A = colToDbl(color->c[0]);
+  *pX = pow(A,gamma);
+  *pY = pow(A,gamma);
+  *pZ = pow(A,gamma);
 }
 
-void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxRGB rgb;
+
+#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::parse(Array *arr) {
   }
   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::getDefaultColor(GfxColor *color) {
 // 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(Array *arr) {
 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 *color, GfxRGB *rgb) {
   } 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 *color, GfxCMYK *cmyk) {
   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::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
   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::copy() {
     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;
 }
 
@@ -1015,24 +1463,163 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
   }
   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();
+#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 += 3;
+	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) {
@@ -3938,6 +4525,9 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
   clipYMax = pageHeight;
 
   saved = NULL;
+#ifdef USE_CMS
+  GfxColorSpace::setupColorProfiles();
+#endif
 }
 
 GfxState::~GfxState() {
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index ac20d35..1afc889 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -32,6 +32,9 @@
 #include "goo/gtypes.h"
 #include "Object.h"
 #include "Function.h"
+#ifdef USE_CMS
+#include "lcms.h"
+#endif
 
 class Array;
 class GfxFont;
@@ -151,6 +154,37 @@ enum GfxColorSpaceMode {
   csPattern
 };
 
+#ifdef USE_CMS
+
+#define COLOR_PROFILE_DIR "/ColorProfiles/"
+#define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR
+
+// wrapper of cmsHTRANSFORM to copy
+class GfxColorTransform {
+public:
+  void doTransform(void *in, void *out, unsigned int size) {
+    cmsDoTransform(transform, in, out, size);
+  }
+  GfxColorTransform(cmsHTRANSFORM transformA) {
+    transform = transformA;
+    refCount = 1;
+  }
+  ~GfxColorTransform() {
+    cmsDeleteTransform(transform);
+  }
+  void ref() {
+    refCount++;
+  }
+  unsigned int unref() {
+    return --refCount;
+  }
+private:
+  GfxColorTransform() {}
+  cmsHTRANSFORM transform;
+  unsigned int refCount;
+};
+#endif
+
 class GfxColorSpace {
 public:
 
@@ -191,6 +225,32 @@ public:
   static char *getColorSpaceModeName(int idx);
 
 private:
+#ifdef USE_CMS
+protected:
+  static cmsHPROFILE RGBProfile;
+  static GooString *displayProfileName; // display profile file Name
+  static cmsHPROFILE displayProfile; // display profile
+  static unsigned int displayPixelType;
+  static GfxColorTransform *XYZ2DisplayTransform;
+  // convert color space signature to cmsColor type 
+  static unsigned int getCMSColorSpaceType(icColorSpaceSignature cs);
+  static unsigned int getCMSNChannels(icColorSpaceSignature cs);
+  static cmsHPROFILE loadColorProfile(const char *fileName);
+public:
+  static int setupColorProfiles();
+  static void setDisplayProfile(cmsHPROFILE displayProfileA) {
+    displayProfile = displayProfileA;
+  }
+  static void setDisplayProfileName(GooString *name) {
+    displayProfileName = name->copy();
+  }
+  static cmsHPROFILE getRGBProfile() {
+    return RGBProfile;
+  }
+  static cmsHPROFILE getDisplayProfile() {
+    return displayProfile;
+  }
+#endif
 };
 
 //------------------------------------------------------------------------
@@ -235,8 +295,6 @@ public:
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
-  virtual void getGrayLine(Guchar *in, Guchar *out, int length);
-  virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
 
   virtual int getNComps() { return 1; }
   virtual void getDefaultColor(GfxColor *color);
@@ -255,6 +313,8 @@ private:
   double whiteX, whiteY, whiteZ;    // white point
   double blackX, blackY, blackZ;    // black point
   double gamma;			    // gamma value
+  double kr, kg, kb;		    // gamut mapping mulitpliers
+  void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ);
 };
 
 //------------------------------------------------------------------------
@@ -299,8 +359,6 @@ public:
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
-  virtual void getGrayLine(Guchar *in, Guchar *out, int length);
-  virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
 
   virtual int getNComps() { return 3; }
   virtual void getDefaultColor(GfxColor *color);
@@ -323,6 +381,8 @@ private:
   double blackX, blackY, blackZ;    // black point
   double gammaR, gammaG, gammaB;    // gamma values
   double mat[9];		    // ABC -> XYZ transform matrix
+  double kr, kg, kb;		    // gamut mapping mulitpliers
+  void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ);
 };
 
 //------------------------------------------------------------------------
@@ -390,6 +450,7 @@ private:
   double blackX, blackY, blackZ;    // black point
   double aMin, aMax, bMin, bMax;    // range for the a and b components
   double kr, kg, kb;		    // gamut mapping mulitpliers
+  void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ);
 };
 
 //------------------------------------------------------------------------
@@ -429,6 +490,10 @@ private:
   double rangeMin[4];		// min values for each component
   double rangeMax[4];		// max values for each component
   Ref iccProfileStream;		// the ICC profile
+#ifdef USE_CMS
+  GfxColorTransform *transform;
+  GfxColorTransform *lineTransform; // color transform for line
+#endif
 };
 
 //------------------------------------------------------------------------
diff --git a/poppler/Makefile.am b/poppler/Makefile.am
index db9fd74..9d8644f 100644
--- a/poppler/Makefile.am
+++ b/poppler/Makefile.am
@@ -105,6 +105,10 @@ abiword_libs =					\
 
 endif
 
+if USE_CMS
+cms_libs = -llcms
+endif
+
 INCLUDES =					\
 	-I$(top_srcdir)				\
 	-I$(top_srcdir)/goo			\
@@ -126,6 +130,7 @@ CXXFLAGS+=$(PTHREAD_CFLAGS)
 libpoppler_la_LIBADD =				\
 	$(top_builddir)/goo/libgoo.la		\
 	$(top_builddir)/fofi/libfofi.la		\
+	$(cms_libs)				\
 	$(splash_libs)				\
 	$(libjpeg_libs)				\
 	$(zlib_libs)				\


More information about the poppler mailing list