diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc index 098e4a4..487c0cb 100644 --- a/poppler/GlobalParams.cc +++ b/poppler/GlobalParams.cc @@ -629,6 +629,7 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir) screenBlackThreshold = 0.0; screenWhiteThreshold = 1.0; minLineWidth = 0.0; + enhanceThinLines = gTrue; overprintPreview = gFalse; mapNumericCharNames = gTrue; mapUnknownCharNames = gFalse; @@ -2163,6 +2164,12 @@ void GlobalParams::setMinLineWidth(double minLineWidthA) unlockGlobalParams; } +void GlobalParams::setEnhanceThinLines(GBool enhanceThinLinesA) { + lockGlobalParams; + enhanceThinLines = enhanceThinLinesA; + unlockGlobalParams; +} + void GlobalParams::setOverprintPreview(GBool overprintPreviewA) { lockGlobalParams; overprintPreview = overprintPreviewA; diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h index bc11684..0eb5cd1 100644 --- a/poppler/GlobalParams.h +++ b/poppler/GlobalParams.h @@ -186,6 +186,7 @@ public: double getScreenBlackThreshold(); double getScreenWhiteThreshold(); double getMinLineWidth(); + GBool getEnhanceThinLines() { return enhanceThinLines; } GBool getOverprintPreview() { return overprintPreview; } GBool getMapNumericCharNames(); GBool getMapUnknownCharNames(); @@ -241,6 +242,7 @@ public: void setScreenBlackThreshold(double blackThreshold); void setScreenWhiteThreshold(double whiteThreshold); void setMinLineWidth(double minLineWidth); + void setEnhanceThinLines(GBool enhanceThinLinesA); void setOverprintPreview(GBool overprintPreviewA); void setMapNumericCharNames(GBool map); void setMapUnknownCharNames(GBool map); @@ -343,6 +345,7 @@ private: double screenBlackThreshold; // screen black clamping threshold double screenWhiteThreshold; // screen white clamping threshold double minLineWidth; // minimum line width + GBool enhanceThinLines; // enhance thin lines GBool overprintPreview; // enable overprint preview GBool mapNumericCharNames; // map numeric char names (from font subsets)? GBool mapUnknownCharNames; // map unknown char names? diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 9e07060..7a27a9f 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -1226,6 +1226,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnhanceThinLines(globalParams->getEnhanceThinLines()); splash->clear(paperColor, 0); fontEngine = NULL; @@ -1366,6 +1367,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { } splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnhanceThinLines(globalParams->getEnhanceThinLines()); if (state) { ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; @@ -2523,6 +2525,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, color[0] = 0xff; } splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnhanceThinLines(globalParams->getEnhanceThinLines()); splash->setFillPattern(new SplashSolidColor(color)); splash->setStrokePattern(new SplashSolidColor(color)); //~ this should copy other state from t3GlyphStack->origSplash? @@ -3680,6 +3683,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnhanceThinLines(globalParams->getEnhanceThinLines()); //~ Acrobat apparently copies at least the fill and stroke colors, and //~ maybe other state(?) -- but not the clipping path (and not sure //~ what else) @@ -4054,6 +4058,7 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *ca } splash = new Splash(bitmap, gTrue); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnhanceThinLines(globalParams->getEnhanceThinLines()); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; diff --git a/splash/Splash.cc b/splash/Splash.cc index e6559f4..83c8546 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -1311,7 +1311,7 @@ inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, } } -inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { +inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine, Guchar lineOpacity) { #if splashAASize == 4 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; @@ -1354,7 +1354,7 @@ inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { #endif if (t != 0) { - pipe->shape = aaGamma[t]; + pipe->shape = (adjustLine) ? div255((int) lineOpacity * aaGamma[t]) : aaGamma[t]; (this->*pipe->run)(pipe); updateModX(x); updateModY(y); @@ -1403,6 +1403,7 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, aaBuf = NULL; } minLineWidth = 0; + enhanceThinLines = gTrue; clearModRegion(); debugMode = gFalse; } @@ -1429,6 +1430,7 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, aaBuf = NULL; } minLineWidth = 0; + enhanceThinLines = gTrue; clearModRegion(); debugMode = gFalse; } @@ -2212,6 +2214,8 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashXPathScanner *scanner; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes, clipRes2; + GBool adjustLine = gFalse; + int linePosI = 0; if (path->length == 0) { return splashErrEmptyPath; @@ -2244,7 +2248,24 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, } } - xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + if (state->strokeAdjust) { + if (state->clip->getXMinI() == state->clip->getXMaxI()) { + linePosI = state->clip->getXMinI(); + adjustLine = gTrue; + } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getXMin() + state->lineWidth); + } else if (state->clip->getYMinI() == state->clip->getYMaxI()) { + linePosI = state->clip->getYMinI(); + adjustLine = gTrue; + } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getYMin() + state->lineWidth); + } + } + + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue, + adjustLine, linePosI); if (vectorAntialias && !inShading) { xPath->aaScale(); } @@ -2277,11 +2298,13 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, // draw the spans if (vectorAntialias && !inShading) { for (y = yMinI; y <= yMaxI; ++y) { - scanner->renderAALine(aaBuf, &x0, &x1, y); + scanner->renderAALine(aaBuf, &x0, &x1, y, xMinI == xMaxI); if (clipRes != splashClipAllInside) { - state->clip->clipAALine(aaBuf, &x0, &x1, y); + state->clip->clipAALine(aaBuf, &x0, &x1, y, xMinI == xMaxI); } - drawAALine(&pipe, x0, x1, y); + drawAALine(&pipe, x0, x1, y, + !enhanceThinLines && (xMinI == xMaxI || yMinI == yMaxI), + clip255(splashRound(state->lineWidth * 255))); } } else { for (y = yMinI; y <= yMaxI; ++y) { diff --git a/splash/Splash.h b/splash/Splash.h index f4fb542..b2d2837 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -243,6 +243,9 @@ public: // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } + // enhance thin lines. + void setEnhanceThinLines(GBool enhance) { enhanceThinLines = enhance; } + // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) @@ -300,7 +303,7 @@ private: void drawAAPixelInit(); void drawAAPixel(SplashPipe *pipe, int x, int y); void drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); - void drawAALine(SplashPipe *pipe, int x0, int x1, int y); + void drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine = gFalse, Guchar lineOpacity = 0); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); @@ -397,6 +400,7 @@ private: int alpha0X, alpha0Y; // offset within alpha0Bitmap SplashCoord aaGamma[splashAASize * splashAASize + 1]; SplashCoord minLineWidth; + GBool enhanceThinLines; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc index 41b73c8..c3e2766 100644 --- a/splash/SplashClip.cc +++ b/splash/SplashClip.cc @@ -334,7 +334,7 @@ SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { return splashClipAllInside; } -void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { +void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, GBool adjustVertLine) { int xx0, xx1, xx, yy, i; SplashColorPtr p; @@ -351,7 +351,7 @@ void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { for (xx = xx0; xx + 7 < xx1; xx += 8) { *p++ = 0; } - if (xx < xx1) { + if (xx < xx1 && !adjustVertLine) { *p &= 0xff >> (xx1 & 7); } } @@ -364,7 +364,7 @@ void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { xx0 = 0; } xx1 = (*x1 + 1) * splashAASize; - if (xx0 < xx1) { + if (xx0 < xx1 && !adjustVertLine) { for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); xx = xx0; diff --git a/splash/SplashClip.h b/splash/SplashClip.h index 3eb2d78..483601a 100644 --- a/splash/SplashClip.h +++ b/splash/SplashClip.h @@ -116,7 +116,8 @@ public: // Clips an anti-aliased line by setting pixels to zero. On entry, // all non-zero pixels are between and . This function // will update and . - void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, + GBool adjustVertLine = gFalse); // Get the rectangle part of the clip region. SplashCoord getXMin() { return xMin; } diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index b82d355..37e08d5 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -66,7 +66,8 @@ inline void SplashXPath::transform(SplashCoord *matrix, //------------------------------------------------------------------------ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool closeSubpaths) { + SplashCoord flatness, GBool closeSubpaths, + GBool adjustLines, int linePosI) { SplashPathHint *hint; SplashXPathPoint *pts; SplashXPathAdjust *adjusts, *adjust; @@ -127,7 +128,15 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, x0 = splashRound(adj0); x1 = splashRound(adj1); if (x1 == x0) { - x1 = x1 + 1; + if (adjustLines) { + // the adjustment moves thin lines (clip rectangle with + // empty width or height) out of clip area, here we need + // a special adjustment: + x0 = linePosI; + x1 = x0 + 1; + } else { + x1 = x1 + 1; + } } adjusts[i].x0 = (SplashCoord)x0; adjusts[i].x1 = (SplashCoord)x1 - 0.01; diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index db06978..ec082f6 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -50,7 +50,8 @@ public: // space, via . If is true, closes all open // subpaths. SplashXPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool closeSubpaths); + SplashCoord flatness, GBool closeSubpaths, + GBool adjustLines = gFalse, int linePosI = 0); // Copy an expanded path. SplashXPath *copy() { return new SplashXPath(this); } diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index c9fe5e5..4bbffc3 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -367,7 +367,7 @@ void SplashXPathScanner::addIntersection(double segYMin, double segYMax, } void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, - int *x0, int *x1, int y) { + int *x0, int *x1, int y, GBool adjustVertLine) { int xx0, xx1, xx, xxMin, xxMax, yy, interEnd; Guchar mask; SplashColorPtr p; @@ -418,8 +418,8 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, xx = xx0; p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); if (xx & 7) { - mask = 0xff >> (xx & 7); - if ((xx & ~7) == (xx1 & ~7)) { + mask = adjustVertLine ? 0xff : 0xff >> (xx & 7); + if (!adjustVertLine && (xx & ~7) == (xx1 & ~7)) { mask &= (Guchar)(0xff00 >> (xx1 & 7)); } *p++ |= mask; @@ -429,7 +429,7 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, *p++ |= 0xff; } if (xx < xx1) { - *p |= (Guchar)(0xff00 >> (xx1 & 7)); + *p |= adjustVertLine ? 0xff : (Guchar)(0xff00 >> (xx1 & 7)); } } if (xx0 < xxMin) { diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 719fae4..211a67d 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -60,7 +60,8 @@ public: // Renders one anti-aliased line into . Returns the min and // max x coordinates with non-zero pixels in and . - void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, + GBool adjustVertLine = gFalse); // Clips an anti-aliased line by setting pixels to zero. On entry, // all non-zero pixels are between and . This function