Author: tilman Date: Wed Feb 14 16:37:07 2018 New Revision: 1824259 URL: http://svn.apache.org/viewvc?rev=1824259&view=rev Log: PDFBOX-4095, PDFBOX-3000: to solve the problem that some blend modes don't work on the page background, render pages that have blending on a temporary transparent ARGB image and draw that one on the target image initialized with a white background, as suggested by Jani Pehkonen
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java?rev=1824259&r1=1824258&r2=1824259&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java Wed Feb 14 16:37:07 2018 @@ -20,9 +20,13 @@ import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; +import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.blend.BlendMode; +import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * Renders a PDF document to an AWT BufferedImage. @@ -114,20 +118,30 @@ public class PDFRenderer int heightPx = Math.round(heightPt * scale); int rotationAngle = page.getRotation(); + int bimType = imageType.toBufferedImageType(); + if (imageType != ImageType.ARGB && hasBlendMode(page)) + { + // PDFBOX-4095: if the PDF has blending on the top level, draw on transparent background + // Inpired from PDF.js: if a PDF page uses any blend modes other than Normal, + // PDF.js renders everything on a fully transparent RGBA canvas. + // Finally when the page has been rendered, PDF.js draws the RGBA canvas on a white canvas. + bimType = BufferedImage.TYPE_INT_ARGB; + } + // swap width and height BufferedImage image; if (rotationAngle == 90 || rotationAngle == 270) { - image = new BufferedImage(heightPx, widthPx, imageType.toBufferedImageType()); + image = new BufferedImage(heightPx, widthPx, bimType); } else { - image = new BufferedImage(widthPx, heightPx, imageType.toBufferedImageType()); + image = new BufferedImage(widthPx, heightPx, bimType); } - // use a transparent background if the imageType supports alpha + // use a transparent background if the image type supports alpha Graphics2D g = image.createGraphics(); - if (imageType == ImageType.ARGB) + if (image.getType() == BufferedImage.TYPE_INT_ARGB) { g.setBackground(new Color(0, 0, 0, 0)); } @@ -146,6 +160,19 @@ public class PDFRenderer g.dispose(); + if (image.getType() != imageType.toBufferedImageType()) + { + // PDFBOX-4095: draw temporary transparent image on white background + BufferedImage newImage = + new BufferedImage(image.getWidth(), image.getHeight(), imageType.toBufferedImageType()); + Graphics2D dstGraphics = newImage.createGraphics(); + dstGraphics.setBackground(Color.WHITE); + dstGraphics.clearRect(0, 0, image.getWidth(), image.getHeight()); + dstGraphics.drawImage(image, 0, 0, null); + dstGraphics.dispose(); + image = newImage; + } + return image; } @@ -224,4 +251,30 @@ public class PDFRenderer { return new PageDrawer(parameters); } + + private boolean hasBlendMode(PDPage page) + { + // check the current resources for blend modes + PDResources resources = page.getResources(); + if (resources == null) + { + return false; + } + for (COSName name : resources.getExtGStateNames()) + { + PDExtendedGraphicsState extGState = resources.getExtGState(name); + if (extGState == null) + { + // can happen if key exists but no value + // see PDFBOX-3950-23EGDHXSBBYQLKYOKGZUOVYVNE675PRD.pdf + continue; + } + BlendMode blendMode = extGState.getBlendMode(); + if (blendMode != BlendMode.NORMAL) + { + return true; + } + } + return false; + } }