From 6e93cc6532b03fd59b2cf91013cc191eedd6344c Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Tue, 23 Aug 2011 21:02:02 +0930 Subject: [PATCH 2/2] cairo: align strokes when Stroke Adjust is true and line width <= 1 If the stroke coordinates are not aligned, lines in cairo may be up to 1 pixel wider than the requested width. This is most obvious when the line width is 1 and the rendered line is 2 pixels. When Stroke Adjust is true, the PDF standard requires that stroke coordinates be adjusted to ensure the stroke width is within half a pixel of the requested width. If Stroke Adjust is enabled and the width is <= 1 pixel (the previous commit adjusts the width to be at least 1 pixel), use the method documented at http://www.cairographics.org/FAQ/#sharp_lines to align the coordinates to ensure the rendered width is 1 pixel. --- poppler/CairoOutputDev.cc | 40 +++++++++++++++++++++++++++++++++++----- poppler/CairoOutputDev.h | 3 +++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc index 272ae55..1071cba 100644 --- a/poppler/CairoOutputDev.cc +++ b/poppler/CairoOutputDev.cc @@ -157,6 +157,8 @@ CairoOutputDev::CairoOutputDev() { // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true stroke_adjust = globalParams->getStrokeAdjust(); + align_stroke_coords = gFalse; + adjusted_stroke_width = gFalse; } CairoOutputDev::~CairoOutputDev() { @@ -412,6 +414,7 @@ void CairoOutputDev::updateMiterLimit(GfxState *state) { void CairoOutputDev::updateLineWidth(GfxState *state) { LOG(printf ("line width: %f\n", state->getLineWidth())); + adjusted_stroke_width = gFalse; if (state->getLineWidth() == 0.0) { /* find out how big pixels (device unit) are in the x and y directions * choose the smaller of the two as our line width */ @@ -431,11 +434,12 @@ void CairoOutputDev::updateLineWidth(GfxState *state) { /* find out line width in device units */ cairo_user_to_device_distance(cairo, &x, &y); - if (x < 0.5 && y < 0.5) { + if (x <= 1.0 && y <= 1.0) { /* adjust width to at least one device pixel */ - x = y = 0.5; + x = y = 1.0; cairo_device_to_user_distance(cairo, &x, &y); width = MIN(fabs(x),fabs(y)); + adjusted_stroke_width = gTrue; } } cairo_set_line_width (cairo, width); @@ -632,6 +636,15 @@ void CairoOutputDev::updateFont(GfxState *state) { cairo_set_font_matrix (cairo, &matrix); } +void CairoOutputDev::alignStrokeCoords(double *x, double *y) +{ + /* see http://www.cairographics.org/FAQ/#sharp_lines */ + cairo_user_to_device (cairo, x, y); + *x = floor(*x) + 0.5; + *y = floor(*y) + 0.5; + cairo_device_to_user (cairo, x, y); +} + void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { GfxSubpath *subpath; int i, j; @@ -639,8 +652,15 @@ void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { - cairo_move_to (cairo, subpath->getX(0), subpath->getY(0)); - j = 1; + if (align_stroke_coords) { + double x = subpath->getX(0); + double y = subpath->getY(0); + alignStrokeCoords(&x, &y); + cairo_move_to (cairo, x, y); + } else { + cairo_move_to (cairo, subpath->getX(0), subpath->getY(0)); + } + j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { cairo_curve_to( cairo, @@ -650,7 +670,14 @@ void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { j += 3; } else { - cairo_line_to (cairo, subpath->getX(j), subpath->getY(j)); + if (align_stroke_coords) { + double x = subpath->getX(j); + double y = subpath->getY(j); + alignStrokeCoords(&x, &y); + cairo_line_to (cairo, x, y); + } else { + cairo_line_to (cairo, subpath->getX(j), subpath->getY(j)); + } ++j; } } @@ -670,7 +697,10 @@ void CairoOutputDev::stroke(GfxState *state) { return; } + if (adjusted_stroke_width) + align_stroke_coords = gTrue; doPath (cairo, state, state->getPath()); + align_stroke_coords = gFalse; cairo_set_source (cairo, stroke_pattern); LOG(printf ("stroke\n")); cairo_stroke (cairo); diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h index 7cf4157..effa9dd 100644 --- a/poppler/CairoOutputDev.h +++ b/poppler/CairoOutputDev.h @@ -282,12 +282,15 @@ protected: GBool getStreamData (Stream *str, char **buffer, int *length); void setMimeData(Stream *str, Object *ref, cairo_surface_t *image); void fillToStrokePathClip(); + void alignStrokeCoords(double *x, double *y); GfxRGB fill_color, stroke_color; cairo_pattern_t *fill_pattern, *stroke_pattern; double fill_opacity; double stroke_opacity; GBool stroke_adjust; + GBool adjusted_stroke_width; + GBool align_stroke_coords; CairoFont *currentFont; struct StrokePathClip { -- 1.7.4.1