diff options
Diffstat (limited to 'xpdf/SplashOutputDev.cc')
-rw-r--r-- | xpdf/SplashOutputDev.cc | 549 |
1 files changed, 432 insertions, 117 deletions
diff --git a/xpdf/SplashOutputDev.cc b/xpdf/SplashOutputDev.cc index 50e4801..d9befec 100644 --- a/xpdf/SplashOutputDev.cc +++ b/xpdf/SplashOutputDev.cc @@ -2,7 +2,7 @@ // // SplashOutputDev.cc // -// Copyright 2003 Glyph & Cog, LLC +// Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== @@ -27,6 +27,7 @@ #include "BuiltinFont.h" #include "BuiltinFontTables.h" #include "FoFiTrueType.h" +#include "JPXStream.h" #include "SplashBitmap.h" #include "SplashGlyphBitmap.h" #include "SplashPattern.h" @@ -118,7 +119,9 @@ static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, int i, x; for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 255) { + if (dest[i] == 0) { + blend[i] = 0; + } else if (src[i] == 255) { blend[i] = 255; } else { x = (dest[i] * 255) / (255 - src[i]); @@ -132,7 +135,9 @@ static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, int i, x; for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 0) { + if (dest[i] == 255) { + blend[i] = 255; + } else if (src[i] == 0) { blend[i] = 0; } else { x = ((255 - dest[i]) * 255) / src[i]; @@ -284,7 +289,10 @@ static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { - Guchar r0, g0, b0, r1, g1, b1; + Guchar r0, g0, b0; +#if SPLASH_CMYK + Guchar r1, g1, b1; +#endif switch (cm) { case splashModeMono1: @@ -317,7 +325,10 @@ static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { - Guchar r0, g0, b0, r1, g1, b1; + Guchar r0, g0, b0; +#if SPLASH_CMYK + Guchar r1, g1, b1; +#endif switch (cm) { case splashModeMono1: @@ -435,7 +446,11 @@ SplashBlendFunc splashOutBlendFuncs[] = { class SplashOutFontFileID: public SplashFontFileID { public: - SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; } + SplashOutFontFileID(Ref *rA) { + r = *rA; + substIdx = -1; + oblique = 0; + } ~SplashOutFontFileID() {} @@ -444,12 +459,15 @@ public: ((SplashOutFontFileID *)id)->r.gen == r.gen; } + void setOblique(double obliqueA) { oblique = obliqueA; } + double getOblique() { return oblique; } void setSubstIdx(int substIdxA) { substIdx = substIdxA; } int getSubstIdx() { return substIdx; } private: Ref r; + double oblique; int substIdx; }; @@ -536,6 +554,10 @@ T3FontCache::~T3FontCache() { struct T3GlyphStack { Gushort code; // character code + GBool haveDx; // set after seeing a d0/d1 operator + GBool doNotCache; // set if we see a gsave/grestore before + // the d0/d1 + //----- cache info T3FontCache *cache; // font cache for the current font T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph @@ -580,6 +602,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, bitmapRowPad = bitmapRowPadA; bitmapTopDown = bitmapTopDownA; bitmapUpsideDown = gFalse; + noComposite = gFalse; allowAntialias = allowAntialiasA; vectorAntialias = allowAntialias && globalParams->getVectorAntialias() && @@ -596,6 +619,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust(globalParams->getStrokeAdjust()); splash->clear(paperColor, 0); fontEngine = NULL; @@ -688,9 +712,6 @@ void SplashOutputDev::startDoc(XRef *xrefA) { delete fontEngine; } fontEngine = new SplashFontEngine( -#if HAVE_T1LIB_H - globalParams->getEnableT1lib(), -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H globalParams->getEnableFreeType(), globalParams->getDisableFreeTypeHinting() @@ -777,18 +798,28 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { } void SplashOutputDev::endPage() { - if (colorMode != splashModeMono1) { + if (colorMode != splashModeMono1 && !noComposite) { splash->compositeBackground(paperColor); } } void SplashOutputDev::saveState(GfxState *state) { splash->saveState(); + if (t3GlyphStack && !t3GlyphStack->haveDx) { + t3GlyphStack->doNotCache = gTrue; + error(errSyntaxWarning, -1, + "Save (q) operator before d0/d1 in Type 3 glyph"); + } } void SplashOutputDev::restoreState(GfxState *state) { splash->restoreState(); needFontUpdate = gTrue; + if (t3GlyphStack && !t3GlyphStack->haveDx) { + t3GlyphStack->doNotCache = gTrue; + error(errSyntaxWarning, -1, + "Restore (Q) operator before d0/d1 in Type 3 glyph"); + } } void SplashOutputDev::updateAll(GfxState *state) { @@ -976,10 +1007,19 @@ void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, if (overprintFlag && globalParams->getOverprintPreview()) { mask = colorSpace->getOverprintMask(); + // The OPM (overprintMode) setting is only relevant when the color + // space is DeviceCMYK or is "implicitly converted to DeviceCMYK". + // Per the PDF spec, this happens with ICCBased color spaces only + // if the profile matches the output device -- Acrobat's output + // preview mode does NOT honor OPM=1 for ICCBased CMYK color + // spaces. To change the behavior here, use: + // if (singleColor && overprintMode && + // (colorSpace->getMode() == csDeviceCMYK || + // (colorSpace->getMode() == csICCBased && + // colorSpace->getNComps() == 4 && + // <...the profile matches...>))) if (singleColor && overprintMode && - (colorSpace->getMode() == csDeviceCMYK || - (colorSpace->getMode() == csICCBased && - colorSpace->getNComps() == 4))) { + colorSpace->getMode() == csDeviceCMYK) { colorSpace->getCMYK(singleColor, &cmyk); if (cmyk.c == 0) { mask &= ~1; @@ -1072,22 +1112,33 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { FoFiTrueType *ff; Ref embRef; Object refObj, strObj; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf; + FILE *extFontFile; +#else GString *tmpFileName, *fileName; FILE *tmpFile; +#endif + char blk[4096]; int *codeToGID; CharCodeToUnicode *ctu; double *textMat; - double m11, m12, m21, m22, fontSize; - double w, fontScaleMin, fontScaleAvg, fontScale; + double m11, m12, m21, m22, fontSize, oblique; + double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale; Gushort ww; SplashCoord mat[4]; char *name; Unicode uBuf[8]; - int c, substIdx, n, code, cmap, i; + int substIdx, n, code, cmap, i; needFontUpdate = gFalse; font = NULL; +#if LOAD_FONTS_FROM_MEM + fontBuf = NULL; +#else tmpFileName = NULL; + fileName = NULL; +#endif substIdx = -1; if (!(gfxFont = state->getFont())) { @@ -1098,10 +1149,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { goto err1; } - // sanity-check the font size - skip anything larger than 10 inches + // sanity-check the font size - skip anything larger than 20 inches // (this avoids problems allocating memory for the font cache) - if (state->getTransformedFontSize() - > 10 * (state->getHDPI() + state->getVDPI())) { + state->textTransformDelta(state->getFontSize(), state->getFontSize(), + &fsx, &fsy); + state->transformDelta(fsx, fsy, &fsx, &fsy); + if (fabs(fsx) > 20 * state->getHDPI() || + fabs(fsy) > 20 * state->getVDPI()) { goto err1; } @@ -1112,7 +1166,6 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } else { - fileName = NULL; fontNum = 0; if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { @@ -1125,6 +1178,24 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { // embedded font if (fontLoc->locType == gfxFontLocEmbedded) { gfxFont->getEmbeddedFontID(&embRef); +#if LOAD_FONTS_FROM_MEM + fontBuf = new GString(); + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { + error(errSyntaxError, -1, "Embedded font object is wrong type"); + strObj.free(); + delete fontLoc; + goto err2; + } + strObj.streamReset(); + while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) { + fontBuf->append(blk, n); + } + strObj.streamClose(); + strObj.free(); +#else if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { error(errIO, -1, "Couldn't create temporary font file"); delete fontLoc; @@ -1141,21 +1212,39 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { goto err2; } strObj.streamReset(); - while ((c = strObj.streamGetChar()) != EOF) { - fputc(c, tmpFile); + while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) { + fwrite(blk, 1, n, tmpFile); } strObj.streamClose(); strObj.free(); fclose(tmpFile); fileName = tmpFileName; +#endif // external font } else { // gfxFontLocExternal +#if LOAD_FONTS_FROM_MEM + if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) { + error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'", + fontLoc->path); + delete fontLoc; + goto err2; + } + fontBuf = new GString(); + while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) { + fontBuf->append(blk, n); + } + fclose(extFontFile); +#else fileName = fontLoc->path; +#endif fontNum = fontLoc->fontNum; if (fontLoc->substIdx >= 0) { id->setSubstIdx(fontLoc->substIdx); } + if (fontLoc->oblique != 0) { + id->setOblique(fontLoc->oblique); + } } // load the font file @@ -1163,8 +1252,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { case fontType1: if (!(fontFile = fontEngine->loadType1Font( id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else fileName->getCString(), fileName == tmpFileName, +#endif (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1176,8 +1269,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { case fontType1C: if (!(fontFile = fontEngine->loadType1CFont( id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else fileName->getCString(), fileName == tmpFileName, +#endif (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1189,8 +1286,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { case fontType1COT: if (!(fontFile = fontEngine->loadOpenTypeT1CFont( id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else fileName->getCString(), fileName == tmpFileName, +#endif (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1201,7 +1302,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { break; case fontTrueType: case fontTrueTypeOT: - if ((ff = FoFiTrueType::load(fileName->getCString()))) { +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + fontNum))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) { +#endif codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); n = 256; delete ff; @@ -1222,9 +1328,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } if (!(fontFile = fontEngine->loadTrueTypeFont( id, - fileName->getCString(), fontNum, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), fileName == tmpFileName, - codeToGID, n, +#endif + fontNum, codeToGID, n, gfxFont->getEmbeddedFontName() ? gfxFont->getEmbeddedFontName()->getCString() : (char *)NULL))) { @@ -1239,8 +1349,14 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { case fontCIDType0C: if (!(fontFile = fontEngine->loadCIDFont( id, +#if LOAD_FONTS_FROM_MEM + fontBuf +#else fileName->getCString(), - fileName == tmpFileName))) { + fileName == tmpFileName +#endif + ))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); @@ -1260,8 +1376,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } if (!(fontFile = fontEngine->loadOpenTypeCFFFont( id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else fileName->getCString(), fileName == tmpFileName, +#endif codeToGID, n))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1281,11 +1401,18 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } + } else if (!globalParams->getMapExtTrueTypeFontsViaUnicode()) { + codeToGID = NULL; + n = 0; } else { // create a CID-to-GID mapping, via Unicode if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { - //~ this should use fontNum to load the correct font - if ((ff = FoFiTrueType::load(fileName->getCString()))) { +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), + fontBuf->getLength(), fontNum))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) { +#endif // look for a Unicode cmap for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { if ((ff->getCmapPlatform(cmap) == 3 && @@ -1296,7 +1423,11 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } if (cmap < ff->getNumCmaps()) { // map CID -> Unicode -> GID - n = ctu->getLength(); + if (ctu->isIdentity()) { + n = 65536; + } else { + n = ctu->getLength(); + } codeToGID = (int *)gmallocn(n, sizeof(int)); for (code = 0; code < n; ++code) { if (ctu->mapToUnicode(code, uBuf, 8) > 0) { @@ -1318,9 +1449,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } if (!(fontFile = fontEngine->loadTrueTypeFont( id, - fileName->getCString(), fontNum, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), fileName == tmpFileName, - codeToGID, n, +#endif + fontNum, codeToGID, n, gfxFont->getEmbeddedFontName() ? gfxFont->getEmbeddedFontName()->getCString() : (char *)NULL))) { @@ -1342,10 +1477,15 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { // get the font matrix textMat = state->getTextMat(); fontSize = state->getFontSize(); - m11 = textMat[0] * fontSize * state->getHorizScaling(); - m12 = textMat[1] * fontSize * state->getHorizScaling(); - m21 = textMat[2] * fontSize; - m22 = textMat[3] * fontSize; + oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique(); + m11 = state->getHorizScaling() * textMat[0]; + m12 = state->getHorizScaling() * textMat[1]; + m21 = oblique * m11 + textMat[2]; + m22 = oblique * m12 + textMat[3]; + m11 *= fontSize; + m12 *= fontSize; + m21 *= fontSize; + m22 *= fontSize; // for substituted fonts: adjust the font matrix -- compare the // widths of letters and digits (A-Z, a-z, 0-9) in the original font @@ -1393,18 +1533,26 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { mat[2] = m21; mat[3] = m22; font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); +#if !LOAD_FONTS_FROM_MEM if (tmpFileName) { delete tmpFileName; } +#endif return; err2: delete id; err1: +#if LOAD_FONTS_FROM_MEM + if (fontBuf) { + delete fontBuf; + } +#else if (tmpFileName) { unlink(tmpFileName->getCString()); delete tmpFileName; } +#endif return; } @@ -1447,7 +1595,8 @@ void SplashOutputDev::eoFill(GfxState *state) { delete path; } -void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, +void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, + Object *strRef, int paintType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, @@ -1529,7 +1678,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, ya = y * yStep; mat1[4] = xa * mat[0] + ya * mat[2] + mat[4]; mat1[5] = xa * mat[1] + ya * mat[3] + mat[5]; - gfx->drawForm(str, resDict, mat1, bbox); + gfx->drawForm(strRef, resDict, mat1, bbox); } } return; @@ -1542,6 +1691,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, colorMode, gTrue, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen()); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust(globalParams->getStrokeAdjust()); for (i = 0; i < splashMaxColorComps; ++i) { color[i] = 0; } @@ -1556,7 +1706,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, // render the tile state->shiftCTM(-tileX0, -tileY0); updateCTM(state, 0, 0, 0, 0, 0, 0); - gfx->drawForm(str, resDict, mat, bbox); + gfx->drawForm(strRef, resDict, mat, bbox); state->shiftCTM(tileX0, tileY0); updateCTM(state, 0, 0, 0, 0, 0, 0); @@ -1889,8 +2039,8 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, t3GlyphStack->cache = t3Font; t3GlyphStack->cacheTag = NULL; t3GlyphStack->cacheData = NULL; - - haveT3Dx = gFalse; + t3GlyphStack->haveDx = gFalse; + t3GlyphStack->doNotCache = gFalse; return gFalse; } @@ -1920,7 +2070,7 @@ void SplashOutputDev::endType3Char(GfxState *state) { } void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { - haveT3Dx = gTrue; + t3GlyphStack->haveDx = gTrue; } void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, @@ -1932,10 +2082,14 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, int i, j; // ignore multiple d0/d1 operators - if (haveT3Dx) { + if (t3GlyphStack->haveDx) { + return; + } + t3GlyphStack->haveDx = gTrue; + // don't cache if we got a gsave/grestore before the d1 + if (t3GlyphStack->doNotCache) { return; } - haveT3Dx = gTrue; t3Font = t3GlyphStack->cache; @@ -2026,6 +2180,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, color[0] = 0xff; } splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust()); splash->setFillPattern(new SplashSolidColor(color)); splash->setStrokePattern(new SplashSolidColor(color)); //~ this should copy other state from t3GlyphStack->origSplash? @@ -2072,10 +2227,9 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { SplashColorPtr q; int x; - if (imgMaskData->y == imgMaskData->height) { - return gFalse; - } - if (!(p = imgMaskData->imgStr->getLine())) { + if (imgMaskData->y == imgMaskData->height || + !(p = imgMaskData->imgStr->getLine())) { + memset(line, 0, imgMaskData->width); return gFalse; } for (x = 0, q = line; x < imgMaskData->width; ++x) { @@ -2087,7 +2241,7 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, - GBool inlineImg) { + GBool inlineImg, GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; @@ -2106,6 +2260,8 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; + reduceImageResolution(str, ctm, &width, &height); + imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; @@ -2114,7 +2270,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, imgMaskData.y = 0; splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, - t3GlyphStack != NULL); + t3GlyphStack != NULL, interpolate); if (inlineImg) { while (imgMaskData.y < height) { imgMaskData.imgStr->getLine(); @@ -2130,7 +2286,8 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, - GBool inlineImg) { + GBool inlineImg, + GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; @@ -2145,6 +2302,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; + reduceImageResolution(str, ctm, &width, &height); imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; @@ -2154,12 +2312,12 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); maskSplash = new Splash(maskBitmap, gTrue); - maskColor[0] = 0; - maskSplash->clear(maskColor); + maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust()); + clearMaskRegion(state, maskSplash, 0, 0, 1, 1); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, - width, height, mat, gFalse); + width, height, mat, gFalse, interpolate); delete imgMaskData.imgStr; str->close(); delete maskSplash; @@ -2180,17 +2338,12 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, SplashOutImageData *imgData = (SplashOutImageData *)data; Guchar *p; SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#if SPLASH_CMYK - GfxCMYK cmyk; -#endif int nComps, x; - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); return gFalse; } @@ -2229,29 +2382,15 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - } + imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width); break; case splashModeRGB8: case splashModeBGR8: - for (x = 0, 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); - } + imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width); break; #if SPLASH_CMYK case splashModeCMYK8: - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - } + imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width); break; #endif } @@ -2274,10 +2413,11 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, Guchar alpha; int nComps, x, i; - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + memset(alphaLine, 0, imgData->width); return gFalse; } @@ -2353,7 +2493,8 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg) { + int *maskColors, GBool inlineImg, + GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageData imgData; @@ -2378,6 +2519,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; + reduceImageResolution(str, ctm, &width, &height); + imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); @@ -2433,12 +2576,14 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, if (colorMode == splashModeMono1) { srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; } else { srcMode = colorMode; } src = maskColors ? &alphaImageSrc : &imageSrc; splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse, - width, height, mat); + width, height, mat, interpolate); if (inlineImg) { while (imgData.y < height) { imgData.imgStr->getLine(); @@ -2470,15 +2615,17 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, #if SPLASH_CMYK GfxCMYK cmyk; #endif + static Guchar bitToByte[2] = {0x00, 0xff}; Guchar alpha; Guchar *maskPtr; - int maskBit; + int maskShift; int nComps, x; - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + memset(alphaLine, 0, imgData->width); return gFalse; } @@ -2486,15 +2633,13 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, maskPtr = imgData->mask->getDataPtr() + imgData->y * imgData->mask->getRowSize(); - maskBit = 0x80; + maskShift = 7; for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { - alpha = (*maskPtr & maskBit) ? 0xff : 0x00; - if (!(maskBit >>= 1)) { - ++maskPtr; - maskBit = 0x80; - } + alpha = bitToByte[(*maskPtr >> maskShift) & 1]; + maskPtr += (8 - maskShift) >> 3; + maskShift = (maskShift - 1) & 7; if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: @@ -2555,7 +2700,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, - int maskHeight, GBool maskInvert) { + int maskHeight, GBool maskInvert, + GBool interpolate) { GfxImageColorMap *maskColorMap; Object maskDecode, decodeLow, decodeHigh; double *ctm; @@ -2577,6 +2723,10 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), NULL); + ctm = state->getCTM(); + reduceImageResolution(str, ctm, &width, &height); + reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight); + // If the mask is higher resolution than the image, use // drawSoftMaskedImage() instead. if (maskWidth > width || maskHeight > height) { @@ -2589,7 +2739,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, new GfxDeviceGrayColorSpace()); maskDecode.free(); drawSoftMaskedImage(state, ref, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskColorMap); + maskStr, maskWidth, maskHeight, maskColorMap, + interpolate); delete maskColorMap; } else { @@ -2610,19 +2761,20 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, imgMaskData.y = 0; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse); maskSplash = new Splash(maskBitmap, gFalse); + maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust()); maskColor[0] = 0; maskSplash->clear(maskColor); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); + // use "glyph mode" here to get the correct scaled size maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, - maskWidth, maskHeight, mat, gFalse); + maskWidth, maskHeight, mat, gTrue, interpolate); delete imgMaskData.imgStr; maskStr->close(); delete maskSplash; //----- draw the source image - ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; @@ -2685,11 +2837,13 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, if (colorMode == splashModeMono1) { srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; } else { srcMode = colorMode; } splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue, - width, height, mat); + width, height, mat, interpolate); delete maskBitmap; gfree(imgData.lookup); @@ -2703,7 +2857,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap) { + GfxImageColorMap *maskColorMap, + GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageData imgData; @@ -2711,7 +2866,6 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; - SplashColor maskColor; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK @@ -2731,6 +2885,9 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; + reduceImageResolution(str, ctm, &width, &height); + reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight); + //----- set up the soft mask imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, @@ -2753,10 +2910,10 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); maskSplash = new Splash(maskBitmap, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); + maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust()); + clearMaskRegion(state, maskSplash, 0, 0, 1, 1); maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse, - maskWidth, maskHeight, mat); + maskWidth, maskHeight, mat, interpolate); delete imgMaskData.imgStr; maskStr->close(); gfree(imgMaskData.lookup); @@ -2820,10 +2977,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, if (colorMode == splashModeMono1) { srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; } else { srcMode = colorMode; } - splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat); + splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, + interpolate); splash->setSoftMask(NULL); gfree(imgData.lookup); @@ -2831,6 +2991,98 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, str->close(); } +void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm, + int *width, int *height) { + double sw, sh; + int reduction; + + if (str->getKind() == strJPX && + *width * *height > 10000000) { + sw = (double)*width / (fabs(ctm[2]) + fabs(ctm[3])); + sh = (double)*height / (fabs(ctm[0]) + fabs(ctm[1])); + if (sw > 8 && sh > 8) { + reduction = 3; + } else if (sw > 4 && sh > 4) { + reduction = 2; + } else if (sw > 2 && sh > 2) { + reduction = 1; + } else { + reduction = 0; + } + if (reduction > 0) { + ((JPXStream *)str)->reduceResolution(reduction); + *width >>= reduction; + *height >>= reduction; + } + } +} + +void SplashOutputDev::clearMaskRegion(GfxState *state, + Splash *maskSplash, + double xMin, double yMin, + double xMax, double yMax) { + SplashBitmap *maskBitmap; + double xxMin, yyMin, xxMax, yyMax, xx, yy; + int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n; + Guchar *p; + + maskBitmap = maskSplash->getBitmap(); + xxMin = maskBitmap->getWidth(); + xxMax = 0; + yyMin = maskBitmap->getHeight(); + yyMax = 0; + state->transform(xMin, yMin, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMin, yMax, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMax, yMin, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMax, yMax, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + xxMinI = (int)floor(xxMin); + if (xxMinI < 0) { + xxMinI = 0; + } + xxMaxI = (int)ceil(xxMax); + if (xxMaxI > maskBitmap->getWidth()) { + xxMaxI = maskBitmap->getWidth(); + } + yyMinI = (int)floor(yyMin); + if (yyMinI < 0) { + yyMinI = 0; + } + yyMaxI = (int)ceil(yyMax); + if (yyMaxI > maskBitmap->getHeight()) { + yyMaxI = maskBitmap->getHeight(); + } + p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize(); + if (maskBitmap->getMode() == splashModeMono1) { + n = (xxMaxI + 7) / 8 - xxMinI / 8; + p += xxMinI / 8; + } else { + n = xxMaxI - xxMinI; + p += xxMinI; + } + if (xxMaxI > xxMinI) { + for (y = yyMinI; y < yyMaxI; ++y) { + memset(p, 0, n); + p += maskBitmap->getRowSize(); + } + } +} + void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, @@ -2921,7 +3173,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, //~ not yet for transparency groups // switch to the blending color space - if (forSoftMask && isolated && blendingColorSpace) { + if (forSoftMask && isolated && !knockout && blendingColorSpace) { if (blendingColorSpace->getMode() == csDeviceGray || blendingColorSpace->getMode() == csCalGray || (blendingColorSpace->getMode() == csICCBased && @@ -2948,6 +3200,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust(globalParams->getStrokeAdjust()); //~ Acrobat apparently copies at least the fill and stroke colors, and //~ maybe other state(?) -- but not the clipping path (and not sure //~ what else) @@ -2962,8 +3215,9 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, splash->clear(color, 0); } else { splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); - splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty); } + splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty, + !isolated, knockout); transpGroup->tBitmap = bitmap; state->shiftCTM(-tx, -ty); updateCTM(state, 0, 0, 0, 0, 0, 0); @@ -3022,7 +3276,7 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, #if SPLASH_CMYK GfxCMYK cmyk; #endif - double lum, lum2; + double backdrop, backdrop2, lum, lum2; int tx, ty, x, y; tx = transpGroupStack->tx; @@ -3030,11 +3284,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, tBitmap = transpGroupStack->tBitmap; // composite with backdrop color + backdrop = 0; if (!alpha && tBitmap->getMode() != splashModeMono1) { //~ need to correctly handle the case where no blending color //~ space is given tSplash = new Splash(tBitmap, vectorAntialias, transpGroupStack->origSplash->getScreen()); + tSplash->setStrokeAdjust(globalParams->getStrokeAdjust()); if (transpGroupStack->blendingColorSpace) { switch (tBitmap->getMode()) { case splashModeMono1: @@ -3042,12 +3298,16 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, break; case splashModeMono8: transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); + backdrop = colToDbl(gray); color[0] = colToByte(gray); tSplash->compositeBackground(color); break; case splashModeRGB8: case splashModeBGR8: transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb); + backdrop = 0.3 * colToDbl(rgb.r) + + 0.59 * colToDbl(rgb.g) + + 0.11 * colToDbl(rgb.b); color[0] = colToByte(rgb.r); color[1] = colToByte(rgb.g); color[2] = colToByte(rgb.b); @@ -3056,6 +3316,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, #if SPLASH_CMYK case splashModeCMYK8: transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk); + backdrop = (1 - colToDbl(cmyk.k)) + - 0.3 * colToDbl(cmyk.c) + - 0.59 * colToDbl(cmyk.m) + - 0.11 * colToDbl(cmyk.y); + if (backdrop < 0) { + backdrop = 0; + } color[0] = colToByte(cmyk.c); color[1] = colToByte(cmyk.m); color[2] = colToByte(cmyk.y); @@ -3067,10 +3334,15 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, delete tSplash; } } + if (transferFunc) { + transferFunc->transform(&backdrop, &backdrop2); + } else { + backdrop2 = backdrop; + } softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); - memset(softMask->getDataPtr(), 0, + memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5), softMask->getRowSize() * softMask->getHeight()); if (tx < softMask->getWidth() && ty < softMask->getHeight()) { p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; @@ -3198,12 +3470,19 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { Ref ref; SplashOutFontFileID *id; GfxFontLoc *fontLoc; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf; + FILE *extFontFile; + char blk[4096]; + int n; +#endif SplashFontFile *fontFile; SplashFont *fontObj; FoFiTrueType *ff; int *codeToGID; Unicode u; SplashCoord textMat[4]; + SplashCoord oblique; int cmap, i; for (i = 0; i < nBuiltinFonts; ++i) { @@ -3227,11 +3506,40 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { if (!(fontLoc = GfxFont::locateBase14Font(name))) { return NULL; } +#if LOAD_FONTS_FROM_MEM + fontBuf = NULL; + if (fontLoc->fontType == fontType1 || + fontLoc->fontType == fontTrueType) { + if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) { + delete fontLoc; + delete id; + return NULL; + } + fontBuf = new GString(); + while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) { + fontBuf->append(blk, n); + } + fclose(extFontFile); + } +#endif if (fontLoc->fontType == fontType1) { - fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(), - gFalse, winAnsiEncoding); + fontFile = fontEngine->loadType1Font(id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fontLoc->path->getCString(), + gFalse, +#endif + winAnsiEncoding); } else if (fontLoc->fontType == fontTrueType) { - if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) { +#if LOAD_FONTS_FROM_MEM + if (!(ff = FoFiTrueType::make(fontBuf->getCString(), + fontBuf->getLength(), + fontLoc->fontNum))) { +#else + if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(), + fontLoc->fontNum))) { +#endif delete fontLoc; delete id; return NULL; @@ -3259,9 +3567,14 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { } delete ff; fontFile = fontEngine->loadTrueTypeFont(id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else fontLoc->path->getCString(), + gFalse, +#endif fontLoc->fontNum, - gFalse, codeToGID, 256, NULL); + codeToGID, 256, NULL); } else { delete fontLoc; delete id; @@ -3274,10 +3587,12 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { } // create the scaled font + oblique = (SplashCoord) + ((SplashOutFontFileID *)fontFile->getID())->getOblique(); textMat[0] = (SplashCoord)textMatA[0]; textMat[1] = (SplashCoord)textMatA[1]; - textMat[2] = (SplashCoord)textMatA[2]; - textMat[3] = (SplashCoord)textMatA[3]; + textMat[2] = oblique * textMatA[0] + textMatA[2]; + textMat[3] = oblique * textMatA[1] + textMatA[3]; fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix()); return fontObj; |