You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2014/05/06 18:11:01 UTC
svn commit: r1592793 - in /pdfbox/branches/1.8/pdfbox/src:
main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java
test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java
Author: tilman
Date: Tue May 6 16:11:01 2014
New Revision: 1592793
URL: http://svn.apache.org/r1592793
Log:
PDFBOX-2057: fix masks for TYPE_4BYTE_ABGR and TYPE_INT_ARGB, improve grayscale images, more tests
Modified:
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java
pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java
Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java?rev=1592793&r1=1592792&r2=1592793&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java Tue May 6 16:11:01 2014
@@ -16,21 +16,21 @@
*/
package org.apache.pdfbox.pdmodel.graphics.xobject;
-import java.awt.AlphaComposite;
import java.awt.Color;
-import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
-import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBufferInt;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.ByteOrder;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import org.apache.commons.logging.Log;
@@ -97,14 +97,14 @@ public class PDPixelMap extends PDXObjec
*
* @param doc The PDF document to embed the image in.
* @param bi The image to read data from.
- * @param mask true if this is a mask, false if not.
+ * @param isMask true if this is a mask, false if not.
*
* @throws IOException If there is an error while embedding this image.
*/
- private PDPixelMap(PDDocument doc, BufferedImage bi, boolean mask) throws IOException
+ private PDPixelMap(PDDocument doc, BufferedImage bi, boolean isMask) throws IOException
{
super(doc, PNG);
- createImageStream(doc, bi, mask);
+ createImageStream(doc, bi, isMask);
}
/**
@@ -112,29 +112,68 @@ public class PDPixelMap extends PDXObjec
*
* @param doc The PDF document to embed the image in.
* @param bi The image to read data from.
- * @param mask true if this is a mask, false if not. If true, the image
+ * @param isMask true if this is a mask, false if not. If true, the image
* stream will be forced to be DeviceGray.
*
* @throws IOException If there is an error while embedding this image.
*/
- private void createImageStream(PDDocument doc, BufferedImage bi, boolean mask) throws IOException
+ private void createImageStream(PDDocument doc, BufferedImage bi, boolean isMask) throws IOException
{
BufferedImage alphaImage = null;
- BufferedImage rgbImage = null;
+ BufferedImage rgbImage;
int width = bi.getWidth();
int height = bi.getHeight();
if (bi.getColorModel().hasAlpha())
{
// extract the alpha information
WritableRaster alphaRaster = bi.getAlphaRaster();
- ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
- false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
- alphaImage = new BufferedImage(cm, alphaRaster, false, null);
- // create a RGB image without alpha
+ DataBuffer dbSrc = alphaRaster.getDataBuffer();
+ if (dbSrc instanceof DataBufferInt)
+ {
+ // PDFBOX-2057, handle TYPE_INT_A... types
+ // See also
+ // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4243485
+
+ alphaImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ DataBuffer dbDst = alphaImage.getRaster().getDataBuffer();
+ // alpha value is in the highest byte
+ for (int i = 0; i < dbSrc.getSize(); ++i)
+ {
+ dbDst.setElem(i, dbSrc.getElem(i) >>> 24);
+ }
+ }
+ else if (dbSrc instanceof DataBufferByte)
+ {
+ alphaImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ DataBuffer dbDst = alphaImage.getRaster().getDataBuffer();
+ // alpha value is at bytes 0...4...8...
+ for (int i = 0; i < dbDst.getSize(); ++i)
+ {
+ dbDst.setElem(i, dbSrc.getElem(i << 2));
+ }
+ }
+ else
+ {
+ // fallback to old solution.
+ // This didn't work for INT types, see PDFBOX-2057.
+ ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
+ false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+ alphaImage = new BufferedImage(cm, alphaRaster, false, null);
+ }
+
+ // create RGB image without alpha
+ //BEWARE: the previous solution in the history
+ // g.setComposite(AlphaComposite.Src) and g.drawImage()
+ // didn't work properly for TYPE_4BYTE_ABGR.
+ // alpha values of 0 result in a black dest pixel!!!
rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
- Graphics2D g = rgbImage.createGraphics();
- g.setComposite(AlphaComposite.Src);
- g.drawImage(bi, 0, 0, null);
+ for (int x = 0; x < width; ++x)
+ {
+ for (int y = 0; y < height; ++y)
+ {
+ rgbImage.setRGB(x, y, bi.getRGB(x, y) & 0xFFFFFF);
+ }
+ }
}
else
{
@@ -148,35 +187,55 @@ public class PDPixelMap extends PDXObjec
{
throw new IllegalStateException();
}
-
+
int bpc;
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- //TODO: activate this when DeviceGray tests work
-// if (((mask || bi.getType() == BufferedImage.TYPE_BYTE_GRAY) ||
-// || bi.getType() == BufferedImage.TYPE_BYTE_BINARY)
-// && bi.getColorModel().getPixelSize() <= 8)
- if (mask || // PDFBOX-2057 force masks to be gray
- (bi.getType() == BufferedImage.TYPE_BYTE_BINARY
- && bi.getColorModel().getPixelSize() <= 8))
+ // use FlateDecode compression
+ getPDStream().addCompression();
+ os = getCOSStream().createUnfilteredStream();
+
+ if (((isMask
+ || bi.getType() == BufferedImage.TYPE_BYTE_GRAY)
+ || bi.getType() == BufferedImage.TYPE_BYTE_BINARY)
+ && bi.getColorModel().getPixelSize() <= 8)
{
setColorSpace(new PDDeviceGray());
- MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(bos);
-
- // grayscale images need one color per sample
- bpc = bi.getColorModel().getPixelSize();
int h = rgbImage.getHeight();
int w = rgbImage.getWidth();
- for (int y = 0; y < h; ++y)
+ bpc = bi.getColorModel().getPixelSize();
+ if (bpc < 8)
{
- for (int x = 0; x < w; ++x)
+ MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(os);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ // grayscale images need one color per sample
+ mcios.writeBits(bi.getRGB(x, y) & 0xFF, bpc);
+ }
+ }
+ // padding
+ while (mcios.getBitOffset() != 0)
{
- mcios.writeBits(rgbImage.getRGB(x, y), bpc);
+ mcios.writeBit(0);
+ }
+ mcios.flush();
+ mcios.close();
+ }
+ else
+ {
+ //BEWARE: the gray values must be extracted from raster
+ // and not from getRGB or the TYPE_BYTE_GRAY tests will fail
+ DataBuffer dataBuffer = rgbImage.getData().getDataBuffer();
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ // grayscale images need one color per sample
+ os.write(dataBuffer.getElem(y * w + x));
+ }
}
}
- mcios.writeBits(0, 7); // padding
- mcios.flush();
- mcios.close();
}
else
{
@@ -189,31 +248,26 @@ public class PDPixelMap extends PDXObjec
{
for (int x = 0; x < w; ++x)
{
+ // rgb images need three colors per sample
Color color = new Color(rgbImage.getRGB(x, y));
- bos.write(color.getRed());
- bos.write(color.getGreen());
- bos.write(color.getBlue());
+ os.write(color.getRed());
+ os.write(color.getGreen());
+ os.write(color.getBlue());
}
}
- }
-
- // add FlateDecode compression
- getPDStream().addCompression();
- os = getCOSStream().createUnfilteredStream();
- os.write(bos.toByteArray());
-
+ }
COSDictionary dic = getCOSStream();
- dic.setItem( COSName.FILTER, COSName.FLATE_DECODE );
- dic.setItem( COSName.SUBTYPE, COSName.IMAGE);
- dic.setItem( COSName.TYPE, COSName.XOBJECT );
- if(alphaImage != null)
+ dic.setItem(COSName.FILTER, COSName.FLATE_DECODE);
+ dic.setItem(COSName.SUBTYPE, COSName.IMAGE);
+ dic.setItem(COSName.TYPE, COSName.XOBJECT);
+ if (alphaImage != null)
{
PDPixelMap smask = new PDPixelMap(doc, alphaImage, true);
dic.setItem(COSName.SMASK, smask);
}
- setBitsPerComponent( bpc );
- setHeight( height );
- setWidth( width );
+ setBitsPerComponent(bpc);
+ setHeight(height);
+ setWidth(width);
}
finally
{
Modified: pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java?rev=1592793&r1=1592792&r2=1592793&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMapTest.java Tue May 6 16:11:01 2014
@@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.pdfbox.pdmodel.graphics.xobject;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.net.URL;
import javax.imageio.ImageIO;
import junit.framework.TestCase;
import org.apache.pdfbox.exceptions.COSVisitorException;
@@ -37,7 +38,7 @@ import org.apache.pdfbox.util.ImageIOUti
*/
public class PDPixelMapTest extends TestCase
{
- private final File testResultsDir = new File("target/test-output");
+ private final File testResultsDir = new File("target/test-output/graphics");
@Override
protected void setUp() throws Exception
@@ -57,7 +58,7 @@ public class PDPixelMapTest extends Test
BufferedImage image = ImageIO.read(this.getClass().getResourceAsStream("png.png"));
PDXObjectImage ximage = new PDPixelMap(document, image);
- validate(ximage, 8, 344, 287, "png", PDDeviceRGB.NAME);
+ validate(ximage, 8, image.getWidth(), image.getHeight(), "png", PDDeviceRGB.NAME);
checkIdent(image, ximage.getRGBImage());
// Create a grayscale image
@@ -67,7 +68,7 @@ public class PDPixelMapTest extends Test
g.dispose();
ximage = new PDPixelMap(document, grayImage);
- validate(ximage, 8, 344, 287, "png", PDDeviceRGB.NAME);
+ validate(ximage, 8, grayImage.getWidth(), grayImage.getHeight(), "png", PDDeviceGray.NAME);
checkIdent(grayImage, ximage.getRGBImage());
// Create a bitonal image
@@ -78,7 +79,28 @@ public class PDPixelMapTest extends Test
ximage = new PDPixelMap(document, bitonalImage);
checkIdent(bitonalImage, ximage.getRGBImage());
- validate(ximage, 1, 344, 287, "png", PDDeviceGray.NAME);
+ validate(ximage, 1, bitonalImage.getWidth(), bitonalImage.getHeight(), "png", PDDeviceGray.NAME);
+ document.close();
+ }
+
+ public void testCreateLosslessFromImageGray() throws IOException
+ {
+ PDDocument document = new PDDocument();
+ BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_BYTE_GRAY);
+ int w = image.getWidth();
+ int h = image.getHeight();
+ for (int x = 0; x < w; ++x)
+ {
+ for (int y = 0; y < h; ++y)
+ {
+ int color = ((x + y) / 2) & 0xFF;
+ color += (color << 8) + (color << 16);
+ image.setRGB(x, y, color);
+ }
+ }
+ PDXObjectImage ximage = new PDPixelMap(document, image);
+ validate(ximage, 8, w, h, "png", PDDeviceGray.NAME);
+ checkIdent(image, ximage.getRGBImage());
document.close();
}
@@ -94,7 +116,7 @@ public class PDPixelMapTest extends Test
PDXObjectImage ximage = new PDPixelMap(document, imageFromBitonalGif);
checkIdent(imageFromBitonalGif, ximage.getRGBImage());
- validate(ximage, 1, 344, 287, "png", PDDeviceGray.NAME);
+ validate(ximage, 1, imageFromBitonalGif.getWidth(), imageFromBitonalGif.getHeight(), "png", PDDeviceGray.NAME);
document.close();
}
@@ -102,29 +124,24 @@ public class PDPixelMapTest extends Test
* Tests RGB PDPixelMapTest() with TYPE_4BYTE_ABGR image.
*
* @throws java.io.IOException
+ * @throws org.apache.pdfbox.exceptions.COSVisitorException
*/
public void testCreateLossless4BYTE_ABGR() throws IOException, COSVisitorException
{
PDDocument document = new PDDocument();
- BufferedImage awtImage = new BufferedImage(300, 300, BufferedImage.TYPE_4BYTE_ABGR);
+ BufferedImage awtImage = createInterestingImage(BufferedImage.TYPE_4BYTE_ABGR);
- // draw something
- Graphics g = awtImage.getGraphics();
- g.setColor(Color.blue);
- g.fillRect(0, 0, 100, 300);
- g.setColor(Color.white);
- g.fillRect(100, 0, 100, 300);
- g.setColor(Color.red);
- g.fillRect(200, 0, 100, 300);
- g.setColor(Color.black);
- g.drawRect(0, 0, 299, 299);
- g.dispose();
+ for (int x = 0; x < awtImage.getWidth(); ++x)
+ {
+ for (int y = 0; y < awtImage.getHeight(); ++y)
+ {
+ awtImage.setRGB(x, y, (awtImage.getRGB(x, y) & 0xFFFFFF) | ((y / 10 * 10) << 24));
+ }
+ }
PDPixelMap ximage = new PDPixelMap(document, awtImage);
- validate(ximage, 8, 300, 300, "png", PDDeviceRGB.NAME);
- validate(ximage.getSMaskImage(), 8, 300, 300, "png", PDDeviceGray.NAME);
- assertEquals(ximage.getColorSpace().getName(), PDDeviceRGB.NAME);
- assertEquals(ximage.getSMaskImage().getColorSpace().getName(), PDDeviceGray.NAME);
+ validate(ximage, 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceRGB.NAME);
+ validate(ximage.getSMaskImage(), 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceGray.NAME);
checkIdent(awtImage, ximage.getRGBImage());
// This part isn't really needed because this test doesn't break
@@ -132,8 +149,9 @@ public class PDPixelMapTest extends Test
// if something goes wrong in the future and we want to have a PDF to open.
PDPage page = new PDPage();
document.addPage(page);
- PDPageContentStream contentStream = new PDPageContentStream(document, page);
+ PDPageContentStream contentStream = new PDPageContentStream(document, page, true, false);
contentStream.drawXObject(ximage, 150, 300, ximage.getWidth(), ximage.getHeight());
+ contentStream.drawXObject(ximage, 200, 350, ximage.getWidth(), ximage.getHeight());
contentStream.close();
File pdfFile = new File(testResultsDir, "4babgr.pdf");
document.save(pdfFile);
@@ -143,6 +161,110 @@ public class PDPixelMapTest extends Test
}
/**
+ * Tests RGB PDPixelMapTest() with TYPE_INT_ARGB image.
+ *
+ * @throws java.io.IOException
+ * @throws org.apache.pdfbox.exceptions.COSVisitorException
+ */
+ public void testCreateLosslessINT_ARGB() throws IOException, COSVisitorException
+ {
+ PDDocument document = new PDDocument();
+ BufferedImage awtImage = createInterestingImage(BufferedImage.TYPE_INT_ARGB);
+
+ for (int x = 0; x < awtImage.getWidth(); ++x)
+ {
+ for (int y = 0; y < awtImage.getHeight(); ++y)
+ {
+ awtImage.setRGB(x, y, (awtImage.getRGB(x, y) & 0xFFFFFF) | ((y / 10 * 10) << 24));
+ }
+ }
+
+ PDPixelMap ximage = new PDPixelMap(document, awtImage);
+ validate(ximage, 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceRGB.NAME);
+ validate(ximage.getSMaskImage(), 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceGray.NAME);
+ checkIdent(awtImage, ximage.getRGBImage());
+
+ // This part isn't really needed because this test doesn't break
+ // if the mask has the wrong colorspace (PDFBOX-2057), but it is still useful
+ // if something goes wrong in the future and we want to have a PDF to open.
+ PDPage page = new PDPage();
+ document.addPage(page);
+ PDPageContentStream contentStream = new PDPageContentStream(document, page, true, false);
+ contentStream.drawXObject(ximage, 150, 300, ximage.getWidth(), ximage.getHeight());
+ contentStream.drawXObject(ximage, 200, 350, ximage.getWidth(), ximage.getHeight());
+ contentStream.close();
+ File pdfFile = new File(testResultsDir, "intargb.pdf");
+ document.save(pdfFile);
+ document.close();
+ document = PDDocument.loadNonSeq(pdfFile, null);
+ document.close();
+ }
+
+ /**
+ * Tests RGB PDPixelMapTest() with TYPE_INT_RGB image.
+ *
+ * @throws java.io.IOException
+ * @throws org.apache.pdfbox.exceptions.COSVisitorException
+ */
+ public void testCreateLosslessINT_RGB() throws IOException, COSVisitorException
+ {
+ PDDocument document = new PDDocument();
+ BufferedImage awtImage = createInterestingImage(BufferedImage.TYPE_INT_RGB);
+
+ PDPixelMap ximage = new PDPixelMap(document, awtImage);
+ validate(ximage, 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceRGB.NAME);
+ assertNull(ximage.getSMaskImage());
+ checkIdent(awtImage, ximage.getRGBImage());
+
+ // This part isn't really needed because this test doesn't break
+ // if the mask has the wrong colorspace (PDFBOX-2057), but it is still useful
+ // if something goes wrong in the future and we want to have a PDF to open.
+ PDPage page = new PDPage();
+ document.addPage(page);
+ PDPageContentStream contentStream = new PDPageContentStream(document, page, true, false);
+ contentStream.drawXObject(ximage, 150, 300, ximage.getWidth(), ximage.getHeight());
+ contentStream.drawXObject(ximage, 200, 350, ximage.getWidth(), ximage.getHeight());
+ contentStream.close();
+ File pdfFile = new File(testResultsDir, "intrgb.pdf");
+ document.save(pdfFile);
+ document.close();
+ document = PDDocument.loadNonSeq(pdfFile, null);
+ document.close();
+ }
+
+ /**
+ * Tests RGB PDPixelMapTest() with TYPE_INT_BGR image.
+ *
+ * @throws java.io.IOException
+ * @throws org.apache.pdfbox.exceptions.COSVisitorException
+ */
+ public void testCreateLosslessINT_BGR() throws IOException, COSVisitorException
+ {
+ PDDocument document = new PDDocument();
+ BufferedImage awtImage = createInterestingImage(BufferedImage.TYPE_INT_BGR);
+
+ PDPixelMap ximage = new PDPixelMap(document, awtImage);
+ validate(ximage, 8, awtImage.getWidth(), awtImage.getHeight(), "png", PDDeviceRGB.NAME);
+ assertNull(ximage.getSMaskImage());
+ checkIdent(awtImage, ximage.getRGBImage());
+
+ // This part isn't really needed because this test doesn't break
+ // if the mask has the wrong colorspace (PDFBOX-2057), but it is still useful
+ // if something goes wrong in the future and we want to have a PDF to open.
+ PDPage page = new PDPage();
+ document.addPage(page);
+ PDPageContentStream contentStream = new PDPageContentStream(document, page, true, false);
+ contentStream.drawXObject(ximage, 150, 300, ximage.getWidth(), ximage.getHeight());
+ contentStream.drawXObject(ximage, 200, 350, ximage.getWidth(), ximage.getHeight());
+ contentStream.close();
+ File pdfFile = new File(testResultsDir, "intbgr.pdf");
+ document.save(pdfFile);
+ document.close();
+ document = PDDocument.loadNonSeq(pdfFile, null);
+ document.close();
+ }
+
+ /**
* Tests RGB PDPixelMapTest() with image from a color GIF
*
* @throws java.io.IOException
@@ -202,4 +324,21 @@ public class PDPixelMapTest extends Test
}
}
}
+
+ private BufferedImage createInterestingImage(int type)
+ {
+ BufferedImage awtImage = new BufferedImage(256, 256, type);
+ Graphics g = awtImage.getGraphics();
+ g.setColor(Color.blue);
+ g.fillRect(0, 0, awtImage.getWidth() / 3, awtImage.getHeight() - 1);
+ g.setColor(Color.white);
+ g.fillRect(awtImage.getWidth() / 3, 0, awtImage.getWidth() / 3, awtImage.getHeight() - 1);
+ g.setColor(Color.red);
+ g.fillRect(awtImage.getWidth() / 3 * 2, 0, awtImage.getWidth() / 3, awtImage.getHeight() - 1);
+ g.setColor(Color.black);
+ g.drawRect(0, 0, awtImage.getWidth() - 1, awtImage.getHeight() - 1);
+ g.dispose();
+ return awtImage;
+ }
+
}