diff options
4 files changed, 133 insertions, 187 deletions
diff --git a/src/java/org/apache/fop/pdf/AlphaRasterImage.java b/src/java/org/apache/fop/pdf/AlphaRasterImage.java index be476bdb2..0b83cc3d6 100644 --- a/src/java/org/apache/fop/pdf/AlphaRasterImage.java +++ b/src/java/org/apache/fop/pdf/AlphaRasterImage.java @@ -22,6 +22,8 @@ package org.apache.fop.pdf; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; import java.io.IOException; import java.io.OutputStream; @@ -137,18 +139,37 @@ public class AlphaRasterImage implements PDFImage { throw new UnsupportedOperationException( "Expected only one band/component for the alpha channel"); } + + //...and write the Raster line by line with a reusable buffer int dataType = alpha.getDataBuffer().getDataType(); - if (dataType != DataBuffer.TYPE_BYTE) { + if (dataType == DataBuffer.TYPE_BYTE) { + byte[] line = new byte[nbands * w]; + for (int y = 0; y < h; y++) { + alpha.getDataElements(0, y, w, 1, line); + out.write(line); + } + } else if (dataType == DataBuffer.TYPE_INT) { + //Is there an better way to get a 8bit raster from a TYPE_INT raster? + int shift = 24; + SampleModel sampleModel = alpha.getSampleModel(); + if (sampleModel instanceof SinglePixelPackedSampleModel) { + SinglePixelPackedSampleModel m = (SinglePixelPackedSampleModel)sampleModel; + shift = m.getBitOffsets()[0]; + } + int[] iline = new int[nbands * w]; + byte[] line = new byte[nbands * w]; + for (int y = 0; y < h; y++) { + alpha.getDataElements(0, y, w, 1, iline); + for (int i = 0; i < w; i++) { + line[i] = (byte)(iline[i] >> shift); + } + out.write(line); + } + } else { throw new UnsupportedOperationException("Unsupported DataBuffer type: " + alpha.getDataBuffer().getClass().getName()); } - //...and write the Raster line by line with a reusable buffer - byte[] line = new byte[nbands * w]; - for (int y = 0; y < h; y++) { - alpha.getDataElements(0, y, w, 1, line); - out.write(line); - } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index cd80a6797..d9fd614cc 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -59,6 +59,9 @@ public abstract class AbstractImageAdapter implements PDFImage { public AbstractImageAdapter(Image image, String key) { this.image = image; this.key = key; + if (log.isDebugEnabled()) { + log.debug("New ImageAdapter created for key: " + key); + } } /** {@inheritDoc} */ @@ -96,11 +99,16 @@ public abstract class AbstractImageAdapter implements PDFImage { pdfICCStream = cs.getICCStream(); } } else { - if (cs == null && "sRGB".equals(desc)) { + if (cs == null && desc.startsWith("sRGB")) { //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer cs = doc.getResources().getColorSpace("DefaultRGB"); } - pdfICCStream = cs.getICCStream(); + if (cs != null) { + pdfICCStream = cs.getICCStream(); + } else { + //DefaultRGB hasn't been mapped to sRGB + //(that's the case with a plain PDFGraphics2D) + } } } if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) { diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index fdc54b48f..864809ebe 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -41,7 +41,6 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; import java.awt.image.ImageObserver; import java.awt.image.Raster; @@ -65,6 +64,7 @@ import org.apache.batik.gvt.PatternPaint; import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageSize; +import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; import org.apache.xmlgraphics.image.loader.impl.ImageRendered; import org.apache.xmlgraphics.java2d.AbstractGraphics2D; @@ -86,7 +86,6 @@ import org.apache.fop.pdf.PDFGState; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFImageXObject; import org.apache.fop.pdf.PDFLink; -import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFPattern; import org.apache.fop.pdf.PDFResourceContext; @@ -94,6 +93,7 @@ import org.apache.fop.pdf.PDFResources; import org.apache.fop.pdf.PDFState; import org.apache.fop.pdf.PDFText; import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter; import org.apache.fop.render.pdf.ImageRawJPEGAdapter; import org.apache.fop.render.pdf.ImageRenderedAdapter; import org.apache.fop.util.ColorExt; @@ -143,10 +143,10 @@ public class PDFGraphics2D extends AbstractGraphics2D { protected int baseLevel = 0; /** - * The count of JPEG images added to document so they recieve + * The count of natively handled images added to document so they receive * unique keys. */ - protected int[] jpegCount = {0}; + protected int nativeCount = 0; /** * The current font information. @@ -232,7 +232,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { this.pageRef = g.pageRef; this.graphicsState = g.graphicsState; this.currentStream = g.currentStream; - this.jpegCount = g.jpegCount; + this.nativeCount = g.nativeCount; this.outputStream = g.outputStream; this.ovFontState = g.ovFontState; } @@ -401,44 +401,41 @@ public class PDFGraphics2D extends AbstractGraphics2D { } /** - * Add a JPEG image directly to the PDF document. - * This is used by the PDFImageElementBridge to draw a JPEG - * directly into the pdf document rather than converting the image into + * Add a natively handled image directly to the PDF document. + * This is used by the PDFImageElementBridge to draw a natively handled image + * (like JPEG or CCITT images) + * directly into the PDF document rather than converting the image into * a bitmap and increasing the size. * - * @param jpeg the jpeg image to draw + * @param image the image to draw * @param x the x position * @param y the y position * @param width the width to draw the image * @param height the height to draw the image */ - public void addJpegImage(ImageRawJPEG jpeg, float x, float y, + void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y, float width, float height) { preparePainting(); - // Need to include hash code as when invoked from FO you - // may have several 'independent' PDFGraphics2D so the - // count is not enough. - String key = "__AddJPEG_" + hashCode() + "_" + jpegCount[0]; - jpegCount[0]++; - PDFImage pdfimage = new ImageRawJPEGAdapter(jpeg, key); - PDFName imageName = this.pdfDoc.addImage(resourceContext, - pdfimage).getName(); - AffineTransform at = getTransform(); - double[] matrix = new double[6]; - at.getMatrix(matrix); - currentStream.write("q\n"); - if (!at.isIdentity()) { - concatMatrix(matrix); + String key = image.getInfo().getOriginalURI(); + if (key == null) { + // Need to include hash code as when invoked from FO you + // may have several 'independent' PDFGraphics2D so the + // count is not enough. + key = "__AddNative_" + hashCode() + "_" + nativeCount; + nativeCount++; } - Shape imclip = getClip(); - writeClip(imclip); - - currentStream.write("" + width + " 0 0 " - + (-height) + " " - + x + " " - + (y + height) + " cm\n" - + imageName + " Do\nQ\n"); - + + PDFImage pdfImage; + if (image instanceof ImageRawJPEG) { + pdfImage = new ImageRawJPEGAdapter((ImageRawJPEG)image, key); + } else if (image instanceof ImageRawCCITTFax) { + pdfImage = new ImageRawCCITTFaxAdapter((ImageRawCCITTFax)image, key); + } else { + throw new IllegalArgumentException( + "Unsupported Image subclass: " + image.getClass().getName()); + } + + PDFXObject xObject = this.pdfDoc.addImage(resourceContext, pdfImage); if (outputStream != null) { try { this.pdfDoc.output(outputStream); @@ -446,6 +443,10 @@ public class PDFGraphics2D extends AbstractGraphics2D { // ignore exception, will be thrown again later } } + + AffineTransform at = new AffineTransform(); + at.translate(x, y); + useXObject(xObject, at, width, height); } /** @@ -492,40 +493,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { BufferedImage.TYPE_INT_ARGB); } - /** - * Draws as much of the specified image as has already been scaled - * to fit inside the specified rectangle. - * <p> - * The image is drawn inside the specified rectangle of this - * graphics context's coordinate space, and is scaled if - * necessary. Transparent pixels do not affect whatever pixels - * are already there. - * <p> - * This method returns immediately in all cases, even if the - * entire image has not yet been scaled, dithered, and converted - * for the current output device. - * If the current output representation is not yet complete, then - * <code>drawImage</code> returns <code>false</code>. As more of - * the image becomes available, the process that draws the image notifies - * the image observer by calling its <code>imageUpdate</code> method. - * <p> - * A scaled version of an image will not necessarily be - * available immediately just because an unscaled version of the - * image has been constructed for this output device. Each size of - * the image may be cached separately and generated from the original - * data in a separate image production sequence. - * @param img the specified image to be drawn. - * @param x the <i>x</i> coordinate. - * @param y the <i>y</i> coordinate. - * @param width the width of the rectangle. - * @param height the height of the rectangle. - * @param observer object to be notified as more of - * the image is converted. - * @return true if the image was drawn - * @see java.awt.Image - * @see java.awt.image.ImageObserver - * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) - */ + /** {@inheritDoc} */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { preparePainting(); @@ -533,8 +501,9 @@ public class PDFGraphics2D extends AbstractGraphics2D { // the pdf document. If so, we just reuse the reference; // otherwise we have to build a FopImage and add it to the pdf // document - PDFXObject imageInfo = pdfDoc.getXObject("TempImage:" + img.toString()); - if (imageInfo == null) { + String key = "TempImage:" + img.toString(); + PDFXObject xObject = pdfDoc.getXObject(key); + if (xObject == null) { // OK, have to build and add a PDF image Dimension size = new Dimension(width, height); @@ -553,92 +522,14 @@ public class PDFGraphics2D extends AbstractGraphics2D { } g.dispose(); - final byte[] result = new byte[buf.getWidth() * buf.getHeight() * 3 /*for RGB*/]; - byte[] mask = new byte[buf.getWidth() * buf.getHeight()]; - boolean hasMask = false; - //boolean binaryMask = true; - - Raster raster = buf.getData(); - DataBuffer bd = raster.getDataBuffer(); - - int count = 0; - int maskpos = 0; - int[] iarray; - int i, j, val, alpha; - switch (bd.getDataType()) { - case DataBuffer.TYPE_INT: - int[][] idata = ((DataBufferInt)bd).getBankData(); - for (i = 0; i < idata.length; i++) { - iarray = idata[i]; - for (j = 0; j < iarray.length; j++) { - val = iarray[j]; - alpha = val >>> 24; - mask[maskpos++] = (byte)(alpha & 0xFF); - if (alpha != 255) { - hasMask = true; - } - result[count++] = (byte)((val >> 16) & 0xFF); - result[count++] = (byte)((val >> 8) & 0xFF); - result[count++] = (byte)((val) & 0xFF); - } - } - break; - default: - // error - break; - } - String ref = null; - if (hasMask) { - // if the mask is binary then we could convert it into a bitmask - BitmapImage fopimg = new BitmapImage("TempImageMask:" - + img.toString(), buf.getWidth(), - buf.getHeight(), mask, null); - fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY)); - PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg); - ref = xobj.referencePDF(); - - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } - } else { - mask = null; - } - - BitmapImage fopimg = new BitmapImage("TempImage:" - + img.toString(), buf.getWidth(), - buf.getHeight(), result, ref); - imageInfo = pdfDoc.addImage(resourceContext, fopimg); - //int xObjectNum = imageInfo.getXNumber(); - - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } + xObject = addRenderedImage(key, buf); } else { - resourceContext.getPDFResources().addXObject(imageInfo); + resourceContext.getPDFResources().addXObject(xObject); } - // now do any transformation required and add the actual image - // placement instance - AffineTransform at = getTransform(); - double[] matrix = new double[6]; - at.getMatrix(matrix); - currentStream.write("q\n"); - if (!at.isIdentity()) { - concatMatrix(matrix); - } - Shape imclip = getClip(); - writeClip(imclip); - currentStream.write("" + width + " 0 0 " + (-height) + " " + x - + " " + (y + height) + " cm\n" - + imageInfo.getName() + " Do\nQ\n"); + AffineTransform at = new AffineTransform(); + at.translate(x, y); + useXObject(xObject, at, width, height); return true; } @@ -1342,18 +1233,24 @@ public class PDFGraphics2D extends AbstractGraphics2D { /** {@inheritDoc} */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { - preparePainting(); String key = "TempImage:" + img.toString(); + drawInnerRenderedImage(key, img, xform); + } + + /** {@inheritDoc} */ + public void drawInnerRenderedImage(String key, RenderedImage img, AffineTransform xform) { + preparePainting(); PDFXObject xObject = pdfDoc.getXObject(key); if (xObject == null) { - ImageInfo info = new ImageInfo(null, "image/unknown"); - ImageSize size = new ImageSize(img.getWidth(), img.getHeight(), 72); - info.setSize(size); - ImageRendered imgRend = new ImageRendered(info, img, null); - ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key); - xObject = pdfDoc.addImage(resourceContext, adapter); + xObject = addRenderedImage(key, img); + } else { + resourceContext.getPDFResources().addXObject(xObject); } + useXObject(xObject, xform, img.getWidth(), img.getHeight()); + } + + private void useXObject(PDFXObject xObject, AffineTransform xform, float width, float height) { // now do any transformation required and add the actual image // placement instance currentStream.write("q\n"); @@ -1361,11 +1258,29 @@ public class PDFGraphics2D extends AbstractGraphics2D { Shape imclip = getClip(); writeClip(imclip); concatMatrix(xform); - currentStream.write("" + img.getWidth() + " 0 0 " + (-img.getHeight()) + " 0" - + " " + (img.getHeight()) + " cm\n" + String w = PDFNumber.doubleOut(width, DEC); + String h = PDFNumber.doubleOut(height, DEC); + currentStream.write("" + w + " 0 0 -" + h + " 0 " + h + " cm\n" + xObject.getName() + " Do\nQ\n"); } + private PDFXObject addRenderedImage(String key, RenderedImage img) { + ImageInfo info = new ImageInfo(null, "image/unknown"); + ImageSize size = new ImageSize(img.getWidth(), img.getHeight(), 72); + info.setSize(size); + ImageRendered imgRend = new ImageRendered(info, img, null); + ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key); + PDFXObject xObject = pdfDoc.addImage(resourceContext, adapter); + if (outputStream != null) { + try { + this.pdfDoc.output(outputStream); + } catch (IOException ioe) { + // ignore exception, will be thrown again later + } + } + return xObject; + } + /** {@inheritDoc} */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { diff --git a/src/java/org/apache/fop/svg/PDFImageElementBridge.java b/src/java/org/apache/fop/svg/PDFImageElementBridge.java index a2542e86f..c3698b837 100644 --- a/src/java/org/apache/fop/svg/PDFImageElementBridge.java +++ b/src/java/org/apache/fop/svg/PDFImageElementBridge.java @@ -39,6 +39,7 @@ import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; +import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; @@ -56,6 +57,7 @@ public class PDFImageElementBridge extends SVGImageElementBridge { private final ImageFlavor[] supportedFlavors = new ImageFlavor[] {ImageFlavor.RAW_JPEG, + ImageFlavor.RAW_CCITTFAX, ImageFlavor.GRAPHICS2D, ImageFlavor.XML_DOM}; /** @@ -90,9 +92,9 @@ public class PDFImageElementBridge extends SVGImageElementBridge { } } if (image instanceof ImageRawJPEG) { - ImageRawJPEG jpegImage = (ImageRawJPEG)image; - specializedNode = new PDFJpegNode(jpegImage, ctx, imageElement, purl); - + specializedNode = new LoaderImageNode(image, ctx, imageElement, purl); + } else if (image instanceof ImageRawCCITTFax) { + specializedNode = new LoaderImageNode(image, ctx, imageElement, purl); } else if (image instanceof ImageGraphics2D) { ImageGraphics2D g2dImage = (ImageGraphics2D)image; specializedNode = new Graphics2DNode(g2dImage); @@ -135,29 +137,29 @@ public class PDFImageElementBridge extends SVGImageElementBridge { /** - * A PDF jpeg node. - * This holds a jpeg image so that it can be drawn into + * An image node for natively handled Image instance. + * This holds a natively handled image so that it can be drawn into * the PDFGraphics2D. */ - public class PDFJpegNode extends AbstractGraphicsNode { + public class LoaderImageNode extends AbstractGraphicsNode { - private ImageRawJPEG jpeg; + private Image image; private BridgeContext ctx; private Element imageElement; private ParsedURL purl; private GraphicsNode origGraphicsNode = null; /** - * Create a new PDF JPEG node for drawing JPEG images - * into pdf graphics. - * @param j the JPEG image + * Create a new image node for drawing natively handled images + * into PDF graphics. + * @param image the JPEG image * @param ctx the bridge context * @param imageElement the SVG image element * @param purl the URL to the image */ - public PDFJpegNode(ImageRawJPEG j, BridgeContext ctx, + public LoaderImageNode(Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) { - this.jpeg = j; + this.image = image; this.ctx = ctx; this.imageElement = imageElement; this.purl = purl; @@ -175,9 +177,9 @@ public class PDFImageElementBridge extends SVGImageElementBridge { float x = 0; float y = 0; try { - float width = jpeg.getSize().getWidthPx(); - float height = jpeg.getSize().getHeightPx(); - pdfg.addJpegImage(jpeg, x, y, width, height); + float width = image.getSize().getWidthPx(); + float height = image.getSize().getHeightPx(); + pdfg.addNativeImage(image, x, y, width, height); } catch (Exception e) { ctx.getUserAgent().displayError(e); } @@ -203,8 +205,8 @@ public class PDFImageElementBridge extends SVGImageElementBridge { /** {@inheritDoc} */ public Rectangle2D getPrimitiveBounds() { return new Rectangle2D.Double(0, 0, - jpeg.getSize().getWidthPx(), - jpeg.getSize().getHeightPx()); + image.getSize().getWidthPx(), + image.getSize().getHeightPx()); } /** {@inheritDoc} */ |