From: Jeremias Maerki Date: Thu, 23 Oct 2008 10:03:27 +0000 (+0000) Subject: Moved IFState instance variable into AbstractIFPainter as all implementations use it. X-Git-Tag: fop-1_0~115^2~124 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=48ddcd67a997a2662c2a3de5e90d008dd8435267;p=xmlgraphics-fop.git Moved IFState instance variable into AbstractIFPainter as all implementations use it. Refactored default image handling methods in AbstractIFPainter a bit to make them more universal. Fixed a small mistake in Java2DImageHandlerRenderedImage. Added configurator code for new PCL implementation. Fixed smaller issues in PCLGenerator. Added support for painting images (e-g and i-f-o) as bitmaps (depends on rev 707329 in XML Graphics Commons). Added support for text with custom fonts to be painted as bitmaps. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@707331 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/META-INF/services/org.apache.fop.render.ImageHandler b/src/java/META-INF/services/org.apache.fop.render.ImageHandler index d9f2ccbba..11144f3bc 100644 --- a/src/java/META-INF/services/org.apache.fop.render.ImageHandler +++ b/src/java/META-INF/services/org.apache.fop.render.ImageHandler @@ -5,3 +5,4 @@ org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax org.apache.fop.render.pdf.PDFImageHandlerSVG org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage org.apache.fop.render.java2d.Java2DImageHandlerGraphics2D +org.apache.fop.render.pcl.PCLImageHandlerRenderedImage diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index a54e62bd0..55ed514e0 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -19,6 +19,7 @@ package org.apache.fop.render.intermediate; +import java.awt.Color; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.geom.AffineTransform; @@ -33,6 +34,7 @@ import org.w3c.dom.Document; 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.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; @@ -57,6 +59,9 @@ public abstract class AbstractIFPainter implements IFPainter { /** non-URI that can be used in feedback messages that an image is an instream-object */ protected static final String INSTREAM_OBJECT_URI = "(instream-object)"; + /** Holds the intermediate format state */ + protected IFState state; + /** * Default constructor. @@ -118,31 +123,84 @@ public abstract class AbstractIFPainter implements IFPainter { //Load and convert the image to a supported format RenderingContext context = createRenderingContext(); - Map hints = ImageUtil.getDefaultHints(sessionContext); + Map hints = createDefaultImageProcessingHints(sessionContext); org.apache.xmlgraphics.image.loader.Image img = manager.getImage( info, imageHandlerRegistry.getSupportedFlavors(context), hints, sessionContext); + try { + drawImage(img, rect, context); + } catch (IOException ioe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageWritingError(this, ioe); + } + } + + /** + * Creates the default map of processing hints for the image loading framework. + * @param sessionContext the session context for access to resolution information + * @return the default processing hints + */ + protected Map createDefaultImageProcessingHints(ImageSessionContext sessionContext) { + return ImageUtil.getDefaultHints(sessionContext); + } + + /** + * Draws an image using a suitable image handler. + * @param image the image to be painted (it needs to of a supported image flavor) + * @param rect the rectangle in which to paint the image + * @param context a suitable rendering context + * @throws IOException in case of an I/O error while handling/writing the image + * @throws ImageException if an error occurs while converting the image to a suitable format + */ + protected void drawImage(Image image, Rectangle rect, + RenderingContext context) throws IOException, ImageException { + drawImage(image, rect, context, false, null); + } + + /** + * Draws an image using a suitable image handler. + * @param image the image to be painted (it needs to of a supported image flavor) + * @param rect the rectangle in which to paint the image + * @param context a suitable rendering context + * @param convert true to run the image through image conversion if that is necessary + * @param additionalHints additional image processing hints + * @throws IOException in case of an I/O error while handling/writing the image + * @throws ImageException if an error occurs while converting the image to a suitable format + */ + protected void drawImage(Image image, Rectangle rect, + RenderingContext context, boolean convert, Map additionalHints) + throws IOException, ImageException { + ImageManager manager = getFopFactory().getImageManager(); + ImageHandlerRegistry imageHandlerRegistry = getFopFactory().getImageHandlerRegistry(); + + Image effImage; + if (convert) { + Map hints = createDefaultImageProcessingHints(getUserAgent().getImageSessionContext()); + if (additionalHints != null) { + hints.putAll(additionalHints); + } + effImage = manager.convertImage(image, + imageHandlerRegistry.getSupportedFlavors(context), hints); + } else { + effImage = image; + } + //First check for a dynamically registered handler - ImageHandler handler = imageHandlerRegistry.getHandler(context, img); + ImageHandler handler = imageHandlerRegistry.getHandler(context, effImage); if (handler == null) { throw new UnsupportedOperationException( "No ImageHandler available for image: " - + info + " (" + img.getClass().getName() + ")"); + + effImage.getInfo() + " (" + effImage.getClass().getName() + ")"); } if (log.isDebugEnabled()) { log.debug("Using ImageHandler: " + handler.getClass().getName()); } - try { - //TODO foreign attributes - handler.handleImage(context, img, rect); - } catch (IOException ioe) { - ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( - getUserAgent().getEventBroadcaster()); - eventProducer.imageWritingError(this, ioe); - return; - } + + //TODO foreign attributes + handler.handleImage(context, effImage, rect); } /** @@ -203,5 +261,27 @@ public abstract class AbstractIFPainter implements IFPainter { } } + /** {@inheritDoc} */ + public void setFont(String family, String style, Integer weight, String variant, Integer size, + Color color) throws IFException { + if (family != null) { + state.setFontFamily(family); + } + if (style != null) { + state.setFontStyle(style); + } + if (weight != null) { + state.setFontWeight(weight.intValue()); + } + if (variant != null) { + state.setFontVariant(variant); + } + if (size != null) { + state.setFontSize(size.intValue()); + } + if (color != null) { + state.setTextColor(color); + } + } } diff --git a/src/java/org/apache/fop/render/java2d/Java2DImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/java2d/Java2DImageHandlerRenderedImage.java index 9c2d24c32..80d57fc59 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/java2d/Java2DImageHandlerRenderedImage.java @@ -30,7 +30,6 @@ import java.io.IOException; import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.ImageInfo; -import org.apache.xmlgraphics.image.loader.impl.ImageRawStream; import org.apache.xmlgraphics.image.loader.impl.ImageRendered; import org.apache.fop.render.ImageHandler; @@ -49,7 +48,7 @@ public class Java2DImageHandlerRenderedImage implements ImageHandler { /** {@inheritDoc} */ public Class getSupportedImageClass() { - return ImageRawStream.class; + return ImageRendered.class; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 9a68f62d0..013be3f86 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -62,9 +62,6 @@ public class Java2DPainter extends AbstractIFPainter { /** The font information */ protected FontInfo fontInfo; - /** Holds the intermediate format state */ - protected IFState state; - private Java2DBorderPainter borderPainter; /** The current state, holds a Graphics2D and its context */ @@ -78,9 +75,24 @@ public class Java2DPainter extends AbstractIFPainter { * @param fontInfo the font information */ public Java2DPainter(Graphics2D g2d, FOUserAgent userAgent, FontInfo fontInfo) { + this(g2d, userAgent, fontInfo, null); + } + + /** + * Special constructor for embedded use (when another painter uses Java2DPainter + * to convert part of a document into a bitmap, for example). + * @param g2d the target Graphics2D instance + * @param userAgent the user agent + * @param fontInfo the font information + */ + public Java2DPainter(Graphics2D g2d, FOUserAgent userAgent, FontInfo fontInfo, IFState state) { super(); this.userAgent = userAgent; - this.state = IFState.create(); + if (state != null) { + this.state = state.push(); + } else { + this.state = IFState.create(); + } this.fontInfo = fontInfo; this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform()); this.borderPainter = new Java2DBorderPainter(this); @@ -227,31 +239,6 @@ public class Java2DPainter extends AbstractIFPainter { g2d.drawGlyphVector(gv, x, y); } - /** {@inheritDoc} */ - public void setFont(String family, String style, Integer weight, String variant, Integer size, - Color color) throws IFException { - if (family != null) { - state.setFontFamily(family); - } - if (style != null) { - state.setFontStyle(style); - } - if (weight != null) { - state.setFontWeight(weight.intValue()); - } - if (variant != null) { - state.setFontVariant(variant); - } - if (size != null) { - state.setFontSize(size.intValue()); - } - if (color != null) { - state.setTextColor(color); - } - } - - //---------------------------------------------------------------------------------------------- - /** Saves the current graphics state on the stack. */ protected void saveGraphicsState() { g2dStateStack.push(g2dState); diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderingContext.java b/src/java/org/apache/fop/render/java2d/Java2DRenderingContext.java index 7bc55502a..86655434f 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DRenderingContext.java +++ b/src/java/org/apache/fop/render/java2d/Java2DRenderingContext.java @@ -26,7 +26,7 @@ import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.AbstractRenderingContext; /** - * Rendering context for PDF production. + * Rendering context for Java2D painting. */ public class Java2DRenderingContext extends AbstractRenderingContext { diff --git a/src/java/org/apache/fop/render/pcl/HardcodedFonts.java b/src/java/org/apache/fop/render/pcl/HardcodedFonts.java new file mode 100644 index 000000000..a1c5a6059 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/HardcodedFonts.java @@ -0,0 +1,160 @@ +/* + * 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.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class hold code for selecting a set of hard-coded fonts available in practically all + * PCL implementations. We hope this can be improved in the future. + */ +class HardcodedFonts { + + /** logging instance */ + private static Log log = LogFactory.getLog(HardcodedFonts.class); + + /** + * Sets the current font (NOTE: Hard-coded font mappings ATM!) + * @param name the font name (internal F* names for now) + * @param size the font size (in millipoints) + * @param text the text to be rendered (used to determine if there are non-printable chars) + * @return true if the font can be mapped to PCL + * @throws IOException if an I/O problem occurs + */ + public static boolean setFont(PCLGenerator gen, String name, int size, String text) + throws IOException { + byte[] encoded = text.getBytes("ISO-8859-1"); + for (int i = 0, c = encoded.length; i < c; i++) { + if (encoded[i] == 0x3F && text.charAt(i) != '?') { + return false; + } + } + return selectFont(gen, name, size); + } + + private static boolean selectFont(PCLGenerator gen, String name, int size) throws IOException { + int fontcode = 0; + if (name.length() > 1 && name.charAt(0) == 'F') { + try { + fontcode = Integer.parseInt(name.substring(1)); + } catch (Exception e) { + log.error(e); + } + } + //Note "(ON" selects ISO 8859-1 symbol set as used by PCLGenerator + String formattedSize = gen.formatDouble2(size / 1000.0); + switch (fontcode) { + case 1: // F1 = Helvetica + // gen.writeCommand("(8U"); + // gen.writeCommand("(s1p" + formattedSize + "v0s0b24580T"); + // Arial is more common among PCL5 printers than Helvetica - so use Arial + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v0s0b16602T"); + break; + case 2: // F2 = Helvetica Oblique + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v1s0b16602T"); + break; + case 3: // F3 = Helvetica Bold + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v0s3b16602T"); + break; + case 4: // F4 = Helvetica Bold Oblique + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v1s3b16602T"); + break; + case 5: // F5 = Times Roman + // gen.writeCommand("(8U"); + // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); + // Times New is more common among PCL5 printers than Times - so use Times New + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v0s0b16901T"); + break; + case 6: // F6 = Times Italic + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v1s0b16901T"); + break; + case 7: // F7 = Times Bold + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v0s3b16901T"); + break; + case 8: // F8 = Times Bold Italic + + gen.writeCommand("(0N"); + gen.writeCommand("(s1p" + formattedSize + "v1s3b16901T"); + break; + case 9: // F9 = Courier + + gen.writeCommand("(0N"); + gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) + + "h0s0b4099T"); + break; + case 10: // F10 = Courier Oblique + + gen.writeCommand("(0N"); + gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) + + "h1s0b4099T"); + break; + case 11: // F11 = Courier Bold + + gen.writeCommand("(0N"); + gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) + + "h0s3b4099T"); + break; + case 12: // F12 = Courier Bold Oblique + + gen.writeCommand("(0N"); + gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) + + "h1s3b4099T"); + break; + case 13: // F13 = Symbol + + return false; + //gen.writeCommand("(19M"); + //gen.writeCommand("(s1p" + formattedSize + "v0s0b16686T"); + // ECMA Latin 1 Symbol Set in Times Roman??? + // gen.writeCommand("(9U"); + // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); + //break; + case 14: // F14 = Zapf Dingbats + + return false; + //gen.writeCommand("(14L"); + //gen.writeCommand("(s1p" + formattedSize + "v0s0b45101T"); + //break; + default: + //gen.writeCommand("(0N"); + //gen.writeCommand("(s" + formattedSize + "V"); + return false; + } + return true; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java index ce032ace5..0c1a3a14a 100644 --- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java +++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java @@ -78,7 +78,7 @@ public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler /** {@inheritDoc} */ public IFDocumentHandlerConfigurator getConfigurator() { - return null; //No configurator, yet. + return new PCLRendererConfigurator(getUserAgent()); } PCLRenderingUtil getPCLUtil() { diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java index 5d42d3320..4a937d7b1 100644 --- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java @@ -52,7 +52,7 @@ public class PCLDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { /** {@inheritDoc} */ public IFDocumentHandlerConfigurator getConfigurator(FOUserAgent userAgent) { - return null; + return new PCLRendererConfigurator(userAgent); } } diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java index 3a451c9ad..eac1af3d0 100644 --- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -601,7 +601,7 @@ public class PCLGenerator { * @return true if it's a grayscale image */ public static boolean isGrayscaleImage(RenderedImage img) { - return (img.getColorModel().getColorSpace().getNumComponents() == 1); + return (img.getColorModel().getNumColorComponents() == 1); } private MonochromeBitmapConverter createMonochromeBitmapConverter() { @@ -751,6 +751,7 @@ public class PCLGenerator { Dimension orgDim = new Dimension(img.getWidth(), img.getHeight()); Dimension effDim = getAdjustedDimension(orgDim, targetResolution, effResolution); boolean scaled = !orgDim.equals(effDim); + //ImageWriterUtil.saveAsPNG(img, new java.io.File("D:/text-0-org.png")); boolean monochrome = isMonochromeImage(img); if (!monochrome) { @@ -770,6 +771,12 @@ public class PCLGenerator { if (!isGrayscaleImage(img) || img.getColorModel().hasAlpha()) { src = new BufferedImage(effDim.width, effDim.height, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = src.createGraphics(); + try { + clearBackground(g2d, effDim); + } finally { + g2d.dispose(); + } ColorConvertOp op = new ColorConvertOp( ColorSpace.getInstance(ColorSpace.CS_GRAY), null); op.filter((BufferedImage)img, src); @@ -782,6 +789,8 @@ public class PCLGenerator { BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d = src.createGraphics(); try { + clearBackground(g2d, effDim); + AffineTransform at = new AffineTransform(); double sx = effDim.getWidth() / orgDim.getWidth(); double sy = effDim.getHeight() / orgDim.getHeight(); @@ -824,6 +833,12 @@ public class PCLGenerator { } } + private void clearBackground(Graphics2D g2d, Dimension effDim) { + //white background + g2d.setBackground(Color.WHITE); + g2d.clearRect(0, 0, effDim.width, effDim.height); + } + /** * Paint a bitmap at the current cursor position. The bitmap must be a monochrome * (1-bit) bitmap image. diff --git a/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java new file mode 100644 index 000000000..ce8c26a6f --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLImageHandlerRenderedImage.java @@ -0,0 +1,78 @@ +/* + * 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.Point2D; +import java.awt.image.RenderedImage; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.impl.ImageRendered; + +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation that paints {@code RenderedImage} instances in PCL. + */ +public class PCLImageHandlerRenderedImage implements ImageHandler { + + /** {@inheritDoc} */ + public int getPriority() { + return 300; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageRendered.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return new ImageFlavor[] { + ImageFlavor.BUFFERED_IMAGE, + ImageFlavor.RENDERED_IMAGE, + }; + } + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PCLRenderingContext pclContext = (PCLRenderingContext)context; + ImageRendered imageRend = (ImageRendered)image; + PCLGenerator gen = pclContext.getPCLGenerator(); + + RenderedImage ri = imageRend.getRenderedImage(); + Point2D transPoint = pclContext.transformedPoint(pos.x, pos.y); + gen.setCursorPos(transPoint.getX(), transPoint.getY()); + gen.paintBitmap(ri, new Dimension(pos.width, pos.height), + pclContext.isSourceTransparencyEnabled()); + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageRendered) + && targetContext instanceof PCLRenderingContext; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index d4e04175e..490ec7f7a 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -21,11 +21,13 @@ package org.apache.fop.render.pcl; import java.awt.Color; import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.io.IOException; import java.util.Map; import java.util.Stack; @@ -35,20 +37,26 @@ import org.w3c.dom.Document; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.ImageProcessingHints; +import org.apache.xmlgraphics.image.loader.ImageSize; +import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; import org.apache.xmlgraphics.java2d.GraphicContext; +import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontTriplet; -import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; +import org.apache.fop.render.java2d.FontMetricsMapper; +import org.apache.fop.render.java2d.Java2DPainter; import org.apache.fop.traits.BorderProps; import org.apache.fop.traits.RuleStyle; import org.apache.fop.util.CharUtilities; -import org.apache.fop.util.UnitConv; /** * {@code IFPainter} implementation that produces PCL 5. @@ -58,10 +66,9 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { /** logging instance */ private static Log log = LogFactory.getLog(PCLPainter.class); - private PCLDocumentHandler parent; + private final boolean DEBUG = false; - /** Holds the intermediate format state */ - protected IFState state; + private PCLDocumentHandler parent; /** The PCL generator */ private PCLGenerator gen; @@ -142,56 +149,25 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { /** {@inheritDoc} */ public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException { - /* - PDFXObject xobject = pdfDoc.getXObject(uri); - if (xobject != null) { - placeImage(rect, xobject); - return; - } - - drawImageUsingURI(uri, rect); - - flushPDFDoc(); - */ + drawImageUsingURI(uri, rect/*, foreignAttributes*/); } /** {@inheritDoc} */ protected RenderingContext createRenderingContext() { - /* PCLRenderingContext pdfContext = new PCLRenderingContext( - getUserAgent(), generator, currentPage, getFontInfo()); - return pdfContext; - */ - return null; - } + getUserAgent(), this.gen, getPCLUtil()) { - /** - * Places a previously registered image at a certain place on the page. - * @param x X coordinate - * @param y Y coordinate - * @param w width for image - * @param h height for image - * @param xobj the image XObject - */ - private void placeImage(Rectangle rect, PDFXObject xobj) { - /* - generator.saveGraphicsState(); - generator.add(format(rect.width) + " 0 0 " - + format(-rect.height) + " " - + format(rect.x) + " " - + format(rect.y + rect.height ) - + " cm " + xobj.getName() + " Do\n"); - generator.restoreGraphicsState(); - */ + public Point2D transformedPoint(int x, int y) { + return PCLPainter.this.transformedPoint(x, y); + } + + }; + return pdfContext; } /** {@inheritDoc} */ public void drawImage(Document doc, Rectangle rect, Map foreignAttributes) throws IFException { - /* drawImageUsingDocument(doc, rect); - - flushPDFDoc(); - */ } /** {@inheritDoc} */ @@ -257,14 +233,21 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { String fontKey = parent.getFontInfo().getInternalFontKey(triplet); boolean pclFont = getPCLUtil().isAllTextAsBitmaps() ? false - : setFont(fontKey, state.getFontSize(), text); - if (true || pclFont) { + : HardcodedFonts.setFont(gen, fontKey, state.getFontSize(), text); + if (pclFont) { drawTextNative(x, y, dx, text, triplet); } else { drawTextAsBitmap(x, y, dx, dy, text, triplet); + if (DEBUG) { + state.setTextColor(Color.GRAY); + HardcodedFonts.setFont(gen, "F1", state.getFontSize(), text); + drawTextNative(x, y, dx, text, triplet); + } } } catch (IOException ioe) { throw new IFException("I/O error in drawText()", ioe); + } catch (ImageException ime) { + throw new IFException("Image processing error in drawText()", ime); } } @@ -319,78 +302,113 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { } - private void drawTextAsBitmap(int x, int y, int[] dx, int[] dy, - String text, FontTriplet triplet) throws IOException { - /* + private static final double SAFETY_MARGIN_FACTOR = 0.05; + + private Rectangle getTextBoundingRect(int x, int y, int[] dx, int[] dy, String text, + Font font, FontMetricsMapper metrics) { + int maxAscent = metrics.getMaxAscent(font.getFontSize()) / 1000; + int descent = metrics.getDescender(font.getFontSize()) / 1000; //is negative + int safetyMargin = (int)(SAFETY_MARGIN_FACTOR * font.getFontSize()); + Rectangle boundingRect = new Rectangle( + x, y - maxAscent - safetyMargin, + 0, maxAscent - descent + 2 * safetyMargin); + + int l = text.length(); + int dxl = (dx != null ? dx.length : 0); + + if (dx != null && dxl > 0 && dx[0] != 0) { + boundingRect.setLocation(boundingRect.x - (int)Math.ceil(dx[0] / 10f), boundingRect.y); + } + float width = 0.0f; + for (int i = 0; i < l; i++) { + char orgChar = text.charAt(i); + float glyphAdjust = 0; + int cw = font.getCharWidth(orgChar); + + if (dx != null && i < dxl - 1) { + glyphAdjust += dx[i + 1]; + } + + width += cw - glyphAdjust; + } + int extraWidth = font.getFontSize() / 3; + boundingRect.setSize( + (int)Math.ceil(width) + extraWidth, + boundingRect.height); + return boundingRect; + } + + private void drawTextAsBitmap(final int x, final int y, final int[] dx, final int[] dy, + final String text, FontTriplet triplet) throws IOException, ImageException { //Use Java2D to paint different fonts via bitmap - final Font font = getFontFromArea(text); - final int baseline = text.getBaselineOffset(); + final Font font = parent.getFontInfo().getFontInstance(triplet, state.getFontSize()); + //final Font font = getFontFromArea(text); + //final int baseline = text.getBaselineOffset(); //for cursive fonts, so the text isn't clipped - int extraWidth = font.getFontSize() / 3; - final FontMetricsMapper mapper = (FontMetricsMapper)fontInfo.getMetricsFor( + final FontMetricsMapper mapper = (FontMetricsMapper)parent.getFontInfo().getMetricsFor( font.getFontName()); - int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; - final int additionalBPD = maxAscent - baseline; - - Graphics2DAdapter g2a = getGraphics2DAdapter(); - final Rectangle paintRect = new Rectangle( - rx, currentBPPosition + text.getOffset() - additionalBPD, - text.getIPD() + extraWidth, text.getBPD() + additionalBPD); - RendererContext rc = createRendererContext(paintRect.x, paintRect.y, - paintRect.width, paintRect.height, null); + final int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; + final int ascent = mapper.getAscender(font.getFontSize()) / 1000; + final int descent = mapper.getDescender(font.getFontSize()) / 1000; + int safetyMargin = (int)(SAFETY_MARGIN_FACTOR * font.getFontSize()); + final int baselineOffset = maxAscent + safetyMargin; + + final Rectangle boundingRect = getTextBoundingRect(x, y, dx, dy, text, font, mapper); + Map atts = new java.util.HashMap(); atts.put(CONV_MODE, "bitmap"); atts.put(SRC_TRANSPARENCY, "true"); - rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); + //rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); + final Dimension dim = boundingRect.getSize(); Graphics2DImagePainter painter = new Graphics2DImagePainter() { public void paint(Graphics2D g2d, Rectangle2D area) { - g2d.setFont(mapper.getFont(font.getFontSize())); - g2d.translate(0, baseline + additionalBPD); - g2d.scale(1000, 1000); - g2d.setColor(col); - Java2DRenderer.renderText(text, g2d, font); - renderTextDecoration(g2d, mapper, fontsize, text, 0, 0); + if (DEBUG) { + g2d.setBackground(Color.LIGHT_GRAY); + g2d.clearRect(0, 0, (int)area.getWidth(), (int)area.getHeight()); + } + g2d.translate(0, -y + baselineOffset); + + if (DEBUG) { + Rectangle rect = new Rectangle(x, y - maxAscent, 3000, maxAscent); + g2d.draw(rect); + rect = new Rectangle(x, y - ascent, 2000, ascent); + g2d.draw(rect); + rect = new Rectangle(x, y, 1000, - descent); + g2d.draw(rect); + } + Java2DPainter painter = new Java2DPainter(g2d, + getUserAgent(), parent.getFontInfo(), state); + try { + painter.drawText(x, y, dx, dy, text); + } catch (IFException e) { + //This should never happen with the Java2DPainter + throw new RuntimeException("Unexpected error while painting text", e); + } } public Dimension getImageSize() { - return paintRect.getSize(); + return dim.getSize(); } }; - g2a.paintImage(painter, rc, - paintRect.x, paintRect.y, paintRect.width, paintRect.height); - currentIPPosition = saveIP + text.getAllocIPD(); - */ + ImageInfo info = new ImageInfo(null, null); + ImageSize size = new ImageSize(); + size.setSizeInMillipoints(boundingRect.width, boundingRect.height); + info.setSize(size); + ImageGraphics2D img = new ImageGraphics2D(info, painter); + + Rectangle rect = boundingRect; + Map hints = new java.util.HashMap(); + hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, + ImageProcessingHints.BITMAP_TYPE_INTENT_GRAY); + PCLRenderingContext context = (PCLRenderingContext)createRenderingContext(); + context.setSourceTransparencyEnabled(true); + drawImage(img, rect, context, true, hints); } - /** {@inheritDoc} */ - public void setFont(String family, String style, Integer weight, String variant, Integer size, - Color color) throws IFException { - if (family != null) { - state.setFontFamily(family); - } - if (style != null) { - state.setFontStyle(style); - } - if (weight != null) { - state.setFontWeight(weight.intValue()); - } - if (variant != null) { - state.setFontVariant(variant); - } - if (size != null) { - state.setFontSize(size.intValue()); - } - if (color != null) { - state.setTextColor(color); - } - } - - //---------------------------------------------------------------------------------------------- - /** Saves the current graphics state on the stack. */ private void saveGraphicsState() { graphicContextStack.push(graphicContext); @@ -410,52 +428,8 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { } private Point2D transformedPoint(int x, int y) { - AffineTransform at = graphicContext.getTransform(); - if (log.isTraceEnabled()) { - log.trace("Current transform: " + at); - } - Point2D.Float orgPoint = new Point2D.Float(x, y); - Point2D.Float transPoint = new Point2D.Float(); - at.transform(orgPoint, transPoint); - //At this point we have the absolute position in FOP's coordinate system - - //Now get PCL coordinates taking the current print direction and the logical page - //into account. - Dimension pageSize = currentPageDefinition.getPhysicalPageSize(); - Rectangle logRect = currentPageDefinition.getLogicalPageRect(); - switch (currentPrintDirection) { - case 0: - transPoint.x -= logRect.x; - transPoint.y -= logRect.y; - break; - case 90: - float ty = transPoint.x; - transPoint.x = pageSize.height - transPoint.y; - transPoint.y = ty; - transPoint.x -= logRect.y; - transPoint.y -= logRect.x; - break; - case 180: - transPoint.x = pageSize.width - transPoint.x; - transPoint.y = pageSize.height - transPoint.y; - transPoint.x -= pageSize.width - logRect.x - logRect.width; - transPoint.y -= pageSize.height - logRect.y - logRect.height; - //The next line is odd and is probably necessary due to the default value of the - //Text Length command: "1/2 inch less than maximum text length" - //I wonder why this isn't necessary for the 90 degree rotation. *shrug* - transPoint.y -= UnitConv.in2mpt(0.5); - break; - case 270: - float tx = transPoint.y; - transPoint.y = pageSize.width - transPoint.x; - transPoint.x = tx; - transPoint.x -= pageSize.height - logRect.y - logRect.height; - transPoint.y -= pageSize.width - logRect.x - logRect.width; - break; - default: - throw new IllegalStateException("Illegal print direction: " + currentPrintDirection); - } - return transPoint; + return PCLRenderingUtil.transformedPoint(x, y, graphicContext.getTransform(), + currentPageDefinition, currentPrintDirection); } private void changePrintDirection() throws IOException { @@ -479,123 +453,4 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { gen.setCursorPos(transPoint.getX(), transPoint.getY()); } - /** - * Sets the current font (NOTE: Hard-coded font mappings ATM!) - * @param name the font name (internal F* names for now) - * @param size the font size (in millipoints) - * @param text the text to be rendered (used to determine if there are non-printable chars) - * @return true if the font can be mapped to PCL - * @throws IOException if an I/O problem occurs - */ - public boolean setFont(String name, int size, String text) throws IOException { - byte[] encoded = text.getBytes("ISO-8859-1"); - for (int i = 0, c = encoded.length; i < c; i++) { - if (encoded[i] == 0x3F && text.charAt(i) != '?') { - return false; - } - } - int fontcode = 0; - if (name.length() > 1 && name.charAt(0) == 'F') { - try { - fontcode = Integer.parseInt(name.substring(1)); - } catch (Exception e) { - log.error(e); - } - } - //Note "(ON" selects ISO 8859-1 symbol set as used by PCLGenerator - String formattedSize = gen.formatDouble2(size / 1000.0); - switch (fontcode) { - case 1: // F1 = Helvetica - // gen.writeCommand("(8U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b24580T"); - // Arial is more common among PCL5 printers than Helvetica - so use Arial - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s0b16602T"); - break; - case 2: // F2 = Helvetica Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s0b16602T"); - break; - case 3: // F3 = Helvetica Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s3b16602T"); - break; - case 4: // F4 = Helvetica Bold Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s3b16602T"); - break; - case 5: // F5 = Times Roman - // gen.writeCommand("(8U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); - // Times New is more common among PCL5 printers than Times - so use Times New - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s0b16901T"); - break; - case 6: // F6 = Times Italic - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s0b16901T"); - break; - case 7: // F7 = Times Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s3b16901T"); - break; - case 8: // F8 = Times Bold Italic - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s3b16901T"); - break; - case 9: // F9 = Courier - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h0s0b4099T"); - break; - case 10: // F10 = Courier Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h1s0b4099T"); - break; - case 11: // F11 = Courier Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h0s3b4099T"); - break; - case 12: // F12 = Courier Bold Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h1s3b4099T"); - break; - case 13: // F13 = Symbol - - return false; - //gen.writeCommand("(19M"); - //gen.writeCommand("(s1p" + formattedSize + "v0s0b16686T"); - // ECMA Latin 1 Symbol Set in Times Roman??? - // gen.writeCommand("(9U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); - //break; - case 14: // F14 = Zapf Dingbats - - return false; - //gen.writeCommand("(14L"); - //gen.writeCommand("(s1p" + formattedSize + "v0s0b45101T"); - //break; - default: - //gen.writeCommand("(0N"); - //gen.writeCommand("(s" + formattedSize + "V"); - return false; - } - return true; - } - } diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderer.java b/src/java/org/apache/fop/render/pcl/PCLRenderer.java index 44631af4f..95ebc8c94 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRenderer.java +++ b/src/java/org/apache/fop/render/pcl/PCLRenderer.java @@ -26,13 +26,11 @@ import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; -import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.FileNotFoundException; import java.io.IOException; @@ -62,6 +60,7 @@ import org.apache.xmlgraphics.util.QName; import org.apache.xmlgraphics.util.UnitConv; import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -95,6 +94,7 @@ import org.apache.fop.render.java2d.Base14FontCollection; import org.apache.fop.render.java2d.ConfiguredFontCollection; import org.apache.fop.render.java2d.FontMetricsMapper; import org.apache.fop.render.java2d.InstalledFontCollection; +import org.apache.fop.render.java2d.Java2DFontMetrics; import org.apache.fop.render.java2d.Java2DRenderer; import org.apache.fop.render.pcl.extensions.PCLElementMapping; import org.apache.fop.traits.BorderProps; @@ -132,28 +132,10 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { private java.awt.Color currentFillColor = null; /** - * Controls whether appearance is more important than speed. False can cause some FO feature - * to be ignored (like the advanced borders). + * Utility class which enables all sorts of features that are not directly connected to the + * normal rendering process. */ - private boolean qualityBeforeSpeed = false; - - /** - * Controls whether all text should be painted as text. This is a fallback setting in case - * the mixture of native and bitmapped text does not provide the necessary quality. - */ - private boolean allTextAsBitmaps = false; - - /** - * Controls whether an RGB canvas is used when converting Java2D graphics to bitmaps. - * This can be used to work around problems with Apache Batik, for example, but setting - * this to true will increase memory consumption. - */ - private final boolean useColorCanvas = false; - - /** - * Controls whether the generation of PJL commands gets disabled. - */ - private boolean disabledPJL = false; + private PCLRenderingUtil pclUtil; /** contains the pageWith of the last printed page */ private long pageWidth = 0; @@ -166,13 +148,23 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { public PCLRenderer() { } + /** {@inheritDoc} */ + public void setUserAgent(FOUserAgent agent) { + super.setUserAgent(agent); + this.pclUtil = new PCLRenderingUtil(getUserAgent()); + } + + PCLRenderingUtil getPCLUtil() { + return this.pclUtil; + } + /** * Configures the renderer to trade speed for quality if desired. One example here is the way * that borders are rendered. * @param qualityBeforeSpeed true if quality is more important than speed */ public void setQualityBeforeSpeed(boolean qualityBeforeSpeed) { - this.qualityBeforeSpeed = qualityBeforeSpeed; + pclUtil.setQualityBeforeSpeed(qualityBeforeSpeed); } /** @@ -180,7 +172,7 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { * @param disable true to disable PJL commands */ public void setPJLDisabled(boolean disable) { - this.disabledPJL = disable; + pclUtil.setPJLDisabled(disable); } /** @@ -188,7 +180,16 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { * @return true if PJL generation is disabled. */ public boolean isPJLDisabled() { - return this.disabledPJL; + return pclUtil.isPJLDisabled(); + } + + /** + * Controls whether all text should be generated as bitmaps or only text for which there's + * no native font. + * @param allTextAsBitmaps true if all text should be painted as bitmaps + */ + public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { + pclUtil.setAllTextAsBitmaps(allTextAsBitmaps); } /** @@ -199,12 +200,7 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { //The PCLRenderer uses the Java2D FontSetup which needs a special font setup //create a temp Image to test font metrics on fontInfo = inFontInfo; - BufferedImage fontImage = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics2D = fontImage.createGraphics(); - //The next line is important to get accurate font metrics! - graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_ON); + Graphics2D graphics2D = Java2DFontMetrics.createFontMetricsGraphics2D(); FontCollection[] fontCollections = new FontCollection[] { new Base14FontCollection(graphics2D), @@ -248,125 +244,6 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { } } - /** - * Sets the current font (NOTE: Hard-coded font mappings ATM!) - * @param name the font name (internal F* names for now) - * @param size the font size - * @param text the text to be rendered (used to determine if there are non-printable chars) - * @return true if the font can be mapped to PCL - * @throws IOException if an I/O problem occurs - */ - public boolean setFont(String name, float size, String text) throws IOException { - byte[] encoded = text.getBytes("ISO-8859-1"); - for (int i = 0, c = encoded.length; i < c; i++) { - if (encoded[i] == 0x3F && text.charAt(i) != '?') { - return false; - } - } - int fontcode = 0; - if (name.length() > 1 && name.charAt(0) == 'F') { - try { - fontcode = Integer.parseInt(name.substring(1)); - } catch (Exception e) { - log.error(e); - } - } - //Note "(ON" selects ISO 8859-1 symbol set as used by PCLGenerator - String formattedSize = gen.formatDouble2(size / 1000); - switch (fontcode) { - case 1: // F1 = Helvetica - // gen.writeCommand("(8U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b24580T"); - // Arial is more common among PCL5 printers than Helvetica - so use Arial - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s0b16602T"); - break; - case 2: // F2 = Helvetica Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s0b16602T"); - break; - case 3: // F3 = Helvetica Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s3b16602T"); - break; - case 4: // F4 = Helvetica Bold Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s3b16602T"); - break; - case 5: // F5 = Times Roman - // gen.writeCommand("(8U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); - // Times New is more common among PCL5 printers than Times - so use Times New - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s0b16901T"); - break; - case 6: // F6 = Times Italic - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s0b16901T"); - break; - case 7: // F7 = Times Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v0s3b16901T"); - break; - case 8: // F8 = Times Bold Italic - - gen.writeCommand("(0N"); - gen.writeCommand("(s1p" + formattedSize + "v1s3b16901T"); - break; - case 9: // F9 = Courier - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h0s0b4099T"); - break; - case 10: // F10 = Courier Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h1s0b4099T"); - break; - case 11: // F11 = Courier Bold - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h0s3b4099T"); - break; - case 12: // F12 = Courier Bold Oblique - - gen.writeCommand("(0N"); - gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f)) - + "h1s3b4099T"); - break; - case 13: // F13 = Symbol - - return false; - //gen.writeCommand("(19M"); - //gen.writeCommand("(s1p" + formattedSize + "v0s0b16686T"); - // ECMA Latin 1 Symbol Set in Times Roman??? - // gen.writeCommand("(9U"); - // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T"); - //break; - case 14: // F14 = Zapf Dingbats - - return false; - //gen.writeCommand("(14L"); - //gen.writeCommand("(s1p" + formattedSize + "v0s0b45101T"); - //break; - default: - //gen.writeCommand("(0N"); - //gen.writeCommand("(s" + formattedSize + "V"); - return false; - } - return true; - } - /** {@inheritDoc} */ public void startRenderer(OutputStream outputStream) throws IOException { log.debug("Rendering areas to PCL..."); @@ -491,52 +368,8 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { } private Point2D transformedPoint(int x, int y) { - AffineTransform at = graphicContext.getTransform(); - if (log.isTraceEnabled()) { - log.trace("Current transform: " + at); - } - Point2D.Float orgPoint = new Point2D.Float(x, y); - Point2D.Float transPoint = new Point2D.Float(); - at.transform(orgPoint, transPoint); - //At this point we have the absolute position in FOP's coordinate system - - //Now get PCL coordinates taking the current print direction and the logical page - //into account. - Dimension pageSize = currentPageDefinition.getPhysicalPageSize(); - Rectangle logRect = currentPageDefinition.getLogicalPageRect(); - switch (currentPrintDirection) { - case 0: - transPoint.x -= logRect.x; - transPoint.y -= logRect.y; - break; - case 90: - float ty = transPoint.x; - transPoint.x = pageSize.height - transPoint.y; - transPoint.y = ty; - transPoint.x -= logRect.y; - transPoint.y -= logRect.x; - break; - case 180: - transPoint.x = pageSize.width - transPoint.x; - transPoint.y = pageSize.height - transPoint.y; - transPoint.x -= pageSize.width - logRect.x - logRect.width; - transPoint.y -= pageSize.height - logRect.y - logRect.height; - //The next line is odd and is probably necessary due to the default value of the - //Text Length command: "1/2 inch less than maximum text length" - //I wonder why this isn't necessary for the 90 degree rotation. *shrug* - transPoint.y -= UnitConv.in2mpt(0.5); - break; - case 270: - float tx = transPoint.y; - transPoint.y = pageSize.width - transPoint.x; - transPoint.x = tx; - transPoint.x -= pageSize.height - logRect.y - logRect.height; - transPoint.y -= pageSize.width - logRect.x - logRect.width; - break; - default: - throw new IllegalStateException("Illegal print direction: " + currentPrintDirection); - } - return transPoint; + return PCLRenderingUtil.transformedPoint(x, y, graphicContext.getTransform(), + currentPageDefinition, currentPrintDirection); } private void changePrintDirection() { @@ -640,9 +473,9 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { try { final Color col = (Color)text.getTrait(Trait.COLOR); - boolean pclFont = allTextAsBitmaps + boolean pclFont = pclUtil.isAllTextAsBitmaps() ? false - : setFont(fontname, fontsize, text.getText()); + : HardcodedFonts.setFont(gen, fontname, fontsize, text.getText()); if (pclFont) { //this.currentFill = col; if (col != null) { @@ -1132,7 +965,7 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { RendererContext context = super.createRendererContext( x, y, width, height, foreignAttributes); context.setProperty(PCLRendererContextConstants.PCL_COLOR_CANVAS, - Boolean.valueOf(this.useColorCanvas)); + Boolean.valueOf(pclUtil.isColorCanvasEnabled())); return context; } @@ -1354,7 +1187,7 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { if (bpsBefore == null && bpsAfter == null && bpsStart == null && bpsEnd == null) { return; //no borders to paint } - if (qualityBeforeSpeed) { + if (pclUtil.isQualityBeforeSpeed()) { drawQualityBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); } else { drawFastBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); @@ -1673,15 +1506,4 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants { super.renderLeader(area); } - /** - * Controls whether all text should be generated as bitmaps or only text for which there's - * no native font. - * @param allTextAsBitmaps true if all text should be painted as bitmaps - */ - public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { - this.allTextAsBitmaps = allTextAsBitmaps; - } - - - } diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java index 6d57825cd..a97f551bb 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java +++ b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java @@ -19,17 +19,34 @@ package org.apache.fop.render.pcl; +import java.awt.Graphics2D; +import java.util.List; + import org.apache.avalon.framework.configuration.Configuration; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fonts.FontCollection; +import org.apache.fop.fonts.FontEventAdapter; +import org.apache.fop.fonts.FontEventListener; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontManager; +import org.apache.fop.fonts.FontResolver; +import org.apache.fop.render.DefaultFontResolver; import org.apache.fop.render.PrintRendererConfigurator; import org.apache.fop.render.Renderer; +import org.apache.fop.render.intermediate.IFDocumentHandler; +import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; +import org.apache.fop.render.java2d.Base14FontCollection; +import org.apache.fop.render.java2d.ConfiguredFontCollection; +import org.apache.fop.render.java2d.InstalledFontCollection; +import org.apache.fop.render.java2d.Java2DFontMetrics; /** * PCL Renderer configurator */ -public class PCLRendererConfigurator extends PrintRendererConfigurator { +public class PCLRendererConfigurator extends PrintRendererConfigurator + implements IFDocumentHandlerConfigurator { /** * Default constructor @@ -50,30 +67,75 @@ public class PCLRendererConfigurator extends PrintRendererConfigurator { if (cfg != null) { PCLRenderer pclRenderer = (PCLRenderer)renderer; - String rendering = cfg.getChild("rendering").getValue(null); - if ("quality".equalsIgnoreCase(rendering)) { - pclRenderer.setQualityBeforeSpeed(true); - } else if ("speed".equalsIgnoreCase(rendering)) { - pclRenderer.setQualityBeforeSpeed(false); - } else if (rendering != null) { - throw new FOPException( - "Valid values for 'rendering' are 'quality' and 'speed'. Value found: " - + rendering); - } - - String textRendering = cfg.getChild("text-rendering").getValue(null); - if ("bitmap".equalsIgnoreCase(textRendering)) { - pclRenderer.setAllTextAsBitmaps(true); - } else if ("auto".equalsIgnoreCase(textRendering)) { - pclRenderer.setAllTextAsBitmaps(false); - } else if (textRendering != null) { - throw new FOPException( - "Valid values for 'text-rendering' are 'auto' and 'bitmap'. Value found: " - + textRendering); - } - - pclRenderer.setPJLDisabled(cfg.getChild("disable-pjl").getValueAsBoolean(false)); + PCLRenderingUtil pclUtil = pclRenderer.getPCLUtil(); + configure(cfg, pclUtil); } super.configure(renderer); } + + private void configure(Configuration cfg, PCLRenderingUtil pclUtil) throws FOPException { + String rendering = cfg.getChild("rendering").getValue(null); + if ("quality".equalsIgnoreCase(rendering)) { + pclUtil.setQualityBeforeSpeed(true); + } else if ("speed".equalsIgnoreCase(rendering)) { + pclUtil.setQualityBeforeSpeed(false); + } else if (rendering != null) { + throw new FOPException( + "Valid values for 'rendering' are 'quality' and 'speed'. Value found: " + + rendering); + } + + String textRendering = cfg.getChild("text-rendering").getValue(null); + if ("bitmap".equalsIgnoreCase(textRendering)) { + pclUtil.setAllTextAsBitmaps(true); + } else if ("auto".equalsIgnoreCase(textRendering)) { + pclUtil.setAllTextAsBitmaps(false); + } else if (textRendering != null) { + throw new FOPException( + "Valid values for 'text-rendering' are 'auto' and 'bitmap'. Value found: " + + textRendering); + } + + pclUtil.setPJLDisabled(cfg.getChild("disable-pjl").getValueAsBoolean(false)); + } + + // ---=== IFDocumentHandler configuration ===--- + + /** {@inheritDoc} */ + public void configure(IFDocumentHandler documentHandler) throws FOPException { + Configuration cfg = super.getRendererConfig(documentHandler.getMimeType()); + if (cfg != null) { + PCLDocumentHandler pclDocumentHandler = (PCLDocumentHandler)documentHandler; + PCLRenderingUtil pclUtil = pclDocumentHandler.getPCLUtil(); + configure(cfg, pclUtil); + } + } + + /** {@inheritDoc} */ + public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo) + throws FOPException { + FontManager fontManager = userAgent.getFactory().getFontManager(); + + Graphics2D graphics2D = Java2DFontMetrics.createFontMetricsGraphics2D(); + + List fontCollections = new java.util.ArrayList(); + fontCollections.add(new Base14FontCollection(graphics2D)); + fontCollections.add(new InstalledFontCollection(graphics2D)); + + Configuration cfg = super.getRendererConfig(documentHandler.getMimeType()); + if (cfg != null) { + FontResolver fontResolver = new DefaultFontResolver(userAgent); + FontEventListener listener = new FontEventAdapter( + userAgent.getEventBroadcaster()); + List fontList = buildFontList(cfg, fontResolver, listener); + fontCollections.add(new ConfiguredFontCollection(fontResolver, fontList)); + } + + fontManager.setup(fontInfo, + (FontCollection[])fontCollections.toArray( + new FontCollection[fontCollections.size()])); + documentHandler.setFontInfo(fontInfo); + } + + } diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java b/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java new file mode 100644 index 000000000..4c41cbf58 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLRenderingContext.java @@ -0,0 +1,96 @@ +/* + * 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.geom.Point2D; + +import org.apache.xmlgraphics.util.MimeConstants; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.render.AbstractRenderingContext; + +/** + * Rendering context for PCL production. The class is abstract and must be subclassed to + * provide the missing functionality. + */ +public abstract class PCLRenderingContext extends AbstractRenderingContext { + + private PCLGenerator generator; + private PCLRenderingUtil pclUtil; + private boolean sourceTransparency = false; + + /** + * Main constructor. + * @param userAgent the user agent + * @param generator the PCL generator + */ + public PCLRenderingContext(FOUserAgent userAgent, + PCLGenerator generator, PCLRenderingUtil pclUtil) { + super(userAgent); + this.generator = generator; + this.pclUtil = pclUtil; + } + + /** {@inheritDoc} */ + public String getMimeType() { + return MimeConstants.MIME_PCL; //not applicable + } + + /** + * Returns the PCL generator. + * @return the PCL generator + */ + public PCLGenerator getPCLGenerator() { + return this.generator; + } + + /** + * Returns the PCL rendering utility. + * @return the PCL rendering utility. + */ + public PCLRenderingUtil getPCLUtil() { + return this.pclUtil; + } + + /** + * Indicates whether source transparency should be enabled when painting bitmaps. + * @return true when source transparency is enabled + */ + public boolean isSourceTransparencyEnabled() { + return this.sourceTransparency; + } + + /** + * Enables or disables source transparency when painting bitmaps. + * @param value true to enable source transparency, false to disable + */ + public void setSourceTransparencyEnabled(boolean value) { + this.sourceTransparency = value; + } + + /** + * Transforms a point into the PCL coordinate system. + * @param x the X coordinate + * @param y the Y coordinate + * @return the transformed point in PCL coordinates + */ + public abstract Point2D transformedPoint(int x, int y); + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java index 63906232d..01ca79fff 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java +++ b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java @@ -19,11 +19,16 @@ 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.util.UnitConv; + import org.apache.fop.apps.FOUserAgent; /** @@ -65,16 +70,6 @@ public class PCLRenderingUtil { initialize(); } - private static boolean booleanValueOf(Object obj) { - if (obj instanceof Boolean) { - return ((Boolean)obj).booleanValue(); - } else if (obj instanceof String) { - return Boolean.valueOf((String)obj).booleanValue(); - } else { - throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected."); - } - } - private void initialize() { } @@ -95,6 +90,14 @@ public class PCLRenderingUtil { this.qualityBeforeSpeed = qualityBeforeSpeed; } + /** + * Indicates whether quality is more important than speed. + * @return true if quality is favored over speed + */ + public boolean isQualityBeforeSpeed() { + return this.qualityBeforeSpeed; + } + /** * Controls whether PJL commands shall be generated by the PCL renderer. * @param disable true to disable PJL commands @@ -111,6 +114,15 @@ public class PCLRenderingUtil { return this.disabledPJL; } + /** + * Controls whether all text should be generated as bitmaps or only text for which there's + * no native font. + * @param allTextAsBitmaps true if all text should be painted as bitmaps + */ + public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { + this.allTextAsBitmaps = allTextAsBitmaps; + } + /** * Indicates whether all text shall be painted as bitmaps. * @return true if all text shall be painted as bitmaps @@ -119,6 +131,14 @@ public class PCLRenderingUtil { return this.allTextAsBitmaps; } + /** + * Indicates whether a color canvas is used when creating bitmap images. + * @return true if a color canvas is used. + */ + public boolean isColorCanvasEnabled() { + return this.useColorCanvas; + } + /** * Determines the print direction based on the given transformation matrix. This method * only detects right angles (0, 90, 180, 270). If any other angle is determined, 0 is @@ -143,5 +163,63 @@ public class PCLRenderingUtil { return newDir; } + /** + * Returns a coordinate in PCL's coordinate system when given a coordinate in the user + * coordinate system. + * @param x the X coordinate + * @param y the Y coordinate + * @param transform the currently valid transformation matrix + * @param pageDefinition the currently valid page definition + * @param printDirection the currently valid print direction + * @return the transformed point + */ + public static Point2D transformedPoint(int x, int y, AffineTransform transform, + PCLPageDefinition pageDefinition, int printDirection) { + if (log.isTraceEnabled()) { + log.trace("Current transform: " + transform); + } + Point2D.Float orgPoint = new Point2D.Float(x, y); + Point2D.Float transPoint = new Point2D.Float(); + transform.transform(orgPoint, transPoint); + //At this point we have the absolute position in FOP's coordinate system + + //Now get PCL coordinates taking the current print direction and the logical page + //into account. + Dimension pageSize = pageDefinition.getPhysicalPageSize(); + Rectangle logRect = pageDefinition.getLogicalPageRect(); + switch (printDirection) { + case 0: + transPoint.x -= logRect.x; + transPoint.y -= logRect.y; + break; + case 90: + float ty = transPoint.x; + transPoint.x = pageSize.height - transPoint.y; + transPoint.y = ty; + transPoint.x -= logRect.y; + transPoint.y -= logRect.x; + break; + case 180: + transPoint.x = pageSize.width - transPoint.x; + transPoint.y = pageSize.height - transPoint.y; + transPoint.x -= pageSize.width - logRect.x - logRect.width; + transPoint.y -= pageSize.height - logRect.y - logRect.height; + //The next line is odd and is probably necessary due to the default value of the + //Text Length command: "1/2 inch less than maximum text length" + //I wonder why this isn't necessary for the 90 degree rotation. *shrug* + transPoint.y -= UnitConv.in2mpt(0.5); + break; + case 270: + float tx = transPoint.y; + transPoint.y = pageSize.width - transPoint.x; + transPoint.x = tx; + transPoint.x -= pageSize.height - logRect.y - logRect.height; + transPoint.y -= pageSize.width - logRect.x - logRect.width; + break; + default: + throw new IllegalStateException("Illegal print direction: " + printDirection); + } + return transPoint; + } } diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 5273220e7..5baaedbea 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -62,9 +62,6 @@ public class PDFPainter extends AbstractIFPainter { private PDFDocumentHandler documentHandler; - /** Holds the intermediate format state */ - protected IFState state; - /** The current content generator */ protected PDFContentGenerator generator; @@ -324,27 +321,4 @@ public class PDFPainter extends AbstractIFPainter { textutil.writeTJ(); } - /** {@inheritDoc} */ - public void setFont(String family, String style, Integer weight, String variant, Integer size, - Color color) throws IFException { - if (family != null) { - state.setFontFamily(family); - } - if (style != null) { - state.setFontStyle(style); - } - if (weight != null) { - state.setFontWeight(weight.intValue()); - } - if (variant != null) { - state.setFontVariant(variant); - } - if (size != null) { - state.setFontSize(size.intValue()); - } - if (color != null) { - state.setTextColor(color); - } - } - } diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java index e0d12d9c7..c9b7b7e3b 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java @@ -68,9 +68,6 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants { /** logging instance */ private static Log log = LogFactory.getLog(SVGPainter.class); - /** Holds the intermediate format state */ - protected IFState state; - private AbstractSVGDocumentHandler parent; /** The SAX content handler that receives the generated XML events. */ @@ -350,29 +347,6 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants { } } - /** {@inheritDoc} */ - public void setFont(String family, String style, Integer weight, String variant, Integer size, - Color color) throws IFException { - if (family != null) { - state.setFontFamily(family); - } - if (style != null) { - state.setFontStyle(style); - } - if (weight != null) { - state.setFontWeight(weight.intValue()); - } - if (variant != null) { - state.setFontVariant(variant); - } - if (size != null) { - state.setFontSize(size.intValue()); - } - if (color != null) { - state.setTextColor(color); - } - } - private void leaveTextMode() throws SAXException { assert this.mode == MODE_TEXT; handler.endElement("g");