diff --git a/goo/ImgWriter.h b/goo/ImgWriter.h
index f44c85d..0795934 100644
--- a/goo/ImgWriter.h
+++ b/goo/ImgWriter.h
@@ -27,6 +27,7 @@ class ImgWriter
 		virtual bool writeRow(unsigned char **row) = 0;
 		
 		virtual bool close() = 0;
+		virtual bool supportCMYK() { return 0; }
 };
 
 #endif
diff --git a/goo/JpegWriter.cc b/goo/JpegWriter.cc
index c9b7052..18c9771 100644
--- a/goo/JpegWriter.cc
+++ b/goo/JpegWriter.cc
@@ -27,13 +27,13 @@ void outputMessage(j_common_ptr cinfo)
 	error(-1, "%s", buffer);
 }
 
-JpegWriter::JpegWriter(int q, bool p)
-: progressive(p), quality(q)
+JpegWriter::JpegWriter(int q, bool p, J_COLOR_SPACE cm)
+: progressive(p), quality(q), colorMode(cm)
 {
 }
 
-JpegWriter::JpegWriter()
-: progressive(false), quality(-1)
+JpegWriter::JpegWriter(J_COLOR_SPACE cm)
+: progressive(false), quality(-1), colorMode(cm)
 {
 }
 
@@ -61,9 +61,24 @@ bool JpegWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
 	cinfo.density_unit = 1; // dots per inch
 	cinfo.X_density = hDPI;
 	cinfo.Y_density = vDPI;
-	cinfo.input_components = 3;     /* # of color components per pixel */
-	cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
-	jpeg_set_defaults(&cinfo);
+	cinfo.in_color_space = colorMode; /* colorspace of input image */
+	/* # of color components per pixel */
+	switch (colorMode) {
+		case JCS_GRAYSCALE:
+			cinfo.input_components = 1;     
+			break;
+		case JCS_RGB:
+			cinfo.input_components = 3;     
+			break;
+		case JCS_CMYK:
+			cinfo.input_components = 4;     
+			break;
+	}
+	jpeg_set_defaults(&cinfo);
+	if (cinfo.in_color_space == JCS_CMYK) {
+		jpeg_set_colorspace(&cinfo, JCS_YCCK);
+		cinfo.write_JFIF_header = TRUE;
+   }
 	
 	// Set quality
 	if( quality >= 0 && quality <= 100 ) { 
@@ -83,16 +98,36 @@ bool JpegWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
 
 bool JpegWriter::writePointers(unsigned char **rowPointers, int rowCount)
 {
+	if (colorMode == JCS_CMYK) {
+		for (int y = 0; y < rowCount; y++) {
+			unsigned char *row = rowPointers[y];
+			for (unsigned int x = 0; x < cinfo.image_width; x++) {
+				for (int n = 0; n < 4; n++) {
+					*row = 0xff - *row;
+					row++;
+				}
+			}
+		}
+	}
 	// Write all rows to the file
 	jpeg_write_scanlines(&cinfo, rowPointers, rowCount);
 	
 	return true;
 }
 
-bool JpegWriter::writeRow(unsigned char **row)
+bool JpegWriter::writeRow(unsigned char **rowPointer)
 {
+	if (colorMode == JCS_CMYK) {
+		unsigned char *row = rowPointer[0];
+		for (unsigned int x = 0; x < cinfo.image_width; x++) {
+			for (int n = 0; n < 4; n++) {
+				*row = 0xff - *row;
+				row++;
+			}
+		}
+	}
 	// Write the row to the file
-	jpeg_write_scanlines(&cinfo, row, 1);
+	jpeg_write_scanlines(&cinfo, rowPointer, 1);
 	
 	return true;
 }
diff --git a/goo/JpegWriter.h b/goo/JpegWriter.h
index 1f7a738..5e27798 100644
--- a/goo/JpegWriter.h
+++ b/goo/JpegWriter.h
@@ -29,8 +29,8 @@ extern "C" {
 class JpegWriter : public ImgWriter
 {
 	public:
-		JpegWriter(int quality, bool progressive);
-		JpegWriter();
+		JpegWriter(int quality, bool progressive, J_COLOR_SPACE colorMode = JCS_RGB);
+		JpegWriter(J_COLOR_SPACE colorMode = JCS_RGB);
 		~JpegWriter();
 		
 		bool init(FILE *f, int width, int height, int hDPI, int vDPI);
@@ -39,10 +39,12 @@ class JpegWriter : public ImgWriter
 		bool writeRow(unsigned char **row);
 		
 		bool close();
+		bool supportCMYK() { return colorMode == JCS_CMYK; }
 	
 	private:
 		bool progressive;
 		int quality;
+		J_COLOR_SPACE colorMode;
 		struct jpeg_compress_struct cinfo;
 		struct jpeg_error_mgr jerr;
 };
diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc
index 7c26e54..16a8bd2 100644
--- a/splash/SplashBitmap.cc
+++ b/splash/SplashBitmap.cc
@@ -302,6 +302,11 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, in
     #endif
 
     #ifdef ENABLE_LIBJPEG
+	#ifdef SPLASH_CMYK
+    case splashFormatJpegCMYK:
+      writer = new JpegWriter(JCS_CMYK);
+      break;
+	#endif
     case splashFormatJpeg:
       writer = new JpegWriter();
       break;
@@ -329,8 +334,98 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, in
 	return e;
 }
 
+// from GfxState.h
+static inline double byteToDbl(Guchar x) {
+  return (double)x / (double)255.0;
+}
+
+static inline Guchar dblToByte(double x) {
+  return (x * 255.0);
+}
+
+// from GfxState_helpers.h
+static inline double clip01(double x) {
+  return (x < 0) ? 0 : (x > 1) ? 1 : x;
+}
+
+static inline void cmykToRGBMatrixMultiplication(const double &c, const double &m, const double &y, const double &k, const double &c1, const double &m1, const double &y1, const double &k1, double &r, double &g, double &b)
+{
+  double x;
+  // this is a matrix multiplication, unrolled for performance
+  //                        C M Y K
+  x = c1 * m1 * y1 * k1; // 0 0 0 0
+  r = g = b = x;
+  x = c1 * m1 * y1 * k;  // 0 0 0 1
+  r += 0.1373 * x;
+  g += 0.1216 * x;
+  b += 0.1255 * x;
+  x = c1 * m1 * y  * k1; // 0 0 1 0
+  r += x;
+  g += 0.9490 * x;
+  x = c1 * m1 * y  * k;  // 0 0 1 1
+  r += 0.1098 * x;
+  g += 0.1020 * x;
+  x = c1 * m  * y1 * k1; // 0 1 0 0
+  r += 0.9255 * x;
+  b += 0.5490 * x;
+  x = c1 * m  * y1 * k;  // 0 1 0 1
+  r += 0.1412 * x;
+  x = c1 * m  * y  * k1; // 0 1 1 0
+  r += 0.9294 * x;
+  g += 0.1098 * x;
+  b += 0.1412 * x;
+  x = c1 * m  * y  * k;  // 0 1 1 1
+  r += 0.1333 * x;
+  x = c  * m1 * y1 * k1; // 1 0 0 0
+  g += 0.6784 * x;
+  b += 0.9373 * x;
+  x = c  * m1 * y1 * k;  // 1 0 0 1
+  g += 0.0588 * x;
+  b += 0.1412 * x;
+  x = c  * m1 * y  * k1; // 1 0 1 0
+  g += 0.6510 * x;
+  b += 0.3137 * x;
+  x = c  * m1 * y  * k;  // 1 0 1 1
+  g += 0.0745 * x;
+  x = c  * m  * y1 * k1; // 1 1 0 0
+  r += 0.1804 * x;
+  g += 0.1922 * x;
+  b += 0.5725 * x;
+  x = c  * m  * y1 * k;  // 1 1 0 1
+  b += 0.0078 * x;
+  x = c  * m  * y  * k1; // 1 1 1 0
+  r += 0.2118 * x;
+  g += 0.2119 * x;
+  b += 0.2235 * x;
+}
+
+void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) {
+	SplashColor col;
+	double c, m, y, k, c1, m1, y1, k1, r, g, b;
+
+	for (int x = 0; x < width; x++) {
+		getPixel(x, yl, col);
+		c = byteToDbl(col[0]);
+		m = byteToDbl(col[1]);
+		y = byteToDbl(col[2]);
+		k = byteToDbl(col[3]);
+		c1 = 1 - c;
+		m1 = 1 - m;
+		y1 = 1 - y;
+		k1 = 1 - k;
+		cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
+		*line++ = dblToByte(clip01(r));
+		*line++ = dblToByte(clip01(g));
+		*line++ = dblToByte(clip01(b));
+	}
+}
+
 SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI) {
-  if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8) {
+  if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8
+#if SPLASH_CMYK
+	  && mode != splashModeCMYK8
+#endif
+	  ) {
     error(-1, "unsupported SplashBitmap mode");
     return splashErrGeneric;
   }
@@ -340,6 +435,35 @@ SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int
   }
 
   switch (mode) {
+#if SPLASH_CMYK
+    case splashModeCMYK8:
+		if (writer->supportCMYK()) {
+		  SplashColorPtr row;
+		  unsigned char **row_pointers = new unsigned char*[height];
+		  row = data;
+
+		  for (int y = 0; y < height; ++y) {
+			row_pointers[y] = row;
+			row += rowSize;
+		  }
+		  if (!writer->writePointers(row_pointers, height)) {
+			delete[] row_pointers;
+			return splashErrGeneric;
+		  }
+		  delete[] row_pointers;
+		} else {
+		  unsigned char *row = new unsigned char[3 * width];
+		  for (int y = 0; y < height; y++) {
+			  getRGBLine(y, row);
+			  if (!writer->writeRow(&row)) {
+				  delete[] row;
+				  return splashErrGeneric;
+			  }
+		  }
+		  delete[] row;
+		}
+		break;
+#endif
     case splashModeRGB8:
     {
       SplashColorPtr row;
diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h
index b276a61..3336507 100644
--- a/splash/SplashBitmap.h
+++ b/splash/SplashBitmap.h
@@ -71,6 +71,7 @@ public:
   SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI);
 
   void getPixel(int x, int y, SplashColorPtr pixel);
+  void getRGBLine(int y, SplashColorPtr line);
   Guchar getAlpha(int x, int y);
 
 private:
diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h
index 273c32d..72d41d9 100644
--- a/splash/SplashTypes.h
+++ b/splash/SplashTypes.h
@@ -162,7 +162,8 @@ typedef int SplashError;
 enum SplashImageFileFormat {
   splashFormatJpeg,
   splashFormatPng,
-  splashFormatTiff
+  splashFormatTiff,
+  splashFormatJpegCMYK
 };
 
 #endif
diff --git a/utils/pdftoppm.cc b/utils/pdftoppm.cc
index 8a7c702..7b7eaca 100644
--- a/utils/pdftoppm.cc
+++ b/utils/pdftoppm.cc
@@ -71,7 +71,9 @@ static GBool mono = gFalse;
 static GBool gray = gFalse;
 static GBool png = gFalse;
 static GBool jpeg = gFalse;
+static GBool jpegcmyk = gFalse;
 static GBool tiff = gFalse;
+static GBool overprint = gFalse;
 static char enableFreeTypeStr[16] = "";
 static char antialiasStr[16] = "";
 static char vectorAntialiasStr[16] = "";
@@ -129,8 +131,16 @@ static const ArgDesc argDesc[] = {
    "generate a PNG file"},
 #endif
 #if ENABLE_LIBJPEG
-  {"-jpeg",    argFlag,     &jpeg,           0,
+  {"-jpeg",   argFlag,     &jpeg,           0,
    "generate a JPEG file"},
+#if SPLASH_CMYK
+  {"-jpegcmyk",argFlag,	   &jpegcmyk,       0,
+   "generate a CMYK JPEG file"},
+#endif
+#endif
+#if SPLASH_CMYK
+  {"-overprint",argFlag,   &overprint,      0,
+   "enable overprint"},
 #endif
 #if ENABLE_LIBTIFF
   {"-tiff",    argFlag,     &tiff,           0,
@@ -191,6 +201,8 @@ static void savePageSlice(PDFDoc *doc,
       bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution);
     } else if (jpeg) {
       bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution);
+    } else if (jpegcmyk) {
+      bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution);
     } else if (tiff) {
       bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, TiffCompressionStr);
     } else {
@@ -337,11 +349,24 @@ int main(int argc, char *argv[]) {
   }
 
   // write PPM files
-  paperColor[0] = 255;
-  paperColor[1] = 255;
-  paperColor[2] = 255;
+#if SPLASH_CMYK
+  if (jpegcmyk || overprint) {
+	  paperColor[0] = 0;
+	  paperColor[1] = 0;
+	  paperColor[2] = 0;
+	  paperColor[3] = 0;
+  } else 
+#endif
+  {
+	  paperColor[0] = 255;
+	  paperColor[1] = 255;
+	  paperColor[2] = 255;
+  }
   splashOut = new SplashOutputDev(mono ? splashModeMono1 :
 				    gray ? splashModeMono8 :
+#if SPLASH_CMYK
+					(jpegcmyk || overprint) ? splashModeCMYK8 :
+#endif
 				             splashModeRGB8, 4,
 				  gFalse, paperColor);
   splashOut->startDoc(doc->getXRef());
@@ -377,7 +402,7 @@ int main(int argc, char *argv[]) {
       pg_h = tmp;
     }
     if (ppmRoot != NULL) {
-      const char *ext = png ? "png" : jpeg ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm";
+      const char *ext = png ? "png" : (jpeg || jpegcmyk) ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm";
       if (singleFile) {
         snprintf(ppmFile, PPM_FILE_SZ, "%.*s.%s",
               PPM_FILE_SZ - 32, ppmRoot, ext);