From dfeb96c655c3e361b5fdf6f0cecbf46adab01a72 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Tue, 4 Nov 2008 15:10:31 +0000 Subject: Making conversion hints available to ImageHandler implementations, too. (Re-)implemented Graphics2D support for PCL. It can be disabled by fox:conversion-mode="bitmap" if necessary. Fixed a bug with non-uniform image scaling when encoding bitmap images. Commented some code that did not reliably convert color images to grayscale. The (probably slower) fallback code is used instead. Some performance improvements for text painting using bitmaps when speed is selected. Removed the RenderingContext interface from RendererContext since it's not used. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@711269 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/render/AbstractRenderingContext.java | 32 +++++ .../org/apache/fop/render/RendererContext.java | 2 +- .../org/apache/fop/render/RenderingContext.java | 8 ++ .../fop/render/intermediate/AbstractIFPainter.java | 2 + .../org/apache/fop/render/pcl/PCLGenerator.java | 19 ++- .../fop/render/pcl/PCLImageHandlerGraphics2D.java | 157 +++++++++++++++++++++ src/java/org/apache/fop/render/pcl/PCLPainter.java | 24 +++- .../apache/fop/render/pcl/PCLRenderingContext.java | 7 + 8 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/AbstractRenderingContext.java b/src/java/org/apache/fop/render/AbstractRenderingContext.java index 7bacac58d..365fec871 100644 --- a/src/java/org/apache/fop/render/AbstractRenderingContext.java +++ b/src/java/org/apache/fop/render/AbstractRenderingContext.java @@ -19,6 +19,9 @@ package org.apache.fop.render; +import java.util.Collections; +import java.util.Map; + import org.apache.fop.apps.FOUserAgent; /** @@ -27,6 +30,7 @@ import org.apache.fop.apps.FOUserAgent; public abstract class AbstractRenderingContext implements RenderingContext { private FOUserAgent userAgent; + private Map hints; /** * Main constructor. @@ -45,5 +49,33 @@ public abstract class AbstractRenderingContext implements RenderingContext { return userAgent; } + /** {@inheritDoc} */ + public void putHints(Map hints) { + if (hints == null) { + return; + } + if (this.hints == null) { + this.hints = new java.util.HashMap(); + } + this.hints.putAll(hints); + } + + /** {@inheritDoc} */ + public Map getHints() { + if (this.hints == null) { + return Collections.EMPTY_MAP; + } else { + return Collections.unmodifiableMap(this.hints); + } + } + + /** {@inheritDoc} */ + public Object getHint(Object key) { + if (this.hints == null) { + return null; + } else { + return this.hints.get(key); + } + } } diff --git a/src/java/org/apache/fop/render/RendererContext.java b/src/java/org/apache/fop/render/RendererContext.java index e52588176..595bdd7c2 100644 --- a/src/java/org/apache/fop/render/RendererContext.java +++ b/src/java/org/apache/fop/render/RendererContext.java @@ -29,7 +29,7 @@ import org.apache.fop.apps.FOUserAgent; * so that external handlers can get information to be able to render to the * render target. */ -public class RendererContext implements RenderingContext { +public class RendererContext { private String mime; private AbstractRenderer renderer; diff --git a/src/java/org/apache/fop/render/RenderingContext.java b/src/java/org/apache/fop/render/RenderingContext.java index a6f958328..5122841b7 100644 --- a/src/java/org/apache/fop/render/RenderingContext.java +++ b/src/java/org/apache/fop/render/RenderingContext.java @@ -19,6 +19,8 @@ package org.apache.fop.render; +import java.util.Map; + import org.apache.fop.apps.FOUserAgent; /** @@ -40,4 +42,10 @@ public interface RenderingContext { */ FOUserAgent getUserAgent(); + void putHints(Map hints); + + Map getHints(); + + Object getHint(Object key); + } diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 8380e69cc..105a2a426 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -127,6 +127,7 @@ public abstract class AbstractIFPainter implements IFPainter { //Load and convert the image to a supported format RenderingContext context = createRenderingContext(); Map hints = createDefaultImageProcessingHints(sessionContext); + context.putHints(hints); org.apache.xmlgraphics.image.loader.Image img = manager.getImage( info, imageHandlerRegistry.getSupportedFlavors(context), hints, sessionContext); @@ -179,6 +180,7 @@ public abstract class AbstractIFPainter implements IFPainter { ImageHandlerRegistry imageHandlerRegistry = getFopFactory().getImageHandlerRegistry(); Image effImage; + context.putHints(additionalHints); if (convert) { Map hints = createDefaultImageProcessingHints(getUserAgent().getImageSessionContext()); if (additionalHints != null) { diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java index 36d1fe079..67dcce1fb 100644 --- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -22,12 +22,10 @@ package org.apache.fop.render.pcl; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; -import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ByteLookupTable; -import java.awt.image.ColorConvertOp; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; @@ -767,11 +765,20 @@ public class PCLGenerator { */ public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency) throws IOException { - double targetResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width); + double targetHResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width); + double targetVResolution = img.getHeight() / UnitConv.mpt2in(targetDim.height); + double targetResolution = Math.max(targetHResolution, targetVResolution); int resolution = (int)Math.round(targetResolution); int effResolution = calculatePCLResolution(resolution, true); Dimension orgDim = new Dimension(img.getWidth(), img.getHeight()); - Dimension effDim = getAdjustedDimension(orgDim, targetResolution, effResolution); + Dimension effDim; + if (targetResolution == effResolution) { + effDim = orgDim; //avoid scaling side-effects + } else { + effDim = new Dimension( + (int)Math.ceil(UnitConv.mpt2px(targetDim.width, effResolution)), + (int)Math.ceil(UnitConv.mpt2px(targetDim.height, effResolution))); + } boolean scaled = !orgDim.equals(effDim); //ImageWriterUtil.saveAsPNG(img, new java.io.File("D:/text-0-org.png")); @@ -791,6 +798,7 @@ public class PCLGenerator { BufferedImage src = null; if (img instanceof BufferedImage && !scaled) { if (!isGrayscaleImage(img) || img.getColorModel().hasAlpha()) { + /* Disabled as this doesn't work reliably, use the fallback below src = new BufferedImage(effDim.width, effDim.height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d = src.createGraphics(); @@ -802,6 +810,7 @@ public class PCLGenerator { ColorConvertOp op = new ColorConvertOp( ColorSpace.getInstance(ColorSpace.CS_GRAY), null); op.filter((BufferedImage)img, src); + */ } else { src = (BufferedImage)img; } @@ -1018,7 +1027,7 @@ public class PCLGenerator { } public void endLine() throws IOException { - if (zeroRow) { + if (zeroRow && PCLGenerator.this.currentSourceTransparency) { writeCommand("*b1Y"); } else if (rlewidth < bytewidth) { writeCommand("*b1m" + rlewidth + "W"); diff --git a/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java new file mode 100644 index 000000000..c24dfd7dc --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLImageHandlerGraphics2D.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.pcl; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; +import org.apache.xmlgraphics.image.loader.impl.ImageRendered; +import org.apache.xmlgraphics.java2d.GraphicContext; +import org.apache.xmlgraphics.util.UnitConv; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation that paints Graphics2D images in PCL. Since PCL is limited in its + * vector graphics capabilities, there's a fallback built in that switches to bitmap painting + * if an advanced feature is encountered. + */ +public class PCLImageHandlerGraphics2D implements ImageHandler { + + /** logging instance */ + private static Log log = LogFactory.getLog(PCLImageHandlerGraphics2D.class); + + /** {@inheritDoc} */ + public int getPriority() { + return 400; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageGraphics2D.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return new ImageFlavor[] { + ImageFlavor.GRAPHICS2D + }; + } + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PCLRenderingContext pclContext = (PCLRenderingContext)context; + ImageGraphics2D imageG2D = (ImageGraphics2D)image; + Dimension imageDim = imageG2D.getSize().getDimensionMpt(); + PCLGenerator gen = pclContext.getPCLGenerator(); + + Point2D transPoint = pclContext.transformedPoint(pos.x, pos.y); + gen.setCursorPos(transPoint.getX(), transPoint.getY()); + + boolean painted = false; + ByteArrayOutputStream baout = new ByteArrayOutputStream(); + PCLGenerator tempGen = new PCLGenerator(baout, gen.getMaximumBitmapResolution()); + try { + GraphicContext ctx = (GraphicContext)pclContext.getGraphicContext().clone(); + + AffineTransform prepareHPGL2 = new AffineTransform(); + prepareHPGL2.scale(0.001, 0.001); + ctx.setTransform(prepareHPGL2); + + PCLGraphics2D graphics = new PCLGraphics2D(tempGen); + graphics.setGraphicContext(ctx); + graphics.setClippingDisabled(false /*pclContext.isClippingDisabled()*/); + Rectangle2D area = new Rectangle2D.Double( + 0.0, 0.0, imageDim.getWidth(), imageDim.getHeight()); + imageG2D.getGraphics2DImagePainter().paint(graphics, area); + + //If we arrive here, the graphic is natively paintable, so write the graphic + gen.writeCommand("*c" + gen.formatDouble4(pos.width / 100f) + "x" + + gen.formatDouble4(pos.height / 100f) + "Y"); + gen.writeCommand("*c0T"); + gen.enterHPGL2Mode(false); + gen.writeText("\nIN;"); + gen.writeText("SP1;"); + //One Plotter unit is 0.025mm! + double scale = imageDim.getWidth() / UnitConv.mm2pt(imageDim.getWidth() * 0.025); + gen.writeText("SC0," + gen.formatDouble4(scale) + + ",0,-" + gen.formatDouble4(scale) + ",2;"); + gen.writeText("IR0,100,0,100;"); + gen.writeText("PU;PA0,0;\n"); + baout.writeTo(gen.getOutputStream()); //Buffer is written to output stream + gen.writeText("\n"); + + gen.enterPCLMode(false); + painted = true; + } catch (UnsupportedOperationException uoe) { + log.debug( + "Cannot paint graphic natively. Falling back to bitmap painting. Reason: " + + uoe.getMessage()); + } + + if (!painted) { + //Fallback solution: Paint to a BufferedImage + FOUserAgent ua = context.getUserAgent(); + ImageManager imageManager = ua.getFactory().getImageManager(); + ImageRendered imgRend; + try { + imgRend = (ImageRendered)imageManager.convertImage( + imageG2D, new ImageFlavor[] {ImageFlavor.RENDERED_IMAGE}/*, hints*/); + } catch (ImageException e) { + throw new IOException( + "Image conversion error while converting the image to a bitmap" + + " as a fallback measure: " + e.getMessage()); + } + + gen.paintBitmap(imgRend.getRenderedImage(), new Dimension(pos.width, pos.height), + pclContext.isSourceTransparencyEnabled()); + } + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + boolean supported = (image == null || image instanceof ImageGraphics2D) + && targetContext instanceof PCLRenderingContext; + if (supported) { + Object hint = targetContext.getHint(PCLConstants.CONV_MODE); + if ("bitmap".equals(hint)) { + return false; + } + } + return supported; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index 7ad6ea931..b709c28f3 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -110,6 +110,10 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { } } + private boolean isSpeedOptimized() { + return getPCLUtil().getRenderingMode() == PCLRenderingMode.SPEED; + } + //---------------------------------------------------------------------------------------------- /** {@inheritDoc} */ @@ -161,6 +165,10 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { return PCLPainter.this.transformedPoint(x, y); } + public GraphicContext getGraphicContext() { + return PCLPainter.this.graphicContext; + } + }; return pdfContext; } @@ -203,7 +211,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { public void drawBorderRect(final Rectangle rect, final BorderProps before, final BorderProps after, final BorderProps start, final BorderProps end) throws IFException { - if (getPCLUtil().getRenderingMode() == PCLRenderingMode.SPEED) { + if (isSpeedOptimized()) { super.drawBorderRect(rect, before, after, start, end); return; } @@ -239,7 +247,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { public void drawLine(final Point start, final Point end, final int width, final Color color, final RuleStyle style) throws IFException { - if (getPCLUtil().getRenderingMode() == PCLRenderingMode.SPEED) { + if (isSpeedOptimized()) { super.drawLine(start, end, width, color, style); return; } @@ -278,8 +286,16 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { ImageGraphics2D img = new ImageGraphics2D(info, painter); Map hints = new java.util.HashMap(); - hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, - ImageProcessingHints.BITMAP_TYPE_INTENT_GRAY); + if (isSpeedOptimized()) { + //Gray text may not be painted in this case! We don't get dithering in Sun JREs. + //But this approach is about twice as fast as the grayscale image. + hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, + ImageProcessingHints.BITMAP_TYPE_INTENT_MONO); + } else { + hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, + ImageProcessingHints.BITMAP_TYPE_INTENT_GRAY); + } + hints.put(PCLConstants.CONV_MODE, "bitmap"); PCLRenderingContext context = (PCLRenderingContext)createRenderingContext(); context.setSourceTransparencyEnabled(true); try { diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java b/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java index 4c41cbf58..9212770fc 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java +++ b/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java @@ -21,6 +21,7 @@ package org.apache.fop.render.pcl; import java.awt.geom.Point2D; +import org.apache.xmlgraphics.java2d.GraphicContext; import org.apache.xmlgraphics.util.MimeConstants; import org.apache.fop.apps.FOUserAgent; @@ -93,4 +94,10 @@ public abstract class PCLRenderingContext extends AbstractRenderingContext { */ public abstract Point2D transformedPoint(int x, int y); + /** + * Returns the current {@code GraphicContext} instance. + * @return the graphic context + */ + public abstract GraphicContext getGraphicContext(); + } -- cgit v1.2.3