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..8c358bb 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 */ + 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/poppler/Gfx.cc b/poppler/Gfx.cc index dc5f8e3..b6421ba 100644 --- a/poppler/Gfx.cc +++ b/poppler/Gfx.cc @@ -1021,6 +1021,11 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { } } obj2.free(); + if (obj1.dictLookup("OPM", &obj2)->isInt()) { + state->setOverprintMode(obj2.getInt()); + out->updateOverprintMode(state); + } + obj2.free(); // stroke adjust if (obj1.dictLookup("SA", &obj2)->isBool()) { @@ -1314,6 +1319,8 @@ void Gfx::opSetRenderingIntent(Object args[], int numArgs) { void Gfx::opSetFillGray(Object args[], int numArgs) { GfxColor color; + GfxColorSpace *colorSpace; + Object obj; if (textHaveCSPattern && drawText) { GBool needFill = out->deviceHasTextClip(state); @@ -1324,7 +1331,14 @@ void Gfx::opSetFillGray(Object args[], int numArgs) { out->restoreState(state); } state->setFillPattern(NULL); - state->setFillColorSpace(new GfxDeviceGrayColorSpace()); + res->lookupColorSpace("DefaultGray", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceGrayColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); + state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setFillColor(&color); @@ -1340,9 +1354,18 @@ void Gfx::opSetFillGray(Object args[], int numArgs) { void Gfx::opSetStrokeGray(Object args[], int numArgs) { GfxColor color; + GfxColorSpace *colorSpace; + Object obj; state->setStrokePattern(NULL); - state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + res->lookupColorSpace("DefaultGray", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceGrayColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); + state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setStrokeColor(&color); @@ -1351,6 +1374,8 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) { void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { GfxColor color; + GfxColorSpace *colorSpace; + Object obj; int i; if (textHaveCSPattern && drawText) { @@ -1361,8 +1386,15 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { } out->restoreState(state); } + res->lookupColorSpace("DefaultCMYK", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceCMYKColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); state->setFillPattern(NULL); - state->setFillColorSpace(new GfxDeviceCMYKColorSpace()); + state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1380,10 +1412,19 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { GfxColor color; + GfxColorSpace *colorSpace; + Object obj; int i; state->setStrokePattern(NULL); - state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace()); + res->lookupColorSpace("DefaultCMYK", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceCMYKColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); + state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1393,6 +1434,8 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { } void Gfx::opSetFillRGBColor(Object args[], int numArgs) { + Object obj; + GfxColorSpace *colorSpace; GfxColor color; int i; @@ -1405,7 +1448,14 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) { out->restoreState(state); } state->setFillPattern(NULL); - state->setFillColorSpace(new GfxDeviceRGBColorSpace()); + res->lookupColorSpace("DefaultRGB", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceRGBColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); + state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1422,11 +1472,20 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) { } void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { + Object obj; + GfxColorSpace *colorSpace; GfxColor color; int i; state->setStrokePattern(NULL); - state->setStrokeColorSpace(new GfxDeviceRGBColorSpace()); + res->lookupColorSpace("DefaultRGB", &obj); + if (obj.isNull()) { + colorSpace = new GfxDeviceRGBColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&obj, this); + } + obj.free(); + state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -4132,11 +4191,32 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { if (!obj1.isNull()) { colorSpace = GfxColorSpace::parse(&obj1, this); } else if (csMode == streamCSDeviceGray) { - colorSpace = new GfxDeviceGrayColorSpace(); + Object objCS; + res->lookupColorSpace("DefaultGray", &objCS); + if (objCS.isNull()) { + colorSpace = new GfxDeviceGrayColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&objCS, this); + } + objCS.free(); } else if (csMode == streamCSDeviceRGB) { - colorSpace = new GfxDeviceRGBColorSpace(); + Object objCS; + res->lookupColorSpace("DefaultRGB", &objCS); + if (objCS.isNull()) { + colorSpace = new GfxDeviceRGBColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&objCS, this); + } + objCS.free(); } else if (csMode == streamCSDeviceCMYK) { - colorSpace = new GfxDeviceCMYKColorSpace(); + Object objCS; + res->lookupColorSpace("DefaultCMYK", &objCS); + if (objCS.isNull()) { + colorSpace = new GfxDeviceCMYKColorSpace(); + } else { + colorSpace = GfxColorSpace::parse(&objCS, this); + } + objCS.free(); } else { colorSpace = NULL; } diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc index 893540f..bce57cc 100644 --- a/poppler/GfxState.cc +++ b/poppler/GfxState.cc @@ -5431,6 +5431,7 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox, strokeOpacity = 1; fillOverprint = gFalse; strokeOverprint = gFalse; + overprintMode = 0; transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL; lineWidth = 1; diff --git a/poppler/GfxState.h b/poppler/GfxState.h index 099b41c..c8f28ea 100644 --- a/poppler/GfxState.h +++ b/poppler/GfxState.h @@ -1332,6 +1332,7 @@ public: double getStrokeOpacity() { return strokeOpacity; } GBool getFillOverprint() { return fillOverprint; } GBool getStrokeOverprint() { return strokeOverprint; } + int getOverprintMode() { return overprintMode; } Function **getTransfer() { return transfer; } double getLineWidth() { return lineWidth; } void getLineDash(double **dash, int *length, double *start) @@ -1402,6 +1403,7 @@ public: void setStrokeOpacity(double opac) { strokeOpacity = opac; } void setFillOverprint(GBool op) { fillOverprint = op; } void setStrokeOverprint(GBool op) { strokeOverprint = op; } + void setOverprintMode(int op) { overprintMode = op; } void setTransfer(Function **funcs); void setLineWidth(double width) { lineWidth = width; } void setLineDash(double *dash, int length, double start); @@ -1482,6 +1484,7 @@ private: double strokeOpacity; // stroke opacity GBool fillOverprint; // fill overprint GBool strokeOverprint; // stroke overprint + int overprintMode; // overprint mode Function *transfer[4]; // transfer function (entries may be: all // NULL = identity; last three NULL = // single function; all four non-NULL = diff --git a/poppler/OutputDev.h b/poppler/OutputDev.h index c922c7a..2e3f9ae 100644 --- a/poppler/OutputDev.h +++ b/poppler/OutputDev.h @@ -177,6 +177,7 @@ public: virtual void updateStrokeOpacity(GfxState * /*state*/) {} virtual void updateFillOverprint(GfxState * /*state*/) {} virtual void updateStrokeOverprint(GfxState * /*state*/) {} + virtual void updateOverprintMode(GfxState * /*state*/) {} virtual void updateTransfer(GfxState * /*state*/) {} virtual void updateFillColorStop(GfxState * /*state*/, double /*offset*/) {} diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index baa0ef9..fcb3d38 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -95,6 +95,11 @@ static inline void convertGfxColor(SplashColorPtr dest, GfxCMYK cmyk; #endif + // make gcc happy + color[0] = color[1] = color[2] = 0; +#if SPLASH_CMYK + color[3] = 0; +#endif switch (colorMode) { case splashModeMono1: case splashModeMono8: @@ -123,18 +128,196 @@ static inline void convertGfxColor(SplashColorPtr dest, splashColorCopy(dest, color); } +static inline SplashPattern *createOverprintPattern(GfxColorSpace *colorSpace, SplashColorPtr color, GBool image, Guchar tolerance = 0x01) { + switch (colorSpace->getMode()) { + case csDeviceCMYK: + if (image) + return new SplashSolidColor(color); + case csSeparation: + case csDeviceN: + if (image && colorSpace->getMode() == csDeviceN) { + GfxDeviceNColorSpace *deviceNSpace = (GfxDeviceNColorSpace *) colorSpace; + GBool hasSpot = gFalse; + GBool hasProcess = gFalse; + for (int i = 0; i < deviceNSpace->getNComps(); i++) { + GooString *name = deviceNSpace->getColorantName(i)->upperCase(); + if (name->cmp("CYAN") != 0 && + name->cmp("MAGENTA") != 0 && + name->cmp("YELLOW") != 0 && + name->cmp("BLACK") != 0 && + name->cmp("NONE") != 0) + hasSpot = gTrue; + else + hasProcess = gTrue; + } + if (hasSpot && hasProcess) + return new SplashSolidColor(color); + } + return new SplashOverprintColor(colorSpace, color, tolerance); + case csIndexed: + return createOverprintPattern(((GfxIndexedColorSpace *) colorSpace)->getBase(), color, image); + case csICCBased: + if (image) + return createOverprintPattern(((GfxICCBasedColorSpace *) colorSpace)->getAlt(), color, gFalse, 0x05); + case csDeviceGray: + if (image) + return new SplashOverprintColor(colorSpace, color, tolerance); + default: // knockout + return new SplashSolidColor(color); + } +} + + +//------------------------------------------------------------------------ +// SplashOverprintColor +//------------------------------------------------------------------------ + +SplashOverprintColor::SplashOverprintColor(GfxColorSpace *colorSpaceA, SplashColorPtr colorA, Guchar toleranceA) { + splashColorCopy(color, colorA); + colorSpace = colorSpaceA; + tolerance = toleranceA; +} + +SplashOverprintColor::~SplashOverprintColor() { +} + +GBool SplashOverprintColor::getColor(int x, int y, SplashColorPtr c) { + splashColorCopy(c, color); + return gTrue; +} + +void SplashOverprintColor::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, + Guchar aDest, SplashColorPtr cDest, + SplashColorPtr colorResult) { + switch(colorSpace->getMode()) { + case csDeviceGray: // only in case of grayscale images + colorResult[0] = cDest[0]; + colorResult[1] = cDest[1]; + colorResult[2] = cDest[2]; + colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + break; + case csDeviceCMYK: + colorResult[0] = (cSrc[0] < tolerance && op) ? + cDest[0] : (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + colorResult[1] = (cSrc[1] < tolerance && op) ? + cDest[1] : (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + colorResult[2] = (cSrc[2] < tolerance && op) ? + cDest[2] : (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + colorResult[3] = (cSrc[3] < tolerance && op) ? + cDest[3] : (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + break; + + case csSeparation: + { + GfxSeparationColorSpace *sepSpace = (GfxSeparationColorSpace *) colorSpace; + GooString *name = sepSpace->getName()->upperCase(); + if (name->cmp("CYAN") == 0) { + colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + colorResult[1] = cDest[1]; + colorResult[2] = cDest[2]; + colorResult[3] = cDest[3]; + } else if (name->cmp("MAGENTA") == 0) { + colorResult[0] = cDest[0]; + colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + colorResult[2] = cDest[2]; + colorResult[3] = cDest[3]; + } else if (name->cmp("YELLOW") == 0) { + colorResult[0] = cDest[0]; + colorResult[1] = cDest[1]; + colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + colorResult[3] = cDest[3]; + } else if (name->cmp("BLACK") == 0) { + colorResult[0] = cDest[0]; + colorResult[1] = cDest[1]; + colorResult[2] = cDest[2]; + colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + } else { + colorResult[0] = ((int) cDest[0] + cSrc[0]) > 0xff ? 0xff : cDest[0] + cSrc[0]; + colorResult[1] = ((int) cDest[1] + cSrc[1]) > 0xff ? 0xff : cDest[1] + cSrc[1]; + colorResult[2] = ((int) cDest[2] + cSrc[2]) > 0xff ? 0xff : cDest[2] + cSrc[2]; + colorResult[3] = ((int) cDest[3] + cSrc[3]) > 0xff ? 0xff : cDest[3] + cSrc[3]; + } + } + break; + case csDeviceN: + { + GfxDeviceNColorSpace *deviceNSpace = (GfxDeviceNColorSpace *) colorSpace; + colorResult[0] = cDest[0]; + colorResult[1] = cDest[1]; + colorResult[2] = cDest[2]; + colorResult[3] = cDest[3]; + for (int i = 0; i < deviceNSpace->getNComps(); i++) { + GooString *name = deviceNSpace->getColorantName(i)->upperCase(); + if (name->cmp("CYAN") == 0) { + colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + } else if (name->cmp("MAGENTA") == 0) { + colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + } else if (name->cmp("YELLOW") == 0) { + colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + } else if (name->cmp("BLACK") == 0) { + colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + } else if (name->cmp("NONE") != 0) { + colorResult[0] = (cSrc[0] < tolerance && op) ? + cDest[0] : (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + colorResult[1] = (cSrc[1] < tolerance && op) ? + cDest[1] : (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + colorResult[2] = (cSrc[2] < tolerance && op) ? + cDest[2] : (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + colorResult[3] = (cSrc[3] < tolerance && op) ? + cDest[3] : (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + break; + } + } + } + break; + default: + // default for overprint is knockout: + colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); + } +} //------------------------------------------------------------------------ // SplashGouraudPattern //------------------------------------------------------------------------ SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA, - GfxState *stateA, GfxGouraudTriangleShading *shadingA) { + GfxState *stateA, GfxGouraudTriangleShading *shadingA, SplashColorMode modeA) { + SplashColor defaultColor; + GfxColor srcColor; state = stateA; shading = shadingA; + mode = modeA; bDirectColorTranslation = bDirectColorTranslationA; + shadingA->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, mode, shadingA->getColorSpace(), &srcColor); + opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01); } SplashGouraudPattern::~SplashGouraudPattern() { + delete opPattern; } void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) { @@ -156,6 +339,11 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor } } +void SplashGouraudPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, + Guchar aDest, SplashColorPtr cDest, + SplashColorPtr colorResult) { + opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult); +} //------------------------------------------------------------------------ // SplashUnivariatePattern //------------------------------------------------------------------------ @@ -214,6 +402,9 @@ GBool SplashUnivariatePattern::testPosition(int x, int y) { SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA): SplashUnivariatePattern(colorModeA, stateA, shadingA) { + SplashColor defaultColor; + GfxColor srcColor; + shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); dx -= x0; dy -= y0; @@ -221,9 +412,13 @@ SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *s a = dx*dx + dy*dy - dr*dr; if (fabs(a) > RADIAL_EPSILON) inva = 1.0 / a; + shadingA->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); + opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01); } SplashRadialPattern::~SplashRadialPattern() { + delete opPattern; } GBool SplashRadialPattern::getParameter(double xs, double ys, SplashCoord *t) { @@ -309,6 +504,12 @@ GBool SplashRadialPattern::getParameter(double xs, double ys, SplashCoord *t) { return gFalse; } +void SplashRadialPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, + Guchar aDest, SplashColorPtr cDest, + SplashColorPtr colorResult) { + opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult); +} + #undef RADIAL_EPSILON //------------------------------------------------------------------------ @@ -318,13 +519,20 @@ GBool SplashRadialPattern::getParameter(double xs, double ys, SplashCoord *t) { SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA): SplashUnivariatePattern(colorModeA, stateA, shadingA) { + SplashColor defaultColor; + GfxColor srcColor; + shadingA->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; mul = 1 / (dx * dx + dy * dy); + shadingA->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); + opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01); } SplashAxialPattern::~SplashAxialPattern() { + delete opPattern; } GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) { @@ -347,6 +555,12 @@ GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) { return gTrue; } +void SplashAxialPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, + Guchar aDest, SplashColorPtr cDest, + SplashColorPtr colorResult) { + opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult); +} + //------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. @@ -1405,7 +1619,7 @@ void SplashOutputDev::updateFillColor(GfxState *state) { state->getFillRGB(&rgb); #if SPLASH_CMYK state->getFillCMYK(&cmyk); - splash->setFillPattern(getColor(gray, &rgb, &cmyk)); + splash->setFillPattern(getColor(state->getFillColorSpace(), gray, &rgb, &cmyk)); #else splash->setFillPattern(getColor(gray, &rgb)); #endif @@ -1422,14 +1636,14 @@ void SplashOutputDev::updateStrokeColor(GfxState *state) { state->getStrokeRGB(&rgb); #if SPLASH_CMYK state->getStrokeCMYK(&cmyk); - splash->setStrokePattern(getColor(gray, &rgb, &cmyk)); + splash->setStrokePattern(getColor(state->getStrokeColorSpace(), gray, &rgb, &cmyk)); #else splash->setStrokePattern(getColor(gray, &rgb)); #endif } #if SPLASH_CMYK -SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb, +SplashPattern *SplashOutputDev::getColor(GfxColorSpace *colorSpace, GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk) { #else SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) { @@ -1471,7 +1685,7 @@ SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) { color[1] = colToByte(cmyk->m); color[2] = colToByte(cmyk->y); color[3] = colToByte(cmyk->k); - pattern = new SplashSolidColor(color); + pattern = createOverprintPattern(colorSpace, color, gFalse); break; #endif } @@ -1491,6 +1705,18 @@ void SplashOutputDev::updateStrokeOpacity(GfxState *state) { splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); } +void SplashOutputDev::updateFillOverprint(GfxState *state) { + splash->setFillOverprint(state->getFillOverprint()); +} + +void SplashOutputDev::updateStrokeOverprint(GfxState *state) { + splash->setStrokeOverprint(state->getStrokeOverprint()); +} + +void SplashOutputDev::updateOverprintMode(GfxState *state) { + splash->setOverprintMode(state->getOverprintMode()); +} + void SplashOutputDev::updateFont(GfxState * /*state*/) { needFontUpdate = gTrue; } @@ -2429,19 +2655,40 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); } - break; + break; + case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: - p = imgData->imgStr->getLine(); - q = colorLine; - - imgData->colorMap->getRGBLine(p, colorLine, imgData->width); - break; - case splashModeXBGR8: - p = imgData->imgStr->getLine(); - q = colorLine; - - imgData->colorMap->getRGBXLine(p, colorLine, imgData->width); + if (!imgData->colorMap->useRGBLine()) + { + GfxRGB rgb; + for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; + x < imgData->width; + ++x, p += nComps) { + imgData->colorMap->getRGB(p, &rgb); + *q++ = colToByte(rgb.r); + *q++ = colToByte(rgb.g); + *q++ = colToByte(rgb.b); + if (imgData->colorMode == splashModeXBGR8) *q++ = 255; + } + } + else + { + p = imgData->imgStr->getLine(); + q = colorLine; + unsigned int* line = (unsigned int *)gmallocn(imgData->width, sizeof(unsigned int)); + + imgData->colorMap->getRGBLine(p, line, imgData->width); + for (x = 0; x < imgData->width; ++x) { + *q++ = (line[x] >> 16) & 255; + *q++ = (line[x] >> 8) & 255; + *q++ = (line[x]) & 255; + if (imgData->colorMode == splashModeXBGR8) { + *q++ = 255; + } + } + gfree(line); + } break; #if SPLASH_CMYK case splashModeCMYK8: @@ -2664,12 +2911,15 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, SplashCoord mat[6]; SplashOutImageData imgData; SplashColorMode srcMode; + SplashColor defaultColor; + GfxColor srcColor; SplashImageSource src; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif + GBool grayIndexed = gFalse; Guchar pix; int n, i; @@ -2734,15 +2984,24 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, break; #if SPLASH_CMYK case splashModeCMYK8: + grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getCMYK(&pix, &cmyk); + if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) + grayIndexed = gFalse; imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } +#ifndef USE_CMS + if (colorMap->getColorSpace()->getMode() == csIndexed) { + if (((GfxIndexedColorSpace *) colorMap->getColorSpace())->getBase()->getMode() == csICCBased) + grayIndexed = gFalse; + } +#endif break; #endif break; @@ -2755,8 +3014,12 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, srcMode = colorMode; } src = maskColors ? &alphaImageSrc : &imageSrc; + colorMap->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor); + SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, !grayIndexed); splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse, - width, height, mat); + width, height, mat, pattern); + delete pattern; if (inlineImg) { while (imgData.y < height) { imgData.imgStr->getLine(); @@ -2914,6 +3177,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, delete maskColorMap; } else { + SplashColor defaultColor; + GfxColor srcColor; //----- scale the mask image to the same size as the source image @@ -3026,9 +3291,12 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, } else { srcMode = colorMode; } + colorMap->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor); + SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, gTrue); splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue, - width, height, mat); - + width, height, mat, pattern); + delete pattern; delete maskBitmap; gfree(imgData.lookup); delete imgData.imgStr; @@ -3052,6 +3320,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; + SplashColor defaultColor; + GfxColor srcColor; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK @@ -3174,8 +3444,12 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, } else { srcMode = colorMode; } - splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat); + colorMap->getColorSpace()->getDefaultColor(&srcColor); + convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor); + SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, gTrue); + splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, pattern); + delete pattern; splash->setSoftMask(NULL); gfree(imgData.lookup); delete imgData.imgStr; @@ -3503,31 +3777,6 @@ void SplashOutputDev::clearModRegion() { splash->clearModRegion(); } -void SplashOutputDev::setFillColor(int r, int g, int b) { - GfxRGB rgb; - GfxGray gray; -#if SPLASH_CMYK - GfxCMYK cmyk; -#endif - - rgb.r = byteToCol(r); - rgb.g = byteToCol(g); - rgb.b = byteToCol(b); - gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5); - if (gray > gfxColorComp1) { - gray = gfxColorComp1; - } -#if SPLASH_CMYK - cmyk.c = gfxColorComp1 - rgb.r; - cmyk.m = gfxColorComp1 - rgb.g; - cmyk.y = gfxColorComp1 - rgb.b; - cmyk.k = 0; - splash->setFillPattern(getColor(gray, &rgb, &cmyk)); -#else - splash->setFillPattern(getColor(gray, &rgb)); -#endif -} - #if 1 //~tmp: turn off anti-aliasing temporarily GBool SplashOutputDev::getVectorAntialias() { return splash->getVectorAntialias(); @@ -3549,7 +3798,7 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Catalog *catalog, Obje double *ptm, int paintType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, - double xStep, double yStep) + double xStep, double yStep) { PDFRectangle box; Gfx *gfx; @@ -3662,7 +3911,7 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Catalog *catalog, Obje m1.m[4] = -kx; m1.m[5] = -ky; - bitmap = new SplashBitmap(surface_width, surface_height, colorMode != splashModeMono1, + bitmap = new SplashBitmap(surface_width, surface_height, colorMode != splashModeMono1, (paintType == 1) ? colorMode : splashModeMono8, gTrue); memset(bitmap->getAlphaPtr(), 0, bitmap->getWidth() * bitmap->getHeight()); if (paintType == 2) { @@ -3732,7 +3981,7 @@ GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTria default: break; } - SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading); + SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading, colorMode); // restore vector antialias because we support it here if (shading->isParameterized()) { GBool vaa = getVectorAntialias(); diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 26f3ddf..34def44 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -51,6 +51,35 @@ struct T3GlyphStack; struct SplashTransparencyGroup; //------------------------------------------------------------------------ +// SplashOverprintColor +//------------------------------------------------------------------------ + +class SplashOverprintColor: public SplashPattern { +public: + + SplashOverprintColor(GfxColorSpace *colorSpace, SplashColorPtr colorA, Guchar tolerance); + + virtual SplashPattern *copy() { return new SplashOverprintColor(colorSpace, color, tolerance); } + + virtual ~SplashOverprintColor(); + + virtual GBool getColor(int x, int y, SplashColorPtr c); + + virtual GBool testPosition(int x, int y) { return gFalse; } + + virtual GBool isStatic() { return gTrue; } + + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); + +private: + + GfxColorSpace *colorSpace; + SplashColor color; + Guchar tolerance; +}; + +//------------------------------------------------------------------------ // Splash dynamic pattern //------------------------------------------------------------------------ @@ -90,18 +119,22 @@ public: virtual GBool getParameter(double xs, double ys, double *t); + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); + private: double x0, y0, x1, y1; double dx, dy, mul; + SplashOverprintColor *opPattern; }; // see GfxState.h, GfxGouraudTriangleShading class SplashGouraudPattern: public SplashGouraudColor { public: - SplashGouraudPattern(GBool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading); + SplashGouraudPattern(GBool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading, SplashColorMode mode); - virtual SplashPattern *copy() { return new SplashGouraudPattern(bDirectColorTranslation, state, shading); } + virtual SplashPattern *copy() { return new SplashGouraudPattern(bDirectColorTranslation, state, shading, mode); } virtual ~SplashGouraudPattern(); @@ -120,10 +153,14 @@ public: virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c); + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); private: GfxGouraudTriangleShading *shading; GfxState *state; GBool bDirectColorTranslation; + SplashOverprintColor *opPattern; + SplashColorMode mode; }; // see GfxState.h, GfxRadialShading @@ -138,9 +175,13 @@ public: virtual GBool getParameter(double xs, double ys, double *t); + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); + private: double x0, y0, r0, dx, dy, dr; double a, inva; + SplashOverprintColor *opPattern; }; //------------------------------------------------------------------------ @@ -220,6 +261,9 @@ public: virtual void updateBlendMode(GfxState *state); virtual void updateFillOpacity(GfxState *state); virtual void updateStrokeOpacity(GfxState *state); + virtual void updateFillOverprint(GfxState *state); + virtual void updateStrokeOverprint(GfxState *state); + virtual void updateOverprintMode(GfxState *state); //----- update text state virtual void updateFont(GfxState *state); @@ -327,9 +371,6 @@ public: // Clear the modified region. void clearModRegion(); - // Set the Splash fill color. - void setFillColor(int r, int g, int b); - SplashFont *getCurrentFont() { return font; } #if 1 //~tmp: turn off anti-aliasing temporarily @@ -344,7 +385,7 @@ private: void setupScreenParams(double hDPI, double vDPI); #if SPLASH_CMYK - SplashPattern *getColor(GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk); + SplashPattern *getColor(GfxColorSpace *colorSpace, GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk); #else SplashPattern *getColor(GfxGray gray, GfxRGB *rgb); #endif diff --git a/splash/Splash.cc b/splash/Splash.cc index 5d65834..34bf418 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -103,6 +103,10 @@ struct SplashPipe { // non-isolated group correction int nonIsolatedGroup; + + // stroke / fill operation and pattern for calculate overprint + GBool stroke; + SplashPattern *overprintPattern; }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { @@ -188,7 +192,7 @@ inline void Splash::updateModY(int y) { inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, SplashCoord aInput, GBool usesShape, - GBool nonIsolatedGroup) { + GBool nonIsolatedGroup, SplashPattern *opPattern, GBool strokeA) { pipeSetXY(pipe, x, y); pipe->pattern = NULL; @@ -239,6 +243,8 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, } else { pipe->nonIsolatedGroup = 0; } + pipe->stroke = strokeA; + pipe->overprintPattern = opPattern; } inline void Splash::pipeRun(SplashPipe *pipe) { @@ -297,10 +303,25 @@ inline void Splash::pipeRun(SplashPipe *pipe) { break; #if SPLASH_CMYK case splashModeCMYK8: - *pipe->destColorPtr++ = pipe->cSrc[0]; - *pipe->destColorPtr++ = pipe->cSrc[1]; - *pipe->destColorPtr++ = pipe->cSrc[2]; - *pipe->destColorPtr++ = pipe->cSrc[3]; + if (pipe->overprintPattern != NULL && + ((pipe->stroke && state->strokeOverprint) || + (!pipe->stroke && state->fillOverprint))) { + SplashColor cResult; + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = pipe->destColorPtr[3]; + pipe->overprintPattern->overprint(state->overprintMode == 1, pipe->aSrc, pipe->cSrc, 255, cDest, cResult); + *pipe->destColorPtr++ = cResult[0]; + *pipe->destColorPtr++ = cResult[1]; + *pipe->destColorPtr++ = cResult[2]; + *pipe->destColorPtr++ = cResult[3]; + } else { + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = pipe->cSrc[3]; + } break; #endif } @@ -435,14 +456,25 @@ inline void Splash::pipeRun(SplashPipe *pipe) { cResult2 = 0; cResult3 = 0; } else { - cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + if (pipe->overprintPattern != NULL && + ((pipe->stroke && state->strokeOverprint) || + (!pipe->stroke && state->fillOverprint))) { + SplashColor cResult; + pipe->overprintPattern->overprint(state->overprintMode == 1, aSrc, pipe->cSrc, alpha2, cDest, cResult); + cResult0 = cResult[0]; + cResult1 = cResult[1]; + cResult2 = cResult[2]; + cResult3 = cResult[3]; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2); - cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2); - cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2); - cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + + cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + aSrc * pipe->cSrc[3]) / alpha2); + } } break; #endif @@ -990,6 +1022,18 @@ void Splash::setFillAlpha(SplashCoord alpha) { state->fillAlpha = alpha; } +void Splash::setFillOverprint(GBool fop) { + state->fillOverprint = fop; +} + +void Splash::setStrokeOverprint(GBool gop) { + state->strokeOverprint = gop; +} + +void Splash::setOverprintMode(int opm) { + state->overprintMode = opm; +} + void Splash::setLineWidth(SplashCoord lineWidth) { state->lineWidth = lineWidth; } @@ -1242,7 +1286,7 @@ void Splash::strokeNarrow(SplashPath *path) { xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha, - gFalse, gFalse); + gFalse, gFalse, state->strokePattern, gTrue); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { @@ -1602,7 +1646,7 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, yMaxI = state->clip->getYMaxI(); } - pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse); + pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse, pattern); // draw the spans if (vectorAntialias) { @@ -1672,7 +1716,7 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { origBlendFunc = state->blendFunc; state->blendFunc = &blendXor; - pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse); + pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse, state->fillPattern); // draw the spans for (y = yMinI; y <= yMaxI; ++y) { @@ -1780,7 +1824,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) if (noClip) { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse); + state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse, state->fillPattern); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { @@ -1800,7 +1844,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse); + state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse, state->fillPattern); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { @@ -1822,7 +1866,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) } else { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse); + state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse, state->fillPattern); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { @@ -1846,7 +1890,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse); + state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse, state->fillPattern); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { @@ -2024,7 +2068,7 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, // initialize the pixel pipe pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha, - gTrue, gFalse); + gTrue, gFalse, state->fillPattern); if (vectorAntialias) { drawAAPixelInit(); } @@ -2159,7 +2203,7 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat) { + int w, int h, SplashCoord *mat, SplashPattern *opImagePattern) { SplashPipe pipe; GBool ok, rot; SplashCoord xScale, yScale, xShear, yShear, yShear1; @@ -2338,7 +2382,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // initialize the pixel pipe pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha, srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), - gFalse); + gFalse, opImagePattern); if (vectorAntialias) { drawAAPixelInit(); } @@ -3258,7 +3302,7 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) SplashPipe pipe; SplashColor cSrcVal; - pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse); + pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse, NULL, gTrue); if (vectorAntialias) { if (aaBuf == NULL) @@ -3959,7 +4003,7 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, yMaxI = state->clip->getYMaxI(); } - pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias && !hasBBox, gFalse); + pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias && !hasBBox, gFalse, pattern); // draw the spans if (vectorAntialias) { diff --git a/splash/Splash.h b/splash/Splash.h index a52dc13..c937ca1 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -120,6 +120,9 @@ public: void setBlendFunc(SplashBlendFunc func); void setStrokeAlpha(SplashCoord alpha); void setFillAlpha(SplashCoord alpha); + void setFillOverprint(GBool fop); + void setStrokeOverprint(GBool sop); + void setOverprintMode(int opm); void setLineWidth(SplashCoord lineWidth); void setLineCap(int lineCap); void setLineJoin(int lineJoin); @@ -202,7 +205,7 @@ public: // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat); + int w, int h, SplashCoord *mat, SplashPattern *overprintPattern = NULL); // Composite a rectangular region from onto this Splash // object. @@ -261,7 +264,7 @@ private: void pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, SplashCoord aInput, GBool usesShape, - GBool nonIsolatedGroup); + GBool nonIsolatedGroup, SplashPattern *overprintPattern = NULL, GBool stroke = gFalse); void pipeRun(SplashPipe *pipe); void pipeSetXY(SplashPipe *pipe, int x, int y); void pipeIncX(SplashPipe *pipe); diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 7c26e54..3c7fb4a 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,36 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, in return e; } +#include "poppler/GfxState.h" +#include "poppler/GfxState_helpers.h" + +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 +373,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/SplashPattern.cc b/splash/SplashPattern.cc index b42714d..f8a9f58 100644 --- a/splash/SplashPattern.cc +++ b/splash/SplashPattern.cc @@ -53,3 +53,17 @@ GBool SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { splashColorCopy(c, color); return gTrue; } + +void SplashSolidColor::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, + Guchar aDest, SplashColorPtr cDest, + SplashColorPtr colorResult) { + // default for overprint is knockout: + colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] + + aSrc * cSrc[0]) / aDest); + colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] + + aSrc * cSrc[1]) / aDest); + colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] + + aSrc * cSrc[2]) / aDest); + colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + + aSrc * cSrc[3]) / aDest); +} diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 42c1660..61f4172 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -52,6 +52,10 @@ public: // value for all pixels. virtual GBool isStatic() = 0; + // calculate destination color if overprint is enables + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult) = 0; + private: }; @@ -74,6 +78,9 @@ public: virtual GBool isStatic() { return gTrue; } + virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, + Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); + private: SplashColor color; diff --git a/splash/SplashState.cc b/splash/SplashState.cc index b1fa2f5..85433fb 100644 --- a/splash/SplashState.cc +++ b/splash/SplashState.cc @@ -71,6 +71,9 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; + fillOverprint = gFalse; + strokeOverprint = gFalse; + overprintMode = 0; next = NULL; } @@ -101,6 +104,9 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; + fillOverprint = gFalse; + strokeOverprint = gFalse; + overprintMode = 0; next = NULL; } @@ -131,6 +137,9 @@ SplashState::SplashState(SplashState *state) { softMask = state->softMask; deleteSoftMask = gFalse; inNonIsolatedGroup = state->inNonIsolatedGroup; + fillOverprint = state->fillOverprint; + strokeOverprint = state->strokeOverprint; + overprintMode = state->overprintMode; next = NULL; } diff --git a/splash/SplashState.h b/splash/SplashState.h index d0e05df..5cd08e7 100644 --- a/splash/SplashState.h +++ b/splash/SplashState.h @@ -68,6 +68,11 @@ public: // Set the soft mask bitmap. void setSoftMask(SplashBitmap *softMaskA); + // Set the overprint parametes. + void setFillOverprint(GBool fillOverprintA) { fillOverprint = fillOverprintA; } + void setStrokeOverprint(GBool strokeOverprintA) { strokeOverprint = strokeOverprintA; } + void setOverprintMode(int overprintModeA) { overprintMode = overprintModeA; } + private: SplashState(SplashState *state); @@ -92,6 +97,9 @@ private: SplashBitmap *softMask; GBool deleteSoftMask; GBool inNonIsolatedGroup; + GBool fillOverprint; + GBool strokeOverprint; + int overprintMode; SplashState *next; // used by Splash class 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..dd43787 100644 --- a/utils/pdftoppm.cc +++ b/utils/pdftoppm.cc @@ -71,7 +71,11 @@ static GBool mono = gFalse; static GBool gray = gFalse; static GBool png = gFalse; static GBool jpeg = gFalse; +static GBool jpegcmyk = gFalse; static GBool tiff = gFalse; +#ifdef SPLASH_CMYK +static GBool overprint = gFalse; +#endif static char enableFreeTypeStr[16] = ""; static char antialiasStr[16] = ""; static char vectorAntialiasStr[16] = ""; @@ -129,8 +133,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 +203,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 +351,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 +404,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);