From 4f5f538f03515d88482e940906bda425c801f78d Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Tue, 24 Jan 2006 20:02:30 +0000 Subject: [PATCH] Following the model of Graphics2DAdapter, added a new interface ImageAdapter which can optionally be implemented by Renderers to support painting of RenderedImage or BufferedImage instances from inside XMLHandler implementations. First implementation by the PSRenderer. Barcode4J is going to be extended to use this interface so barcode support can easily be added to the AFP Renderer without having a Graphics2D implementation at first. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@372004 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/render/AbstractRenderer.java | 5 + .../org/apache/fop/render/ImageAdapter.java | 46 ++++++ src/java/org/apache/fop/render/Renderer.java | 5 + .../apache/fop/render/ps/PSImageUtils.java | 142 ++++++++++++++++-- .../org/apache/fop/render/ps/PSRenderer.java | 23 ++- 5 files changed, 203 insertions(+), 18 deletions(-) create mode 100644 src/java/org/apache/fop/render/ImageAdapter.java diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index 850190aab..8dbaeaa20 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -164,6 +164,11 @@ public abstract class AbstractRenderer return null; } + /** @see org.apache.fop.render.Renderer#getImageAdapter() */ + public ImageAdapter getImageAdapter() { + return null; + } + /** @return the current PageViewport or null, if none is active */ protected PageViewport getCurrentPageViewport() { return this.currentPageViewport; diff --git a/src/java/org/apache/fop/render/ImageAdapter.java b/src/java/org/apache/fop/render/ImageAdapter.java new file mode 100644 index 000000000..e49741d05 --- /dev/null +++ b/src/java/org/apache/fop/render/ImageAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render; + +import java.awt.image.RenderedImage; +import java.io.IOException; + +/** + * This interface represents an optional feature that can be provided by + * a renderer. It is exposed by calling the getImageAdapter() method + * on the renderer. Renderers that support this feature allow painting + * of images (RendererImage instances). + */ +public interface ImageAdapter { + + /** + * Paints an image at the given position. + * @param image the image which will be painted + * @param context the renderer context for the current renderer + * @param x X position of the image + * @param y Y position of the image + * @param width width of the image + * @param height height of the image + * @throws IOException In case of an I/O error while writing the output format + */ + void paintImage(RenderedImage image, + RendererContext context, + int x, int y, int width, int height) throws IOException; + +} diff --git a/src/java/org/apache/fop/render/Renderer.java b/src/java/org/apache/fop/render/Renderer.java index da347e0e6..e71f33001 100644 --- a/src/java/org/apache/fop/render/Renderer.java +++ b/src/java/org/apache/fop/render/Renderer.java @@ -121,6 +121,11 @@ public interface Renderer { */ Graphics2DAdapter getGraphics2DAdapter(); + /** + * @return the adapter for painting RenderedImages (or null if not supported) + */ + ImageAdapter getImageAdapter(); + /** * This is called if the renderer supports out of order rendering. The * renderer should prepare the page so that a page further on in the set of diff --git a/src/java/org/apache/fop/render/ps/PSImageUtils.java b/src/java/org/apache/fop/render/ps/PSImageUtils.java index 991ea7396..bd3736eb5 100644 --- a/src/java/org/apache/fop/render/ps/PSImageUtils.java +++ b/src/java/org/apache/fop/render/ps/PSImageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 The Apache Software Foundation. + * Copyright 2004-2006 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,13 @@ package org.apache.fop.render.ps; +import java.awt.Dimension; import java.awt.color.ColorSpace; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.io.IOException; import java.io.OutputStream; @@ -64,23 +70,38 @@ public class PSImageUtils { return; } } - boolean iscolor = img.getColorSpace().getType() - != ColorSpace.CS_GRAY; byte[] imgmap; if (img.getBitmapsSize() > 0) { imgmap = img.getBitmaps(); } else { imgmap = img.getRessourceBytes(); } + + String imgName = img.getMimeType() + " " + img.getOriginalURI(); + Dimension imgDim = new Dimension(img.getWidth(), img.getHeight()); + Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h); + boolean isJPEG = (img instanceof JpegImage); + writeImage(imgmap, imgDim, imgName, targetRect, isJPEG, + img.getColorSpace(), gen); + } + + private static void writeImage(byte[] img, + Dimension imgDim, String imgName, + Rectangle2D targetRect, + boolean isJPEG, ColorSpace colorSpace, + PSGenerator gen) throws IOException { + boolean iscolor = colorSpace.getType() != ColorSpace.CS_GRAY; gen.saveGraphicsState(); - gen.writeln(x + " " + y + " translate"); - gen.writeln(w + " " + h + " scale"); + gen.writeln(gen.formatDouble(targetRect.getX()) + " " + + gen.formatDouble(targetRect.getY()) + " translate"); + gen.writeln(gen.formatDouble(targetRect.getWidth()) + " " + + gen.formatDouble(targetRect.getHeight()) + " scale"); - gen.commentln("%FOPBeginBitmap: " + img.getMimeType() + " " + img.getOriginalURI()); - if (img.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + gen.commentln("%FOPBeginBitmap: " + imgName); + if (colorSpace.getType() == ColorSpace.TYPE_CMYK) { gen.writeln("/DeviceCMYK setcolorspace"); - } else if (img.getColorSpace().getType() == ColorSpace.CS_GRAY) { + } else if (colorSpace.getType() == ColorSpace.CS_GRAY) { gen.writeln("/DeviceGray setcolorspace"); } else { gen.writeln("/DeviceRGB setcolorspace"); @@ -90,7 +111,7 @@ public class PSImageUtils { // Template: (RawData is used for the EOF signal only) // gen.write("/RawData currentfile filter def"); // gen.write("/Data RawData [...] def"); - if (img instanceof JpegImage) { + if (isJPEG) { gen.writeln("/RawData currentfile /ASCII85Decode filter def"); gen.writeln("/Data RawData << >> /DCTDecode filter def"); } else { @@ -104,10 +125,10 @@ public class PSImageUtils { } gen.writeln("<<"); gen.writeln(" /ImageType 1"); - gen.writeln(" /Width " + img.getWidth()); - gen.writeln(" /Height " + img.getHeight()); + gen.writeln(" /Width " + imgDim.width); + gen.writeln(" /Height " + imgDim.height); gen.writeln(" /BitsPerComponent 8"); - if (img.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + if (colorSpace.getType() == ColorSpace.TYPE_CMYK) { if (false /*TODO img.invertImage()*/) { gen.writeln(" /Decode [1 0 1 0 1 0 1 0]"); } else { @@ -119,8 +140,8 @@ public class PSImageUtils { gen.writeln(" /Decode [0 1]"); } // Setup scanning for left-to-right and top-to-bottom - gen.writeln(" /ImageMatrix [" + img.getWidth() + " 0 0 " - + img.getHeight() + " 0 0]"); + gen.writeln(" /ImageMatrix [" + imgDim.width + " 0 0 " + + imgDim.height + " 0 0]"); gen.writeln(" /DataSource Data"); gen.writeln(">>"); @@ -135,7 +156,7 @@ public class PSImageUtils { OutputStream out = gen.getOutputStream(); out = new ASCII85OutputStream(out); - if (img instanceof JpegImage) { + if (isJPEG) { //nop } else { if (gen.getPSLevel() >= 3) { @@ -144,7 +165,7 @@ public class PSImageUtils { out = new RunLengthEncodeOutputStream(out); } } - out.write(imgmap); + out.write(img); if (out instanceof Finalizable) { ((Finalizable)out).finalizeStream(); } else { @@ -156,6 +177,95 @@ public class PSImageUtils { gen.restoreGraphicsState(); } + /** + * Renders a bitmap image to PostScript. + * @param img image to render + * @param x x position + * @param y y position + * @param w width + * @param h height + * @param gen PS generator + * @throws IOException In case of an I/O problem while rendering the image + */ + public static void renderBitmapImage(RenderedImage img, + float x, float y, float w, float h, PSGenerator gen) + throws IOException { + byte[] imgmap = getBitmapBytes(img); + + String imgName = img.getClass().getName(); + Dimension imgDim = new Dimension(img.getWidth(), img.getHeight()); + Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h); + boolean isJPEG = false; + writeImage(imgmap, imgDim, imgName, targetRect, isJPEG, + img.getColorModel().getColorSpace(), gen); + } + + private static byte[] getBitmapBytes(RenderedImage img) { + int[] tmpMap = getRGB(img, 0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()); + // Should take care of the ColorSpace and bitsPerPixel + byte[] bitmaps = new byte[img.getWidth() * img.getHeight() * 3]; + for (int y = 0, my = img.getHeight(); y < my; y++) { + for (int x = 0, mx = img.getWidth(); x < mx; x++) { + int p = tmpMap[y * mx + x]; + int r = (p >> 16) & 0xFF; + int g = (p >> 8) & 0xFF; + int b = (p) & 0xFF; + bitmaps[3 * (y * mx + x)] = (byte)(r & 0xFF); + bitmaps[3 * (y * mx + x) + 1] = (byte)(g & 0xFF); + bitmaps[3 * (y * mx + x) + 2] = (byte)(b & 0xFF); + } + } + return bitmaps; + } + + public static int[] getRGB(RenderedImage img, + int startX, int startY, int w, int h, + int[] rgbArray, int offset, int scansize) { + Raster raster = img.getData(); + int yoff = offset; + int off; + Object data; + int nbands = raster.getNumBands(); + int dataType = raster.getDataBuffer().getDataType(); + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new byte[nbands]; + break; + case DataBuffer.TYPE_USHORT: + data = new short[nbands]; + break; + case DataBuffer.TYPE_INT: + data = new int[nbands]; + break; + case DataBuffer.TYPE_FLOAT: + data = new float[nbands]; + break; + case DataBuffer.TYPE_DOUBLE: + data = new double[nbands]; + break; + default: + throw new IllegalArgumentException("Unknown data buffer type: "+ + dataType); + } + + if (rgbArray == null) { + rgbArray = new int[offset+h*scansize]; + } + + ColorModel colorModel = img.getColorModel(); + for (int y = startY; y < startY+h; y++, yoff+=scansize) { + off = yoff; + for (int x = startX; x < startX+w; x++) { + rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x, + y, + data)); + } + } + + return rgbArray; + + } + public static void renderEPS(EPSImage img, float x, float y, float w, float h, PSGenerator gen) { diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index 5d3242346..b786a4962 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -21,6 +21,7 @@ package org.apache.fop.render.ps; // Java import java.awt.Color; import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; import java.io.IOException; import java.io.LineNumberReader; import java.io.OutputStream; @@ -63,6 +64,7 @@ import org.apache.fop.image.ImageFactory; import org.apache.fop.image.XMLImage; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.AbstractPathOrientedRenderer; +import org.apache.fop.render.ImageAdapter; import org.apache.fop.render.RendererContext; import org.apache.fop.render.ps.extensions.PSSetupCode; import org.apache.fop.util.CharUtilities; @@ -88,7 +90,7 @@ import org.w3c.dom.Document; * @author Apache FOP Development Team * @version $Id$ */ -public class PSRenderer extends AbstractPathOrientedRenderer { +public class PSRenderer extends AbstractPathOrientedRenderer implements ImageAdapter { /** The MIME type for PostScript */ public static final String MIME_TYPE = "application/postscript"; @@ -154,6 +156,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer { return new PSGraphics2DAdapter(this); } + /** @see org.apache.fop.render.Renderer#getImageAdapter() */ + public ImageAdapter getImageAdapter() { + return this; + } + /** * Write out a command * @param cmd PostScript command @@ -303,6 +310,16 @@ public class PSRenderer extends AbstractPathOrientedRenderer { } } + public void paintImage(RenderedImage image, RendererContext context, int x, int y, int width, int height) throws IOException { + float fx = (float)x / 1000f; + x += currentIPPosition / 1000f; + float fy = (float)y / 1000f; + y += currentBPPosition / 1000f; + float fw = (float)width / 1000f; + float fh = (float)height / 1000f; + PSImageUtils.renderBitmapImage(image, fx, fy, fw, fh, gen); + } + /** * Draw a line. * @@ -740,7 +757,8 @@ public class PSRenderer extends AbstractPathOrientedRenderer { gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP); //Handle PSSetupCode instances on simple-page-master - if (page.getExtensionAttachments().size() > 0) { + if (page.getExtensionAttachments() != null + && page.getExtensionAttachments().size() > 0) { List list = new java.util.ArrayList(); //Extract all PSSetupCode instances from the attachment list on the s-p-m Iterator i = page.getExtensionAttachments().iterator(); @@ -1104,4 +1122,5 @@ public class PSRenderer extends AbstractPathOrientedRenderer { return MIME_TYPE; } + } -- 2.39.5