From: Jeremias Maerki Date: Mon, 24 Jul 2006 13:16:23 +0000 (+0000) Subject: Promoting the PCL renderer from sandbox to the main source tree. X-Git-Tag: fop-0_93~153 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=fe9c40f495aee9919b86a746dfd587069f7cc3f3;p=xmlgraphics-fop.git Promoting the PCL renderer from sandbox to the main source tree. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@425038 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java b/src/java/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java new file mode 100644 index 000000000..43580446f --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java @@ -0,0 +1,52 @@ +/* + * 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.pcl; + +import java.awt.RenderingHints; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorConvertOp; +import java.awt.image.RenderedImage; + +/** + * Default implementation of the MonochromeBitmapConverter which uses the Java Class Library + * to convert grayscale bitmaps to monochrome bitmaps. + */ +public class DefaultMonochromeBitmapConverter implements + MonochromeBitmapConverter { + + /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */ + public void setHint(String name, String value) { + //ignore, not supported + } + + /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */ + public RenderedImage convertToMonochrome(BufferedImage img) { + BufferedImage buf = new BufferedImage(img.getWidth(), img.getHeight(), + BufferedImage.TYPE_BYTE_BINARY); + RenderingHints hints = new RenderingHints(null); + //This hint doesn't seem to make a difference :-( + hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); + ColorConvertOp op = new ColorConvertOp( + ColorSpace.getInstance(ColorSpace.CS_GRAY), hints); + op.filter(img, buf); + return buf; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java b/src/java/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java new file mode 100644 index 000000000..4818b2b1d --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java @@ -0,0 +1,97 @@ +/* + * 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.pcl; + +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.ParameterBlock; + +import javax.media.jai.ColorCube; +import javax.media.jai.ImageLayout; +import javax.media.jai.JAI; +import javax.media.jai.KernelJAI; +import javax.media.jai.LookupTableJAI; +import javax.media.jai.PlanarImage; + +/** + * Implementation of the MonochromeBitmapConverter which uses Java Advanced Imaging (JAI) + * to convert grayscale bitmaps to monochrome bitmaps. JAI provides better dithering options + * including error diffusion dithering. + *

+ * If you call setHint("quality", "true") on the instance you can enabled error diffusion + * dithering which produces a nicer result but is also a lot slower. + */ +public class JAIMonochromeBitmapConverter implements + MonochromeBitmapConverter { + + private boolean isErrorDiffusion = false; + + /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */ + public void setHint(String name, String value) { + if ("quality".equalsIgnoreCase(name)) { + isErrorDiffusion = "true".equalsIgnoreCase(value); + } + } + + /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */ + public RenderedImage convertToMonochrome(BufferedImage img) { + if (img.getColorModel().getColorSpace().getNumComponents() != 1) { + throw new IllegalArgumentException("Source image must be a grayscale image!"); + } + + // Load the ParameterBlock for the dithering operation + // and set the operation name. + ParameterBlock pb = new ParameterBlock(); + pb.addSource(img); + String opName = null; + if (isErrorDiffusion) { + opName = "errordiffusion"; + LookupTableJAI lut = new LookupTableJAI(new byte[] {(byte)0x00, (byte)0xff}); + pb.add(lut); + pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG); + } else { + opName = "ordereddither"; + //Create the color cube. + ColorCube colorMap = ColorCube.createColorCube(DataBuffer.TYPE_BYTE, + 0, new int[] {2}); + pb.add(colorMap); + pb.add(KernelJAI.DITHER_MASK_441); + } + + //Create an image layout for a monochrome b/w image + ImageLayout layout = new ImageLayout(); + byte[] map = new byte[] {(byte)0x00, (byte)0xff}; + ColorModel cm = new IndexColorModel(1, 2, map, map, map); + layout.setColorModel(cm); + + // Create a hint containing the layout. + RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); + + // Dither the image. + PlanarImage dst = JAI.create(opName, pb, hints); + + //Convert it to a BufferedImage + return dst.getAsBufferedImage(); + } + +} diff --git a/src/java/org/apache/fop/render/pcl/MonochromeBitmapConverter.java b/src/java/org/apache/fop/render/pcl/MonochromeBitmapConverter.java new file mode 100644 index 000000000..f2db9d798 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/MonochromeBitmapConverter.java @@ -0,0 +1,43 @@ +/* + * 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.pcl; + +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; + +/** + * Interface for converters that convert grayscale images to monochrome (1-bit) bitmap images. + */ +public interface MonochromeBitmapConverter { + + /** + * Sets a hint to the implementation + * @param name the name of the hint + * @param value the value + */ + void setHint(String name, String value); + + /** + * Converts a grayscale bitmap image to a monochrome (1-bit) b/w bitmap image. + * @param img the grayscale image + * @return the converted monochrome image + */ + RenderedImage convertToMonochrome(BufferedImage img); + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java new file mode 100644 index 000000000..8612a51e6 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -0,0 +1,751 @@ +/* + * 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.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.IndexColorModel; +import java.awt.image.LookupOp; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.OutputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import org.apache.fop.util.UnitConv; +import org.apache.xmlgraphics.image.GraphicsUtil; + +/** + * This class provides methods for generating PCL print files. + */ +public class PCLGenerator { + + /** The ESC (escape) character */ + public static final char ESC = '\033'; + + /** A list of all supported resolutions in PCL (values in dpi) */ + public static final int[] PCL_RESOLUTIONS = new int[] {75, 100, 150, 200, 300, 600}; + + private final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + private final DecimalFormat df2 = new DecimalFormat("0.##", symbols); + private final DecimalFormat df4 = new DecimalFormat("0.####", symbols); + + private OutputStream out; + + private boolean currentSourceTransparency = true; + private boolean currentPatternTransparency = true; + + private int maxBitmapResolution = PCL_RESOLUTIONS[PCL_RESOLUTIONS.length - 1]; + + /** + * Main constructor. + * @param out the OutputStream to write the PCL stream to + */ + public PCLGenerator(OutputStream out) { + this.out = out; + } + + /** + * Main constructor. + * @param out the OutputStream to write the PCL stream to + * @param maxResolution the maximum resolution to encode bitmap images at + */ + public PCLGenerator(OutputStream out, int maxResolution) { + this(out); + boolean found = false; + for (int i = 0; i < PCL_RESOLUTIONS.length; i++) { + if (PCL_RESOLUTIONS[i] == maxResolution) { + found = true; + break; + } + } + if (!found) { + throw new IllegalArgumentException("Illegal value for maximum resolution!"); + } + this.maxBitmapResolution = maxResolution; + } + + /** @return the OutputStream that this generator writes to */ + public OutputStream getOutputStream() { + return this.out; + } + + /** @return the maximum resolution to encode bitmap images at */ + public int getMaximumBitmapResolution() { + return this.maxBitmapResolution; + } + + /** + * Writes a PCL escape command to the output stream. + * @param cmd the command (without the ESCAPE character) + * @throws IOException In case of an I/O error + */ + public void writeCommand(String cmd) throws IOException { + out.write(27); //ESC + out.write(cmd.getBytes("US-ASCII")); + } + + /** + * Writes raw text (in ISO-8859-1 encoding) to the output stream. + * @param s the text + * @throws IOException In case of an I/O error + */ + public void writeText(String s) throws IOException { + out.write(s.getBytes("ISO-8859-1")); + } + + /** + * Formats a double value with two decimal positions for PCL output. + * + * @param value value to format + * @return the formatted value + */ + public final String formatDouble2(double value) { + return df2.format(value); + } + + /** + * Formats a double value with four decimal positions for PCL output. + * + * @param value value to format + * @return the formatted value + */ + public final String formatDouble4(double value) { + return df4.format(value); + } + + /** + * Sends the universal end of language command (UEL). + * @throws IOException In case of an I/O error + */ + public void universalEndOfLanguage() throws IOException { + writeCommand("%-12345X"); + } + + /** + * Resets the printer and restores the user default environment. + * @throws IOException In case of an I/O error + */ + public void resetPrinter() throws IOException { + writeCommand("E"); + } + + /** + * Sends the job separation command. + * @throws IOException In case of an I/O error + */ + public void separateJobs() throws IOException { + writeCommand("&l1T"); + } + + /** + * Sends the form feed character. + * @throws IOException In case of an I/O error + */ + public void formFeed() throws IOException { + out.write(12); //=OC ("FF", Form feed) + } + + /** + * Sets the unit of measure. + * @param value the resolution value (units per inch) + * @throws IOException In case of an I/O error + */ + public void setUnitOfMeasure(int value) throws IOException { + writeCommand("&u" + value + "D"); + } + + /** + * Sets the raster graphics resolution + * @param value the resolution value (units per inch) + * @throws IOException In case of an I/O error + */ + public void setRasterGraphicsResolution(int value) throws IOException { + writeCommand("*t" + value + "R"); + } + + /** + * Selects the page size. + * @param selector the integer representing the page size + * @throws IOException In case of an I/O error + */ + public void selectPageSize(int selector) throws IOException { + writeCommand("&l" + selector + "A"); + } + + /** + * Selects the paper source. The parameter is usually printer-specific. Usually, "1" is the + * default tray, "2" is the manual paper feed, "3" is the manual envelope feed, "4" is the + * "lower" tray and "7" is "auto-select". Consult the technical reference for your printer + * for all available values. + * @param selector the integer representing the paper source/tray + * @throws IOException In case of an I/O error + */ + public void selectPaperSource(int selector) throws IOException { + writeCommand("&l" + selector + "H"); + } + + /** + * Clears the horizontal margins. + * @throws IOException In case of an I/O error + */ + public void clearHorizontalMargins() throws IOException { + writeCommand("9"); + } + + /** + * The Top Margin command designates the number of lines between + * the top of the logical page and the top of the text area. + * @param numberOfLines the number of lines (See PCL specification for details) + * @throws IOException In case of an I/O error + */ + public void setTopMargin(int numberOfLines) throws IOException { + writeCommand("&l" + numberOfLines + "E"); + } + + /** + * The Text Length command can be used to define the bottom border. See the PCL specification + * for details. + * @param numberOfLines the number of lines + * @throws IOException In case of an I/O error + */ + public void setTextLength(int numberOfLines) throws IOException { + writeCommand("&l" + numberOfLines + "F"); + } + + /** + * Sets the Vertical Motion Index (VMI). + * @param value the VMI value + * @throws IOException In case of an I/O error + */ + public void setVMI(double value) throws IOException { + writeCommand("&l" + formatDouble4(value) + "C"); + } + + /** + * Sets the cursor to a new absolute coordinate. + * @param x the X coordinate (in millipoints) + * @param y the Y coordinate (in millipoints) + * @throws IOException In case of an I/O error + */ + public void setCursorPos(double x, double y) throws IOException { + if (x < 0) { + //A negative x value will result in a relative movement so go to "0" first. + //But this will most probably have no effect anyway since you can't paint to the left + //of the logical page + writeCommand("&a0h" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); + } else { + writeCommand("&a" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); + } + } + + /** + * Pushes the current cursor position on a stack (stack size: max 20 entries) + * @throws IOException In case of an I/O error + */ + public void pushCursorPos() throws IOException { + writeCommand("&f0S"); + } + + /** + * Pops the current cursor position from the stack. + * @throws IOException In case of an I/O error + */ + public void popCursorPos() throws IOException { + writeCommand("&f1S"); + } + + /** + * Changes the current print direction while maintaining the current cursor position. + * @param rotate the rotation angle (counterclockwise), one of 0, 90, 180 and 270. + * @throws IOException In case of an I/O error + */ + public void changePrintDirection(int rotate) throws IOException { + writeCommand("&a" + rotate + "P"); + } + + /** + * Enters the HP GL/2 mode. + * @param restorePreviousHPGL2Cursor true if the previous HP GL/2 pen position should be + * restored, false if the current position is maintained + * @throws IOException In case of an I/O error + */ + public void enterHPGL2Mode(boolean restorePreviousHPGL2Cursor) throws IOException { + if (restorePreviousHPGL2Cursor) { + writeCommand("%0B"); + } else { + writeCommand("%1B"); + } + } + + /** + * Enters the PCL mode. + * @param restorePreviousPCLCursor true if the previous PCL cursor position should be restored, + * false if the current position is maintained + * @throws IOException In case of an I/O error + */ + public void enterPCLMode(boolean restorePreviousPCLCursor) throws IOException { + if (restorePreviousPCLCursor) { + writeCommand("%0A"); + } else { + writeCommand("%1A"); + } + } + + /** + * Generate a filled rectangle at the current cursor position. + * + * @param w the width in millipoints + * @param h the height in millipoints + * @param col the fill color + * @throws IOException In case of an I/O error + */ + protected void fillRect(int w, int h, Color col) throws IOException { + if ((w == 0) || (h == 0)) { + return; + } + if (h < 0) { + h *= -1; + } else { + //y += h; + } + + setPatternTransparencyMode(false); + writeCommand("*c" + formatDouble4(w / 100) + "h" + + formatDouble4(h / 100) + "V"); + int lineshade = convertToPCLShade(col); + writeCommand("*c" + lineshade + "G"); + writeCommand("*c2P"); + // Reset pattern transparency mode. + setPatternTransparencyMode(true); + } + + /** + * Sets the source transparency mode. + * @param transparent true if transparent, false for opaque + * @throws IOException In case of an I/O error + */ + public void setSourceTransparencyMode(boolean transparent) throws IOException { + setTransparencyMode(transparent, currentPatternTransparency); + } + + /** + * Sets the pattern transparency mode. + * @param transparent true if transparent, false for opaque + * @throws IOException In case of an I/O error + */ + public void setPatternTransparencyMode(boolean transparent) throws IOException { + setTransparencyMode(currentSourceTransparency, transparent); + } + + /** + * Sets the transparency modes. + * @param source source transparency: true if transparent, false for opaque + * @param pattern pattern transparency: true if transparent, false for opaque + * @throws IOException In case of an I/O error + */ + public void setTransparencyMode(boolean source, boolean pattern) throws IOException { + if (source != currentSourceTransparency && pattern != currentPatternTransparency) { + writeCommand("*v" + (source ? '0' : '1') + "n" + (pattern ? '0' : '1') + "O"); + } else if (source != currentSourceTransparency) { + writeCommand("*v" + (source ? '0' : '1') + "N"); + } else if (pattern != currentPatternTransparency) { + writeCommand("*v" + (pattern ? '0' : '1') + "O"); + } + this.currentSourceTransparency = source; + this.currentPatternTransparency = pattern; + } + + /** + * Convert an RGB color value to a grayscale from 0 to 100. + * @param r the red component + * @param g the green component + * @param b the blue component + * @return the gray value + */ + public final int convertToGray(int r, int g, int b) { + return (r * 30 + g * 59 + b * 11) / 100; + } + + /** + * Convert a Color value to a PCL shade value (0-100). + * @param col the color + * @return the PCL shade value (100=black) + */ + public final int convertToPCLShade(Color col) { + float gray = convertToGray(col.getRed(), col.getGreen(), col.getBlue()) / 255f; + return (int)(100 - (gray * 100f)); + } + + /** + * Select the current pattern + * @param patternID the pattern ID (*c#G command) + * @param pattern the pattern type (*v#T command) + * @throws IOException In case of an I/O error + */ + public void selectCurrentPattern(int patternID, int pattern) throws IOException { + if (pattern > 1) { + writeCommand("*c" + patternID + "G"); + } + writeCommand("*v" + pattern + "T"); + } + + /** + * Indicates whether an image is a monochrome (b/w) image. + * @param img the image + * @return true if it's a monochrome image + */ + public static boolean isMonochromeImage(RenderedImage img) { + ColorModel cm = img.getColorModel(); + if (cm instanceof IndexColorModel) { + IndexColorModel icm = (IndexColorModel)cm; + return icm.getMapSize() == 2; + } else { + return false; + } + } + + /** + * Indicates whether an image is a grayscale image. + * @param img the image + * @return true if it's a grayscale image + */ + public static boolean isGrayscaleImage(RenderedImage img) { + return (img.getColorModel().getColorSpace().getNumComponents() == 1); + } + + private MonochromeBitmapConverter createMonochromeBitmapConverter() { + MonochromeBitmapConverter converter = null; + try { + String clName = "org.apache.fop.render.pcl.JAIMonochromeBitmapConverter"; + Class clazz = Class.forName(clName); + converter = (MonochromeBitmapConverter)clazz.newInstance(); + } catch (ClassNotFoundException cnfe) { + // Class was not compiled so is not available. Simply ignore. + } catch (LinkageError le) { + // This can happen if fop was build with support for a + // particular provider (e.g. a binary fop distribution) + // but the required support files (i.e. JAI) are not + // available in the current runtime environment. + // Simply continue with the backup implementation. + } catch (InstantiationException e) { + // Problem instantiating the class, simply continue with the backup implementation + } catch (IllegalAccessException e) { + // Problem instantiating the class, simply continue with the backup implementation + } + if (converter == null) { + converter = new DefaultMonochromeBitmapConverter(); + } + return converter; + } + + private int calculatePCLResolution(int resolution) { + return calculatePCLResolution(resolution, false); + } + + /** + * Calculates the ideal PCL resolution for a given resolution. + * @param resolution the input resolution + * @param increased true if you want to go to a higher resolution, for example if you + * convert grayscale or color images to monochrome images so dithering has + * a chance to generate better quality. + * @return the resulting PCL resolution (one of 75, 100, 150, 200, 300, 600) + */ + private int calculatePCLResolution(int resolution, boolean increased) { + int choice = -1; + for (int i = PCL_RESOLUTIONS.length - 2; i >= 0; i--) { + if (resolution > PCL_RESOLUTIONS[i]) { + int idx = i + 1; + if (idx < PCL_RESOLUTIONS.length - 2) { + idx += increased ? 2 : 0; + } else if (idx < PCL_RESOLUTIONS.length - 1) { + idx += increased ? 1 : 0; + } + choice = idx; + break; + //return PCL_RESOLUTIONS[idx]; + } + } + if (choice < 0) { + choice = (increased ? 2 : 0); + } + while (choice > 0 && PCL_RESOLUTIONS[choice] > getMaximumBitmapResolution()) { + choice--; + } + return PCL_RESOLUTIONS[choice]; + } + + private boolean isValidPCLResolution(int resolution) { + return resolution == calculatePCLResolution(resolution); + } + + private Dimension getAdjustedDimension(Dimension orgDim, double orgResolution, + int pclResolution) { + if (orgResolution == pclResolution) { + return orgDim; + } else { + Dimension result = new Dimension(); + result.width = (int)Math.round((double)orgDim.width * pclResolution / orgResolution); + result.height = (int)Math.round((double)orgDim.height * pclResolution / orgResolution); + return result; + } + } + + //Threshold table to convert an alpha channel (8-bit) into a clip mask (1-bit) + private static final byte[] THRESHOLD_TABLE = new byte[256]; + static { // Initialize the arrays + for (int i = 0; i < 256; i++) { + THRESHOLD_TABLE[i] = (byte) ((i < 240) ? 255 : 0); + } + } + + private RenderedImage getMask(RenderedImage img, Dimension targetDim) { + ColorModel cm = img.getColorModel(); + if (cm.hasAlpha()) { + BufferedImage alpha = new BufferedImage(img.getWidth(), img.getHeight(), + BufferedImage.TYPE_BYTE_GRAY); + Raster raster = img.getData(); + GraphicsUtil.copyBand(raster, cm.getNumColorComponents(), alpha.getRaster(), 0); + + BufferedImageOp op1 = new LookupOp(new ByteLookupTable(0, THRESHOLD_TABLE), null); + BufferedImage alphat = op1.filter(alpha, null); + + BufferedImage mask; + if (true) { + mask = new BufferedImage(targetDim.width, targetDim.height, + BufferedImage.TYPE_BYTE_BINARY); + } else { + byte[] arr = {(byte)0, (byte)0xff}; + ColorModel colorModel = new IndexColorModel(1, 2, arr, arr, arr); + WritableRaster wraster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, + targetDim.width, targetDim.height, 1, 1, null); + mask = new BufferedImage(colorModel, wraster, false, null); + } + + Graphics2D g2d = mask.createGraphics(); + try { + AffineTransform at = new AffineTransform(); + double sx = targetDim.getWidth() / img.getWidth(); + double sy = targetDim.getHeight() / img.getHeight(); + at.scale(sx, sy); + g2d.drawRenderedImage(alphat, at); + } finally { + g2d.dispose(); + } + /* + try { + BatchDiffer.saveAsPNG(alpha, new java.io.File("D:/out-alpha.png")); + BatchDiffer.saveAsPNG(mask, new java.io.File("D:/out-mask.png")); + } catch (IOException e) { + e.printStackTrace(); + }*/ + return mask; + } else { + return null; + } + } + + /** + * Paint a bitmap at the current cursor position. The bitmap is converted to a monochrome + * (1-bit) bitmap image. + * @param img the bitmap image + * @param targetDim the target Dimention (in mpt) + * @param sourceTransparency true if the background should not be erased + * @throws IOException In case of an I/O error + */ + public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency) + throws IOException { + double targetResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width); + 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); + boolean scaled = !orgDim.equals(effDim); + + boolean monochrome = isMonochromeImage(img); + if (!monochrome) { + //Transparency mask disabled. Doesn't work reliably + final boolean transparencyDisabled = true; + RenderedImage mask = (transparencyDisabled ? null : getMask(img, effDim)); + if (mask != null) { + pushCursorPos(); + selectCurrentPattern(0, 1); //Solid white + setTransparencyMode(true, true); + paintMonochromeBitmap(mask, effResolution); + popCursorPos(); + } + + BufferedImage src = null; + if (img instanceof BufferedImage && !scaled) { + if (!isGrayscaleImage(img) || img.getColorModel().hasAlpha()) { + src = new BufferedImage(effDim.width, effDim.height, + BufferedImage.TYPE_BYTE_GRAY); + ColorConvertOp op = new ColorConvertOp( + ColorSpace.getInstance(ColorSpace.CS_GRAY), null); + op.filter((BufferedImage)img, src); + } else { + src = (BufferedImage)img; + } + } + if (src == null) { + src = new BufferedImage(effDim.width, effDim.height, + BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = src.createGraphics(); + try { + AffineTransform at = new AffineTransform(); + double sx = effDim.getWidth() / orgDim.getWidth(); + double sy = effDim.getHeight() / orgDim.getHeight(); + at.scale(sx, sy); + g2d.drawRenderedImage(img, at); + } finally { + g2d.dispose(); + } + } + MonochromeBitmapConverter converter = createMonochromeBitmapConverter(); + converter.setHint("quality", "false"); + + BufferedImage buf = (BufferedImage)converter.convertToMonochrome(src); + + RenderedImage red = buf; + selectCurrentPattern(0, 0); //Solid black + setTransparencyMode(sourceTransparency || mask != null, true); + paintMonochromeBitmap(red, effResolution); + } else { + //TODO untested! + RenderedImage effImg = img; + if (scaled) { + BufferedImage buf = new BufferedImage(effDim.width, effDim.height, + BufferedImage.TYPE_BYTE_BINARY); + Graphics2D g2d = buf.createGraphics(); + try { + AffineTransform at = new AffineTransform(); + double sx = effDim.getWidth() / orgDim.getWidth(); + double sy = effDim.getHeight() / orgDim.getHeight(); + at.scale(sx, sy); + g2d.drawRenderedImage(img, at); + } finally { + g2d.dispose(); + } + effImg = buf; + } + setSourceTransparencyMode(sourceTransparency); + selectCurrentPattern(0, 0); //Solid black + paintMonochromeBitmap(effImg, effResolution); + } + } + + /** + * Paint a bitmap at the current cursor position. The bitmap must be a monochrome + * (1-bit) bitmap image. + * @param img the bitmap image (must be 1-bit b/w) + * @param resolution the resolution of the image (must be a PCL resolution) + * @throws IOException In case of an I/O error + */ + public void paintMonochromeBitmap(RenderedImage img, int resolution) throws IOException { + if (!isValidPCLResolution(resolution)) { + throw new IllegalArgumentException("Invalid PCL resolution: " + resolution); + } + setRasterGraphicsResolution(resolution); + writeCommand("*r0f" + img.getHeight() + "t" + img.getWidth() + "s1A"); + Raster raster = img.getData(); + boolean monochrome = isMonochromeImage(img); + if (!monochrome) { + throw new IllegalArgumentException("img must be a monochrome image"); + } + + int x = 0; + int y = 0; + int imgw = img.getWidth(); + int imgh = img.getHeight(); + int bytewidth = (imgw / 8); + if ((imgw % 8) != 0) { + bytewidth++; + } + byte ib; + byte[] rle = new byte[bytewidth * 2]; //compressed (RLE) + byte[] uncompressed = new byte[bytewidth]; //uncompressed + int lastcount = -1; + byte lastbyte = 0; + int rlewidth = 0; + + // Transfer graphics data + for (y = 0; y < imgh; y++) { + ib = 0; + for (x = 0; x < imgw; x++) { + int sample = raster.getSample(x, y, 0); + //Set image bit for black + if ((sample == 0)) { + ib |= (1 << (7 - (x % 8))); + } + + //RLE encoding + if ((x % 8) == 7 || ((x + 1) == imgw)) { + if (rlewidth < bytewidth) { + if (lastcount >= 0) { + if (ib == lastbyte) { + lastcount++; + } else { + rle[rlewidth++] = (byte)(lastcount & 0xFF); + rle[rlewidth++] = lastbyte; + lastbyte = ib; + lastcount = 0; + } + } else { + lastbyte = ib; + lastcount = 0; + } + if (lastcount == 255 || ((x + 1) == imgw)) { + rle[rlewidth++] = (byte)(lastcount & 0xFF); + rle[rlewidth++] = lastbyte; + lastbyte = 0; + lastcount = -1; + } + } + uncompressed[x / 8] = ib; + ib = 0; + } + } + if (rlewidth < bytewidth) { + writeCommand("*b1m" + rlewidth + "W"); + this.out.write(rle, 0, rlewidth); + } else { + writeCommand("*b0m" + bytewidth + "W"); + this.out.write(uncompressed); + } + lastcount = -1; + rlewidth = 0; + } + + // End raster graphics + writeCommand("*rB"); + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLGraphics2D.java b/src/java/org/apache/fop/render/pcl/PCLGraphics2D.java new file mode 100644 index 000000000..0e27a8d29 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLGraphics2D.java @@ -0,0 +1,615 @@ +/* + * 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.pcl; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; + +import org.apache.fop.util.UnitConv; +import org.apache.xmlgraphics.java2d.AbstractGraphics2D; +import org.apache.xmlgraphics.java2d.GraphicContext; + +/** + * Graphics2D implementation implementing PCL and HP GL/2. + * Note: This class cannot be used stand-alone to create full PCL documents. + */ +public class PCLGraphics2D extends AbstractGraphics2D { + + /** The PCL generator */ + protected PCLGenerator gen; + + private boolean failOnUnsupportedFeature = true; + private boolean clippingDisabled = false; + + /** + * Create a new PCLGraphics2D. + * @param gen the PCL Generator to paint with + */ + public PCLGraphics2D(PCLGenerator gen) { + super(true); + this.gen = gen; + } + + /** + * Copy constructor + * @param g parent PCLGraphics2D + */ + public PCLGraphics2D(PCLGraphics2D g) { + super(true); + this.gen = g.gen; + } + + /** @see java.awt.Graphics#create() */ + public Graphics create() { + PCLGraphics2D copy = new PCLGraphics2D(this); + copy.setGraphicContext((GraphicContext)getGraphicContext().clone()); + return copy; + } + + /** @see java.awt.Graphics#dispose() */ + public void dispose() { + this.gen = null; + } + + /** + * Sets the GraphicContext + * @param c GraphicContext to use + */ + public void setGraphicContext(GraphicContext c) { + this.gc = c; + } + + /** + * Allows to disable all clipping operations. + * @param value true if clipping should be disabled. + */ + public void setClippingDisabled(boolean value) { + this.clippingDisabled = value; + } + + /** + * Central handler for IOExceptions for this class. + * @param ioe IOException to handle + */ + public void handleIOException(IOException ioe) { + //TODO Surely, there's a better way to do this. + ioe.printStackTrace(); + } + + /** + * Raises an UnsupportedOperationException if this instance is configured to do so and an + * unsupported feature has been requested. Clients can make use of this to fall back to + * a more compatible way of painting a PCL graphic. + * @param msg the error message to be displayed + */ + protected void handleUnsupportedFeature(String msg) { + if (this.failOnUnsupportedFeature) { + throw new UnsupportedOperationException(msg); + } + } + + /** @see java.awt.Graphics2D#getDeviceConfiguration() */ + public GraphicsConfiguration getDeviceConfiguration() { + return GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + } + + /** + * Applies a new Stroke object. + * @param stroke Stroke object to use + * @throws IOException In case of an I/O problem + */ + protected void applyStroke(Stroke stroke) throws IOException { + if (stroke instanceof BasicStroke) { + BasicStroke bs = (BasicStroke)stroke; + + float[] da = bs.getDashArray(); + if (da != null) { + + gen.writeText("UL1,"); + int len = Math.min(20, da.length); + float patternLen = 0.0f; + for (int idx = 0; idx < len; idx++) { + patternLen += da[idx]; + } + if (len == 1) { + patternLen *= 2; + } + for (int idx = 0; idx < len; idx++) { + float perc = da[idx] * 100 / patternLen; + gen.writeText(gen.formatDouble2(perc)); + if (idx < da.length - 1) { + gen.writeText(","); + } + } + if (len == 1) { + gen.writeText("," + gen.formatDouble2(da[0] * 100 / patternLen )); + + } + gen.writeText(";"); + /* TODO Dash phase NYI + float offset = bs.getDashPhase(); + gen.writeln(gen.formatDouble4(offset) + " setdash"); + */ + Point2D ptLen = new Point2D.Double(patternLen, 0); + //interpret as absolute length + getTransform().deltaTransform(ptLen, ptLen); + double transLen = UnitConv.pt2mm(ptLen.distance(0, 0)); + gen.writeText("LT1," + gen.formatDouble4(transLen) + ",1;"); + } else { + gen.writeText("LT;"); + } + + gen.writeText("LA1"); //line cap + int ec = bs.getEndCap(); + switch (ec) { + case BasicStroke.CAP_BUTT: + gen.writeText(",1"); + break; + case BasicStroke.CAP_ROUND: + gen.writeText(",4"); + break; + case BasicStroke.CAP_SQUARE: + gen.writeText(",2"); + break; + default: System.err.println("Unsupported line cap: " + ec); + } + + gen.writeText(",2"); //line join + int lj = bs.getLineJoin(); + switch (lj) { + case BasicStroke.JOIN_MITER: + gen.writeText(",1"); + break; + case BasicStroke.JOIN_ROUND: + gen.writeText(",4"); + break; + case BasicStroke.JOIN_BEVEL: + gen.writeText(",5"); + break; + default: System.err.println("Unsupported line join: " + lj); + } + + float ml = bs.getMiterLimit(); + gen.writeText(",3" + gen.formatDouble4(ml)); + + float lw = bs.getLineWidth(); + Point2D ptSrc = new Point2D.Double(lw, 0); + //Pen widths are set as absolute metric values (WU0;) + Point2D ptDest = getTransform().deltaTransform(ptSrc, null); + double transDist = UnitConv.pt2mm(ptDest.distance(0, 0)); + //System.out.println("--" + ptDest.distance(0, 0) + " " + transDist); + gen.writeText(";PW" + gen.formatDouble4(transDist) + ";"); + + } else { + handleUnsupportedFeature("Unsupported Stroke: " + stroke.getClass().getName()); + } + } + + /** + * Applies a new Paint object. + * @param paint Paint object to use + * @throws IOException In case of an I/O problem + */ + protected void applyPaint(Paint paint) throws IOException { + if (paint instanceof Color) { + Color col = (Color)paint; + int shade = gen.convertToPCLShade(col); + gen.writeText("TR0;FT10," + shade + ";"); + } else { + handleUnsupportedFeature("Unsupported Paint: " + paint.getClass().getName()); + } + } + + private void writeClip(Shape imclip) throws IOException { + if (clippingDisabled) { + return; + } + if (imclip == null) { + //gen.writeText("IW;"); + } else { + handleUnsupportedFeature("Clipping is not supported. Shape: " + imclip); + /* This is an attempt to clip using the "InputWindow" (IW) but this only allows to + * clip a rectangular area. Force falling back to bitmap mode for now. + Rectangle2D bounds = imclip.getBounds2D(); + Point2D p1 = new Point2D.Double(bounds.getX(), bounds.getY()); + Point2D p2 = new Point2D.Double( + bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()); + getTransform().transform(p1, p1); + getTransform().transform(p2, p2); + gen.writeText("IW" + gen.formatDouble4(p1.getX()) + + "," + gen.formatDouble4(p2.getY()) + + "," + gen.formatDouble4(p2.getX()) + + "," + gen.formatDouble4(p1.getY()) + ";"); + */ + } + } + + /** @see java.awt.Graphics2D#draw(java.awt.Shape) */ + public void draw(Shape s) { + try { + AffineTransform trans = getTransform(); + + Shape imclip = getClip(); + writeClip(imclip); + + if (!Color.black.equals(getColor())) { + //TODO PCL 5 doesn't support colored pens, PCL5c has a pen color (PC) command + handleUnsupportedFeature("Only black is supported as stroke color: " + getColor()); + } + applyStroke(getStroke()); + + PathIterator iter = s.getPathIterator(trans); + processPathIteratorStroke(iter); + writeClip(null); + } catch (IOException ioe) { + handleIOException(ioe); + } + } + + /** @see java.awt.Graphics2D#fill(java.awt.Shape) */ + public void fill(Shape s) { + try { + AffineTransform trans = getTransform(); + Shape imclip = getClip(); + writeClip(imclip); + + applyPaint(getPaint()); + + PathIterator iter = s.getPathIterator(trans); + processPathIteratorFill(iter); + writeClip(null); + } catch (IOException ioe) { + handleIOException(ioe); + } + } + + /** + * Processes a path iterator generating the nexessary painting operations. + * @param iter PathIterator to process + * @throws IOException In case of an I/O problem. + */ + public void processPathIteratorStroke(PathIterator iter) throws IOException { + gen.writeText("\n"); + double[] vals = new double[6]; + boolean penDown = false; + double x = 0; + double y = 0; + StringBuffer sb = new StringBuffer(256); + penUp(sb); + while (!iter.isDone()) { + int type = iter.currentSegment(vals); + if (type == PathIterator.SEG_CLOSE) { + gen.writeText("PM;"); + gen.writeText(sb.toString()); + gen.writeText("PM2;EP;"); + sb.setLength(0); + iter.next(); + continue; + } else if (type == PathIterator.SEG_MOVETO) { + gen.writeText(sb.toString()); + sb.setLength(0); + if (penDown) { + penUp(sb); + penDown = false; + } + } else { + if (!penDown) { + penDown(sb); + penDown = true; + } + } + switch (type) { + case PathIterator.SEG_CLOSE: + break; + case PathIterator.SEG_MOVETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + gen.writeText(sb.toString()); + sb.setLength(0); + break; + case PathIterator.SEG_LINETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + break; + case PathIterator.SEG_CUBICTO: + x = vals[4]; + y = vals[5]; + bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); + break; + case PathIterator.SEG_QUADTO: + double originX = x; + double originY = y; + x = vals[2]; + y = vals[3]; + quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); + break; + default: + break; + } + iter.next(); + } + sb.append("\n"); + gen.writeText(sb.toString()); + } + + /** + * Processes a path iterator generating the nexessary painting operations. + * @param iter PathIterator to process + * @throws IOException In case of an I/O problem. + */ + public void processPathIteratorFill(PathIterator iter) throws IOException { + gen.writeText("\n"); + double[] vals = new double[6]; + boolean penDown = false; + double x = 0; + double y = 0; + boolean pendingPM0 = true; + StringBuffer sb = new StringBuffer(256); + penUp(sb); + while (!iter.isDone()) { + int type = iter.currentSegment(vals); + if (type == PathIterator.SEG_CLOSE) { + sb.append("PM1;"); + iter.next(); + continue; + } else if (type == PathIterator.SEG_MOVETO) { + if (penDown) { + penUp(sb); + penDown = false; + } + } else { + if (!penDown) { + penDown(sb); + penDown = true; + } + } + switch (type) { + case PathIterator.SEG_MOVETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + break; + case PathIterator.SEG_LINETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + break; + case PathIterator.SEG_CUBICTO: + x = vals[4]; + y = vals[5]; + bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); + break; + case PathIterator.SEG_QUADTO: + double originX = x; + double originY = y; + x = vals[2]; + y = vals[3]; + quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); + break; + default: + throw new IllegalStateException("Must not get here"); + } + if (pendingPM0) { + pendingPM0 = false; + sb.append("PM;"); + } + iter.next(); + } + sb.append("PM2;"); + fillPolygon(iter.getWindingRule(), sb); + sb.append("\n"); + gen.writeText(sb.toString()); + } + + private void fillPolygon(int windingRule, StringBuffer sb) { + int fillMethod = (windingRule == PathIterator.WIND_EVEN_ODD ? 0 : 1); + sb.append("FP").append(fillMethod).append(";"); + } + + private void plotAbsolute(double x, double y, StringBuffer sb) { + sb.append("PA").append(gen.formatDouble4(x)); + sb.append(",").append(gen.formatDouble4(y)).append(";"); + } + + private void bezierAbsolute(double x1, double y1, double x2, double y2, double x3, double y3, + StringBuffer sb) { + sb.append("BZ").append(gen.formatDouble4(x1)); + sb.append(",").append(gen.formatDouble4(y1)); + sb.append(",").append(gen.formatDouble4(x2)); + sb.append(",").append(gen.formatDouble4(y2)); + sb.append(",").append(gen.formatDouble4(x3)); + sb.append(",").append(gen.formatDouble4(y3)).append(";"); + } + + private void quadraticBezierAbsolute(double originX, double originY, + double x1, double y1, double x2, double y2, StringBuffer sb) { + //Quadratic Bezier curve can be mapped to a normal bezier curve + //See http://pfaedit.sourceforge.net/bezier.html + double nx1 = originX + (2.0 / 3.0) * (x1 - originX); + double ny1 = originY + (2.0 / 3.0) * (y1 - originY); + + double nx2 = nx1 + (1.0 / 3.0) * (x2 - originX); + double ny2 = ny1 + (1.0 / 3.0) * (y2 - originY); + + bezierAbsolute(nx1, ny1, nx2, ny2, x2, y2, sb); + } + + private void penDown(StringBuffer sb) { + sb.append("PD;"); + } + + private void penUp(StringBuffer sb) { + sb.append("PU;"); + } + + /** @see java.awt.Graphics2D#drawString(java.lang.String, float, float) */ + public void drawString(String s, float x, float y) { + java.awt.Font awtFont = getFont(); + FontRenderContext frc = getFontRenderContext(); + GlyphVector gv = awtFont.createGlyphVector(frc, s); + Shape glyphOutline = gv.getOutline(x, y); + fill(glyphOutline); + } + + /** @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) */ + public void drawString(AttributedCharacterIterator iterator, float x, + float y) { + // TODO Auto-generated method stub + handleUnsupportedFeature("drawString NYI"); + } + + /** + * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage, + * java.awt.geom.AffineTransform) + */ + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + handleUnsupportedFeature("Bitmap images are not supported"); + } + + /** + * @see java.awt.Graphics2D#drawRenderableImage(java.awt.image.renderable.RenderableImage, + * java.awt.geom.AffineTransform) + */ + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + handleUnsupportedFeature("Bitmap images are not supported"); + } + + /** + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, + * java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) { + handleUnsupportedFeature("Bitmap images are not supported"); + return false; + } + + /** + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + handleUnsupportedFeature("Bitmap images are not supported"); + return false; + /* + * First attempt disabled. + * Reasons: Lack of transparency control, positioning and rotation issues + final int width = img.getWidth(observer); + final int height = img.getHeight(observer); + if (width == -1 || height == -1) { + return false; + } + + Dimension size = new Dimension(width, height); + BufferedImage buf = buildBufferedImage(size); + + java.awt.Graphics2D g = buf.createGraphics(); + try { + g.setComposite(AlphaComposite.SrcOver); + g.setBackground(new Color(255, 255, 255)); + g.setPaint(new Color(255, 255, 255)); + g.fillRect(0, 0, width, height); + g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight())); + + if (!g.drawImage(img, 0, 0, observer)) { + return false; + } + } finally { + g.dispose(); + } + + try { + AffineTransform at = getTransform(); + gen.enterPCLMode(false); + //Shape imclip = getClip(); Clipping is not available in PCL + Point2D p1 = new Point2D.Double(x, y); + at.transform(p1, p1); + pclContext.getTransform().transform(p1, p1); + gen.setCursorPos(p1.getX(), p1.getY()); + gen.paintBitmap(buf, 72); + gen.enterHPGL2Mode(false); + } catch (IOException ioe) { + handleIOException(ioe); + } + + return true;*/ + } + + /** @see java.awt.Graphics#copyArea(int, int, int, int, int, int) */ + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + // TODO Auto-generated method stub + handleUnsupportedFeature("copyArea NYI"); + } + + /** @see java.awt.Graphics#setXORMode(java.awt.Color) */ + public void setXORMode(Color c1) { + // TODO Auto-generated method stub + handleUnsupportedFeature("setXORMode NYI"); + } + + /** + * Used to create proper font metrics + */ + private Graphics2D fmg; + + { + BufferedImage bi = new BufferedImage(1, 1, + BufferedImage.TYPE_INT_ARGB); + + fmg = bi.createGraphics(); + } + + /** + * Creates a buffered image. + * @param size dimensions of the image to be created + * @return the buffered image + */ + protected BufferedImage buildBufferedImage(Dimension size) { + return new BufferedImage(size.width, size.height, + BufferedImage.TYPE_BYTE_GRAY); + } + + /** @see java.awt.Graphics#getFontMetrics(java.awt.Font) */ + public java.awt.FontMetrics getFontMetrics(java.awt.Font f) { + return fmg.getFontMetrics(f); + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java b/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java new file mode 100644 index 000000000..d2e91f8e6 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java @@ -0,0 +1,119 @@ +/* + * 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.pcl; + +import java.awt.Dimension; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +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.fop.render.AbstractGraphics2DAdapter; +import org.apache.fop.render.Graphics2DImagePainter; +import org.apache.fop.render.RendererContext; +import org.apache.fop.util.UnitConv; +import org.apache.xmlgraphics.java2d.GraphicContext; + +/** + * Graphics2DAdapter implementation for PCL and HP GL/2. + */ +public class PCLGraphics2DAdapter extends AbstractGraphics2DAdapter { + + /** logging instance */ + private static Log log = LogFactory.getLog(PCLGraphics2DAdapter.class); + + /** + * Main constructor + */ + public PCLGraphics2DAdapter() { + } + + /** @see org.apache.fop.render.Graphics2DAdapter */ + public void paintImage(Graphics2DImagePainter painter, + RendererContext context, + int x, int y, int width, int height) throws IOException { + PCLRendererContext pclContext = PCLRendererContext.wrapRendererContext(context); + PCLRenderer pcl = (PCLRenderer)context.getRenderer(); + PCLGenerator gen = pcl.gen; + + // get the 'width' and 'height' attributes of the image/document + Dimension dim = painter.getImageSize(); + float imw = (float)dim.getWidth(); + float imh = (float)dim.getHeight(); + + boolean painted = false; + boolean paintAsBitmap = pclContext.paintAsBitmap(); + if (!paintAsBitmap) { + ByteArrayOutputStream baout = new ByteArrayOutputStream(); + PCLGenerator tempGen = new PCLGenerator(baout, gen.getMaximumBitmapResolution()); + try { + GraphicContext ctx = (GraphicContext)pcl.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(pclContext.isClippingDisabled()); + Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh); + painter.paint(graphics, area); + + //If we arrive here, the graphic is natively paintable, so write the graphic + pcl.saveGraphicsState(); + pcl.setCursorPos(x, y); + gen.writeCommand("*c" + gen.formatDouble4(width / 100f) + "x" + + gen.formatDouble4(height / 100f) + "Y"); + gen.writeCommand("*c0T"); + gen.enterHPGL2Mode(false); + gen.writeText("\nIN;"); + gen.writeText("SP1;"); + //One Plotter unit is 0.025mm! + double scale = imw / UnitConv.mm2pt(imw * 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); + pcl.restoreGraphicsState(); + 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 + int resolution = (int)Math.round(context.getUserAgent().getTargetResolution()); + BufferedImage bi = paintToBufferedImage(painter, pclContext, resolution, true, false); + + pcl.setCursorPos(x, y); + gen.paintBitmap(bi, new Dimension(width, height), pclContext.isSourceTransparency()); + } + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java b/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java new file mode 100644 index 000000000..43978e954 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java @@ -0,0 +1,192 @@ +/* + * 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.pcl; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.util.Iterator; +import java.util.List; + +import org.apache.fop.util.UnitConv; + +/** + * This class represents a page format with PCL-specific properties. + */ +public class PCLPageDefinition { + + private static List pageDefinitions; + private static PCLPageDefinition defaultPageDefinition; + + private String name; + private int selector; + private Dimension physicalPageSize; + private Rectangle logicalPageRect; + private boolean landscape; + + static { + createPageDefinitions(); + } + + /** + * Main constructor + * @param name the name of the page definition + * @param selector the selector used by the &l#A command (page size) + * @param physicalPageSize the physical page size + * @param logicalPageRect the rectangle defining the logical page + * @param landscape true if it is a landscape format + */ + public PCLPageDefinition(String name, int selector, Dimension physicalPageSize, + Rectangle logicalPageRect, boolean landscape) { + this.name = name; + this.selector = selector; + this.physicalPageSize = physicalPageSize; + this.logicalPageRect = logicalPageRect; + this.landscape = landscape; + } + + /** @return the name of the page definition */ + public String getName() { + return this.name; + } + + /** @return the selector used by the &l#A command (page size) */ + public int getSelector() { + return this.selector; + } + + /** @return true if it is a landscape format */ + public boolean isLandscapeFormat() { + return this.landscape; + } + + /** @return the physical page size */ + public Dimension getPhysicalPageSize() { + return this.physicalPageSize; + } + + /** @return the rectangle defining the logical page */ + public Rectangle getLogicalPageRect() { + return this.logicalPageRect; + } + + private boolean matches(long width, long height, int errorMargin) { + return (Math.abs(this.physicalPageSize.width - width) < errorMargin) + && (Math.abs(this.physicalPageSize.height - height) < errorMargin); + } + + /** @see java.lang.Object#toString() */ + public String toString() { + return getName(); + } + + /** + * Tries to determine a matching page definition. + * @param width the physical page width (in mpt) + * @param height the physical page height (in mpt) + * @param errorMargin the error margin for detecting the right page definition + * @return the page definition or null if no match was found + */ + public static PCLPageDefinition getPageDefinition(long width, long height, int errorMargin) { + Iterator iter = pageDefinitions.iterator(); + while (iter.hasNext()) { + PCLPageDefinition def = (PCLPageDefinition)iter.next(); + if (def.matches(width, height, errorMargin)) { + return def; + } + } + return null; + } + + /** @return the default page definition (letter) */ + public static PCLPageDefinition getDefaultPageDefinition() { + return defaultPageDefinition; + } + + /** + * Converts an offset values for logical pages to millipoints. The values are given as pixels + * in a 300dpi environment. + * @param offset the offset as given in the PCL 5 specification (under "Printable Area") + * @return the converted value in millipoints + */ + private static int convert300dpiDotsToMpt(int offset) { + return (int)Math.round(((double)offset) * 72000 / 300); + } + + private static Dimension createPhysicalPageSizeInch(float width, float height) { + return new Dimension( + (int)Math.round(UnitConv.in2mpt(width)), + (int)Math.round(UnitConv.in2mpt(height))); + } + + private static Dimension createPhysicalPageSizeMm(float width, float height) { + return new Dimension( + (int)Math.round(UnitConv.mm2mpt(width)), + (int)Math.round(UnitConv.mm2mpt(height))); + } + + private static Rectangle createLogicalPageRect(int x, int y, int width, int height) { + return new Rectangle(convert300dpiDotsToMpt(x), convert300dpiDotsToMpt(y), + convert300dpiDotsToMpt(width), convert300dpiDotsToMpt(height)); + } + + private static void createPageDefinitions() { + pageDefinitions = new java.util.ArrayList(); + pageDefinitions.add(new PCLPageDefinition("Letter", 2, + createPhysicalPageSizeInch(8.5f, 11), + createLogicalPageRect(75, 0, 2400, 3300), false)); + defaultPageDefinition = new PCLPageDefinition("Legal", 3, + createPhysicalPageSizeInch(8.5f, 14), + createLogicalPageRect(75, 0, 2400, 4200), false); + pageDefinitions.add(defaultPageDefinition); + pageDefinitions.add(new PCLPageDefinition("Executive", 1, + createPhysicalPageSizeInch(7.25f, 10.5f), + createLogicalPageRect(75, 0, 2025, 3150), false)); + pageDefinitions.add(new PCLPageDefinition("Ledger", 6, + createPhysicalPageSizeInch(11, 17), + createLogicalPageRect(75, 0, 3150, 5100), false)); + pageDefinitions.add(new PCLPageDefinition("A4", 26, + createPhysicalPageSizeMm(210, 297), + createLogicalPageRect(71, 0, 2338, 3507), false)); + pageDefinitions.add(new PCLPageDefinition("A3", 27, + createPhysicalPageSizeMm(297, 420), + createLogicalPageRect(71, 0, 3365, 4960), false)); + + //TODO Add envelope definitions + + pageDefinitions.add(new PCLPageDefinition("LetterL", 2, + createPhysicalPageSizeInch(11, 8.5f), + createLogicalPageRect(60, 0, 3180, 2550), true)); + pageDefinitions.add(new PCLPageDefinition("LegalL", 3, + createPhysicalPageSizeInch(14, 8.5f), + createLogicalPageRect(60, 0, 4080, 2550), true)); + pageDefinitions.add(new PCLPageDefinition("ExecutiveL", 1, + createPhysicalPageSizeInch(10.5f, 7.25f), + createLogicalPageRect(60, 0, 3030, 2175), true)); + pageDefinitions.add(new PCLPageDefinition("LedgerL", 6, + createPhysicalPageSizeInch(17, 11), + createLogicalPageRect(60, 0, 4980, 3300), true)); + pageDefinitions.add(new PCLPageDefinition("A4L", 26, + createPhysicalPageSizeMm(297, 210), + createLogicalPageRect(59, 0, 3389, 2480), true)); + pageDefinitions.add(new PCLPageDefinition("A3L", 27, + createPhysicalPageSizeMm(420, 297), + createLogicalPageRect(59, 0, 4842, 3507), true)); + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderer.java b/src/java/org/apache/fop/render/pcl/PCLRenderer.java new file mode 100644 index 000000000..05952d933 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLRenderer.java @@ -0,0 +1,1448 @@ +/* + * Copyright 1999-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.pcl; + +//Java +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import org.w3c.dom.Document; + +import org.apache.xmlgraphics.java2d.GraphicContext; + +// FOP +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.BlockViewport; +import org.apache.fop.area.CTM; +import org.apache.fop.area.PageViewport; +import org.apache.fop.area.Trait; +import org.apache.fop.area.inline.AbstractTextArea; +import org.apache.fop.area.inline.ForeignObject; +import org.apache.fop.area.inline.Image; +import org.apache.fop.area.inline.InlineArea; +import org.apache.fop.area.inline.SpaceArea; +import org.apache.fop.area.inline.TextArea; +import org.apache.fop.area.inline.Viewport; +import org.apache.fop.area.inline.WordArea; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.image.EPSImage; +import org.apache.fop.image.FopImage; +import org.apache.fop.image.ImageFactory; +import org.apache.fop.image.XMLImage; +import org.apache.fop.render.Graphics2DAdapter; +import org.apache.fop.render.Graphics2DImagePainter; +import org.apache.fop.render.PrintRenderer; +import org.apache.fop.render.RendererContext; +import org.apache.fop.render.RendererContextConstants; +import org.apache.fop.render.java2d.FontMetricsMapper; +import org.apache.fop.render.java2d.FontSetup; +import org.apache.fop.render.java2d.Java2DRenderer; +import org.apache.fop.render.pcl.extensions.PCLElementMapping; +import org.apache.fop.traits.BorderProps; +import org.apache.fop.util.QName; +import org.apache.fop.util.UnitConv; + +/** + * Renderer for the PCL 5 printer language. It also uses HP GL/2 for certain graphic elements. + */ +public class PCLRenderer extends PrintRenderer { + + /** logging instance */ + private static Log log = LogFactory.getLog(PCLRenderer.class); + + /** The MIME type for PCL */ + public static final String MIME_TYPE = MimeConstants.MIME_PCL_ALT; + + private static final QName CONV_MODE + = new QName(ExtensionElementMapping.URI, null, "conversion-mode"); + private static final QName SRC_TRANSPARENCY + = new QName(ExtensionElementMapping.URI, null, "source-transparency"); + + /** The OutputStream to write the PCL stream to */ + protected OutputStream out; + + /** The PCL generator */ + protected PCLGenerator gen; + private boolean ioTrouble = false; + + private Stack graphicContextStack = new Stack(); + private GraphicContext graphicContext = new GraphicContext(); + + private PCLPageDefinition currentPageDefinition; + private int currentPrintDirection = 0; + private GeneralPath currentPath = null; + 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). + */ + 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; + + /** + * Create the PCL renderer + */ + public PCLRenderer() { + } + + /** + * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) + */ + public void configure(Configuration cfg) throws ConfigurationException { + super.configure(cfg); + String rendering = cfg.getChild("rendering").getValue(null); + if ("quality".equalsIgnoreCase(rendering)) { + this.qualityBeforeSpeed = true; + } else if ("speed".equalsIgnoreCase(rendering)) { + this.qualityBeforeSpeed = false; + } else if (rendering != null) { + throw new ConfigurationException( + "Valid values for 'rendering' are 'quality' and 'speed'. Value found: " + + rendering); + } + String textRendering = cfg.getChild("text-rendering").getValue(null); + if ("bitmap".equalsIgnoreCase(textRendering)) { + this.allTextAsBitmaps = true; + } else if ("auto".equalsIgnoreCase(textRendering)) { + this.allTextAsBitmaps = false; + } else if (textRendering != null) { + throw new ConfigurationException( + "Valid values for 'text-rendering' are 'auto' and 'bitmap'. Value found: " + + textRendering); + } + } + + /** + * @see org.apache.fop.render.Renderer#setupFontInfo(org.apache.fop.fonts.FontInfo) + */ + public void setupFontInfo(FontInfo inFontInfo) { + //Don't call super.setupFontInfo() here! + //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 g = fontImage.createGraphics(); + //The next line is important to get accurate font metrics! + g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + FontSetup.setup(fontInfo, g); + } + + /** + * Central exception handler for I/O exceptions. + * @param ioe IOException to handle + */ + protected void handleIOTrouble(IOException ioe) { + if (!ioTrouble) { + log.error("Error while writing to target file", ioe); + ioTrouble = true; + } + } + + /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */ + public Graphics2DAdapter getGraphics2DAdapter() { + return new PCLGraphics2DAdapter(); + } + + /** @return the GraphicContext used to track coordinate system transformations */ + public GraphicContext getGraphicContext() { + return this.graphicContext; + } + + /** @return the target resolution */ + protected int getResolution() { + int resolution = (int)Math.round(userAgent.getTargetResolution()); + if (resolution <= 300) { + return 300; + } else { + return 600; + } + } + + /** + * 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; + } + + /** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */ + public void startRenderer(OutputStream outputStream) throws IOException { + log.debug("Rendering areas to PCL..."); + this.out = outputStream; + this.gen = new PCLGenerator(out, getResolution()); + + gen.universalEndOfLanguage(); + gen.writeText("@PJL COMMENT Produced by " + userAgent.getProducer() + "\n"); + if (userAgent.getTitle() != null) { + gen.writeText("@PJL JOB NAME = \"" + userAgent.getTitle() + "\"\n"); + } + gen.writeText("@PJL SET RESOLUTION = " + getResolution() + "\n"); + gen.writeText("@PJL ENTER LANGUAGE = PCL\n"); + gen.resetPrinter(); + gen.setUnitOfMeasure(getResolution()); + gen.setRasterGraphicsResolution(getResolution()); + } + + /** @see org.apache.fop.render.Renderer#stopRenderer() */ + public void stopRenderer() throws IOException { + gen.separateJobs(); + gen.resetPrinter(); + gen.universalEndOfLanguage(); + } + + /** @see org.apache.fop.render.AbstractRenderer */ + public String getMimeType() { + return MIME_TYPE; + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderPage(org.apache.fop.area.PageViewport) + */ + public void renderPage(PageViewport page) throws IOException, FOPException { + saveGraphicsState(); + + //Paper source + String paperSource = page.getForeignAttributeValue( + new QName(PCLElementMapping.NAMESPACE, null, "paper-source")); + if (paperSource != null) { + gen.selectPaperSource(Integer.parseInt(paperSource)); + } + + //Page size + final long pagewidth = Math.round(page.getViewArea().getWidth()); + final long pageheight = Math.round(page.getViewArea().getHeight()); + selectPageFormat(pagewidth, pageheight); + + super.renderPage(page); + + //Eject page + gen.formFeed(); + restoreGraphicsState(); + } + + private void selectPageFormat(long pagewidth, long pageheight) throws IOException { + this.currentPageDefinition = PCLPageDefinition.getPageDefinition( + pagewidth, pageheight, 1000); + + if (this.currentPageDefinition == null) { + this.currentPageDefinition = PCLPageDefinition.getDefaultPageDefinition(); + log.warn("Paper type could not be determined. Falling back to: " + + this.currentPageDefinition.getName()); + } + log.debug("page size: " + currentPageDefinition.getPhysicalPageSize()); + log.debug("logical page: " + currentPageDefinition.getLogicalPageRect()); + if (this.currentPageDefinition.isLandscapeFormat()) { + gen.writeCommand("&l1O"); //Orientation + } else { + gen.writeCommand("&l0O"); //Orientation + } + gen.selectPageSize(this.currentPageDefinition.getSelector()); + + gen.clearHorizontalMargins(); + gen.setTopMargin(0); + } + + /** Saves the current graphics state on the stack. */ + protected void saveGraphicsState() { + graphicContextStack.push(graphicContext); + graphicContext = (GraphicContext)graphicContext.clone(); + } + + /** Restores the last graphics state from the stack. */ + protected void restoreGraphicsState() { + graphicContext = (GraphicContext)graphicContextStack.pop(); + } + + /** + * Clip an area. write a clipping operation given coordinates in the current + * transform. Coordinates are in points. + * + * @param x the x coordinate + * @param y the y coordinate + * @param width the width of the area + * @param height the height of the area + */ + protected void clipRect(float x, float y, float width, float height) { + //PCL cannot clip (only HP GL/2 can) + } + + private Point2D transformedPoint(float x, float y) { + return transformedPoint(Math.round(x), Math.round(y)); + } + + 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; + } + + private void changePrintDirection() { + AffineTransform at = graphicContext.getTransform(); + int newDir; + try { + if (at.getScaleX() == 0 && at.getScaleY() == 0 + && at.getShearX() == 1 && at.getShearY() == -1) { + newDir = 90; + } else if (at.getScaleX() == -1 && at.getScaleY() == -1 + && at.getShearX() == 0 && at.getShearY() == 0) { + newDir = 180; + } else if (at.getScaleX() == 0 && at.getScaleY() == 0 + && at.getShearX() == -1 && at.getShearY() == 1) { + newDir = 270; + } else { + newDir = 0; + } + if (newDir != this.currentPrintDirection) { + this.currentPrintDirection = newDir; + gen.changePrintDirection(this.currentPrintDirection); + } + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D) + */ + protected void startVParea(CTM ctm, Rectangle2D clippingRect) { + saveGraphicsState(); + AffineTransform at = new AffineTransform(ctm.toArray()); + graphicContext.transform(at); + changePrintDirection(); + if (log.isDebugEnabled()) { + log.debug("startVPArea: " + at + " --> " + graphicContext.getTransform()); + } + } + + /** + * @see org.apache.fop.render.AbstractRenderer#endVParea() + */ + protected void endVParea() { + restoreGraphicsState(); + changePrintDirection(); + if (log.isDebugEnabled()) { + log.debug("endVPArea() --> " + graphicContext.getTransform()); + } + } + + /** + * Handle block traits. + * The block could be any sort of block with any positioning + * so this should render the traits such as border and background + * in its position. + * + * @param block the block to render the traits + */ + protected void handleBlockTraits(Block block) { + int borderPaddingStart = block.getBorderAndPaddingWidthStart(); + int borderPaddingBefore = block.getBorderAndPaddingWidthBefore(); + + float startx = currentIPPosition / 1000f; + float starty = currentBPPosition / 1000f; + float width = block.getIPD() / 1000f; + float height = block.getBPD() / 1000f; + + startx += block.getStartIndent() / 1000f; + startx -= block.getBorderAndPaddingWidthStart() / 1000f; + + width += borderPaddingStart / 1000f; + width += block.getBorderAndPaddingWidthEnd() / 1000f; + height += borderPaddingBefore / 1000f; + height += block.getBorderAndPaddingWidthAfter() / 1000f; + + drawBackAndBorders(block, startx, starty, width, height); + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea) + */ + protected void renderText(final TextArea text) { + renderInlineAreaBackAndBorders(text); + + String fontname = getInternalFontNameForArea(text); + int fontsize = text.getTraitAsInteger(Trait.FONT_SIZE); + + //Determine position + int saveIP = currentIPPosition; + int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); + int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); + + try { + final Color col = (Color)text.getTrait(Trait.COLOR); + boolean pclFont = allTextAsBitmaps + ? false + : setFont(fontname, fontsize, text.getText()); + if (pclFont) { + //this.currentFill = col; + if (col != null) { + //useColor(ct); + gen.setTransparencyMode(true, false); + gen.selectCurrentPattern(gen.convertToPCLShade(col), 2); + } + + saveGraphicsState(); + graphicContext.translate(rx, bl); + setCursorPos(0, 0); + gen.setTransparencyMode(true, true); + + super.renderText(text); //Updates IPD and renders words and spaces + restoreGraphicsState(); + } else { + //Use Java2D to paint different fonts via bitmap + 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( + 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); + Map atts = new java.util.HashMap(); + atts.put(CONV_MODE, "bitmap"); + atts.put(SRC_TRANSPARENCY, "true"); + rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); + + 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); + } + + public Dimension getImageSize() { + return paintRect.getSize(); + } + + }; + g2a.paintImage(painter, rc, + paintRect.x, paintRect.y, paintRect.width, paintRect.height); + currentIPPosition = saveIP + text.getAllocIPD(); + } + + //renderTextDecoration(tf, fontsize, area, bl, rx); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * Sets the current cursor position. The coordinates are transformed to the absolute position + * on the logical PCL page and then passed on to the PCLGenerator. + * @param x the x coordinate (in millipoints) + * @param y the y coordinate (in millipoints) + */ + void setCursorPos(float x, float y) { + try { + Point2D transPoint = transformedPoint(x, y); + gen.setCursorPos(transPoint.getX(), transPoint.getY()); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() + */ + protected void clip() { + if (currentPath == null) { + throw new IllegalStateException("No current path available!"); + } + //TODO Find a good way to do clipping. PCL itself cannot clip. + currentPath = null; + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() + */ + protected void closePath() { + currentPath.closePath(); + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) + */ + protected void lineTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.lineTo(x, y); + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) + */ + protected void moveTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.moveTo(x, y); + } + + /** + * Fill a rectangular area. + * @param x the x coordinate (in pt) + * @param y the y coordinate (in pt) + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + protected void fillRect(float x, float y, float width, float height) { + try { + setCursorPos(x * 1000, y * 1000); + gen.fillRect((int)(width * 1000), (int)(height * 1000), + this.currentFillColor); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * Sets the new current fill color. + * @param color the color + */ + protected void updateFillColor(java.awt.Color color) { + this.currentFillColor = color; + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderWord(org.apache.fop.area.inline.WordArea) + */ + protected void renderWord(WordArea word) { + //Font font = getFontFromArea(word.getParentArea()); + + String s = word.getWord(); + + try { + gen.writeText(s); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + + super.renderWord(word); + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderSpace(org.apache.fop.area.inline.SpaceArea) + */ + protected void renderSpace(SpaceArea space) { + AbstractTextArea textArea = (AbstractTextArea)space.getParentArea(); + String s = space.getSpace(); + char sp = s.charAt(0); + Font font = getFontFromArea(textArea); + + int tws = (space.isAdjustable() + ? textArea.getTextWordSpaceAdjust() + + 2 * textArea.getTextLetterSpaceAdjust() + : 0); + + double dx = (font.getCharWidth(sp) + tws) / 100f; + try { + gen.writeCommand("&a+" + gen.formatDouble2(dx) + "H"); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + super.renderSpace(space); + } + + /** + * Render an inline viewport. + * This renders an inline viewport by clipping if necessary. + * @param viewport the viewport to handle + * @todo Copied from AbstractPathOrientedRenderer + */ + public void renderViewport(Viewport viewport) { + + float x = currentIPPosition / 1000f; + float y = (currentBPPosition + viewport.getOffset()) / 1000f; + float width = viewport.getIPD() / 1000f; + float height = viewport.getBPD() / 1000f; + // TODO: Calculate the border rect correctly. + float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f; + float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f; + float bpwidth = borderPaddingStart + + (viewport.getBorderAndPaddingWidthEnd() / 1000f); + float bpheight = borderPaddingBefore + + (viewport.getBorderAndPaddingWidthAfter() / 1000f); + + drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight); + + if (viewport.getClip()) { + saveGraphicsState(); + + clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height); + } + super.renderViewport(viewport); + + if (viewport.getClip()) { + restoreGraphicsState(); + } + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List) + */ + protected void renderBlockViewport(BlockViewport bv, List children) { + // clip and position viewport if necessary + + // save positions + int saveIP = currentIPPosition; + int saveBP = currentBPPosition; + //String saveFontName = currentFontName; + + CTM ctm = bv.getCTM(); + int borderPaddingStart = bv.getBorderAndPaddingWidthStart(); + int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore(); + float x, y; + x = (float)(bv.getXOffset() + containingIPPosition) / 1000f; + y = (float)(bv.getYOffset() + containingBPPosition) / 1000f; + //This is the content-rect + float width = (float)bv.getIPD() / 1000f; + float height = (float)bv.getBPD() / 1000f; + + + if (bv.getPositioning() == Block.ABSOLUTE + || bv.getPositioning() == Block.FIXED) { + + currentIPPosition = bv.getXOffset(); + currentBPPosition = bv.getYOffset(); + + //For FIXED, we need to break out of the current viewports to the + //one established by the page. We save the state stack for restoration + //after the block-container has been painted. See below. + List breakOutList = null; + if (bv.getPositioning() == Block.FIXED) { + breakOutList = breakOutOfStateStack(); + } + + CTM tempctm = new CTM(containingIPPosition, containingBPPosition); + ctm = tempctm.multiply(ctm); + + //Adjust for spaces (from margin or indirectly by start-indent etc. + x += bv.getSpaceStart() / 1000f; + currentIPPosition += bv.getSpaceStart(); + + y += bv.getSpaceBefore() / 1000f; + currentBPPosition += bv.getSpaceBefore(); + + float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f; + float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f; + + drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight); + + //Now adjust for border/padding + currentIPPosition += borderPaddingStart; + currentBPPosition += borderPaddingBefore; + + Rectangle2D clippingRect = null; + if (bv.getClip()) { + clippingRect = new Rectangle(currentIPPosition, currentBPPosition, + bv.getIPD(), bv.getBPD()); + } + + startVParea(ctm, clippingRect); + currentIPPosition = 0; + currentBPPosition = 0; + renderBlocks(bv, children); + endVParea(); + + if (breakOutList != null) { + restoreStateStackAfterBreakOut(breakOutList); + } + + currentIPPosition = saveIP; + currentBPPosition = saveBP; + } else { + + currentBPPosition += bv.getSpaceBefore(); + + //borders and background in the old coordinate system + handleBlockTraits(bv); + + //Advance to start of content area + currentIPPosition += bv.getStartIndent(); + + CTM tempctm = new CTM(containingIPPosition, currentBPPosition); + ctm = tempctm.multiply(ctm); + + //Now adjust for border/padding + currentBPPosition += borderPaddingBefore; + + Rectangle2D clippingRect = null; + if (bv.getClip()) { + clippingRect = new Rectangle(currentIPPosition, currentBPPosition, + bv.getIPD(), bv.getBPD()); + } + + startVParea(ctm, clippingRect); + currentIPPosition = 0; + currentBPPosition = 0; + renderBlocks(bv, children); + endVParea(); + + currentIPPosition = saveIP; + currentBPPosition = saveBP; + + currentBPPosition += (int)(bv.getAllocBPD()); + } + //currentFontName = saveFontName; + } + + private List breakOutOfStateStack() { + log.debug("Block.FIXED --> break out"); + List breakOutList = new java.util.ArrayList(); + while (!this.graphicContextStack.empty()) { + breakOutList.add(0, this.graphicContext); + restoreGraphicsState(); + } + return breakOutList; + } + + private void restoreStateStackAfterBreakOut(List breakOutList) { + log.debug("Block.FIXED --> restoring context after break-out"); + for (int i = 0, c = breakOutList.size(); i < c; i++) { + saveGraphicsState(); + this.graphicContext = (GraphicContext)breakOutList.get(i); + } + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D) + */ + public void renderImage(Image image, Rectangle2D pos) { + drawImage(image.getURL(), pos, image.getForeignAttributes()); + } + + /** + * Draw an image at the indicated location. + * @param url the URI/URL of the image + * @param pos the position of the image + * @param foreignAttributes an optional Map with foreign attributes, may be null + */ + protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { + url = ImageFactory.getURL(url); + ImageFactory fact = userAgent.getFactory().getImageFactory(); + FopImage fopimage = fact.getImage(url, userAgent); + if (fopimage == null) { + return; + } + if (!fopimage.load(FopImage.DIMENSIONS)) { + return; + } + String mime = fopimage.getMimeType(); + if ("text/xml".equals(mime)) { + if (!fopimage.load(FopImage.ORIGINAL_DATA)) { + return; + } + Document doc = ((XMLImage) fopimage).getDocument(); + String ns = ((XMLImage) fopimage).getNameSpace(); + + renderDocument(doc, ns, pos, foreignAttributes); + } else if ("image/svg+xml".equals(mime)) { + if (!fopimage.load(FopImage.ORIGINAL_DATA)) { + return; + } + Document doc = ((XMLImage) fopimage).getDocument(); + String ns = ((XMLImage) fopimage).getNameSpace(); + + renderDocument(doc, ns, pos, foreignAttributes); + } else if (fopimage instanceof EPSImage) { + log.warn("EPS images are not supported by this renderer"); + } else { + if (!fopimage.load(FopImage.BITMAP)) { + log.error("Bitmap image could not be processed: " + fopimage); + return; + } + byte[] imgmap = fopimage.getBitmaps(); + + ColorModel cm = new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), + new int[] {8, 8, 8}, + false, false, + ColorModel.OPAQUE, DataBuffer.TYPE_BYTE); + int imgw = fopimage.getWidth(); + int imgh = fopimage.getHeight(); + SampleModel sampleModel = new PixelInterleavedSampleModel( + DataBuffer.TYPE_BYTE, imgw, imgh, 3, imgw * 3, new int[] {0, 1, 2}); + DataBuffer dbuf = new DataBufferByte(imgmap, imgw * imgh * 3); + + WritableRaster raster = Raster.createWritableRaster(sampleModel, + dbuf, null); + + // Combine the color model and raster into a buffered image + RenderedImage img = new BufferedImage(cm, raster, false, null); + + try { + setCursorPos(this.currentIPPosition + (int)pos.getX(), + this.currentBPPosition + (int)pos.getY()); + gen.paintBitmap(img, + new Dimension((int)pos.getWidth(), (int)pos.getHeight()), + false); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + } + + /** + * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D) + */ + public void renderForeignObject(ForeignObject fo, Rectangle2D pos) { + Document doc = fo.getDocument(); + String ns = fo.getNameSpace(); + renderDocument(doc, ns, pos, fo.getForeignAttributes()); + } + + /** + * Common method to render the background and borders for any inline area. + * The all borders and padding are drawn outside the specified area. + * @param area the inline area for which the background, border and padding is to be + * rendered + * @todo Copied from AbstractPathOrientedRenderer + */ + protected void renderInlineAreaBackAndBorders(InlineArea area) { + float x = currentIPPosition / 1000f; + float y = (currentBPPosition + area.getOffset()) / 1000f; + float width = area.getIPD() / 1000f; + float height = area.getBPD() / 1000f; + float borderPaddingStart = area.getBorderAndPaddingWidthStart() / 1000f; + float borderPaddingBefore = area.getBorderAndPaddingWidthBefore() / 1000f; + float bpwidth = borderPaddingStart + + (area.getBorderAndPaddingWidthEnd() / 1000f); + float bpheight = borderPaddingBefore + + (area.getBorderAndPaddingWidthAfter() / 1000f); + + if (height != 0.0f || bpheight != 0.0f && bpwidth != 0.0f) { + drawBackAndBorders(area, x, y - borderPaddingBefore + , width + bpwidth + , height + bpheight); + } + } + + /** + * Draw the background and borders. This draws the background and border + * traits for an area given the position. + * + * @param area the area whose traits are used + * @param startx the start x position + * @param starty the start y position + * @param width the width of the area + * @param height the height of the area + */ + protected void drawBackAndBorders(Area area, float startx, float starty, + float width, float height) { + BorderProps bpsBefore = (BorderProps) area.getTrait(Trait.BORDER_BEFORE); + BorderProps bpsAfter = (BorderProps) area.getTrait(Trait.BORDER_AFTER); + BorderProps bpsStart = (BorderProps) area.getTrait(Trait.BORDER_START); + BorderProps bpsEnd = (BorderProps) area.getTrait(Trait.BORDER_END); + + // draw background + Trait.Background back; + back = (Trait.Background) area.getTrait(Trait.BACKGROUND); + if (back != null) { + + // Calculate padding rectangle + float sx = startx; + float sy = starty; + float paddRectWidth = width; + float paddRectHeight = height; + + if (bpsStart != null) { + sx += bpsStart.width / 1000f; + paddRectWidth -= bpsStart.width / 1000f; + } + if (bpsBefore != null) { + sy += bpsBefore.width / 1000f; + paddRectHeight -= bpsBefore.width / 1000f; + } + if (bpsEnd != null) { + paddRectWidth -= bpsEnd.width / 1000f; + } + if (bpsAfter != null) { + paddRectHeight -= bpsAfter.width / 1000f; + } + + if (back.getColor() != null) { + updateFillColor(back.getColor()); + fillRect(sx, sy, paddRectWidth, paddRectHeight); + } + + // background image + if (back.getFopImage() != null) { + FopImage fopimage = back.getFopImage(); + if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) { + saveGraphicsState(); + clipRect(sx, sy, paddRectWidth, paddRectHeight); + int horzCount = (int) ((paddRectWidth * 1000 / fopimage + .getIntrinsicWidth()) + 1.0f); + int vertCount = (int) ((paddRectHeight * 1000 / fopimage + .getIntrinsicHeight()) + 1.0f); + if (back.getRepeat() == EN_NOREPEAT) { + horzCount = 1; + vertCount = 1; + } else if (back.getRepeat() == EN_REPEATX) { + vertCount = 1; + } else if (back.getRepeat() == EN_REPEATY) { + horzCount = 1; + } + // change from points to millipoints + sx *= 1000; + sy *= 1000; + if (horzCount == 1) { + sx += back.getHoriz(); + } + if (vertCount == 1) { + sy += back.getVertical(); + } + for (int x = 0; x < horzCount; x++) { + for (int y = 0; y < vertCount; y++) { + // place once + Rectangle2D pos; + // Image positions are relative to the currentIP/BP + pos = new Rectangle2D.Float( + sx - currentIPPosition + + (x * fopimage.getIntrinsicWidth()), + sy - currentBPPosition + + (y * fopimage.getIntrinsicHeight()), + fopimage.getIntrinsicWidth(), + fopimage.getIntrinsicHeight()); + drawImage(back.getURL(), pos, null); + } + } + restoreGraphicsState(); + } else { + log.warn( + "Can't find background image: " + back.getURL()); + } + } + } + + Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height); + drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } + + /** + * Draws borders. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + if (bpsBefore == null && bpsAfter == null && bpsStart == null && bpsEnd == null) { + return; //no borders to paint + } + if (qualityBeforeSpeed) { + drawQualityBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } else { + drawFastBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } + } + + /** + * Draws borders. Borders are drawn as shaded rectangles with no clipping. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawFastBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + float startx = borderRect.x; + float starty = borderRect.y; + float width = borderRect.width; + float height = borderRect.height; + if (bpsBefore != null) { + float borderWidth = bpsBefore.width / 1000f; + updateFillColor(bpsBefore.color); + fillRect(startx, starty, width, borderWidth); + } + if (bpsAfter != null) { + float borderWidth = bpsAfter.width / 1000f; + updateFillColor(bpsAfter.color); + fillRect(startx, (starty + height - borderWidth), + width, borderWidth); + } + if (bpsStart != null) { + float borderWidth = bpsStart.width / 1000f; + updateFillColor(bpsStart.color); + fillRect(startx, starty, borderWidth, height); + } + if (bpsEnd != null) { + float borderWidth = bpsEnd.width / 1000f; + updateFillColor(bpsEnd.color); + fillRect((startx + width - borderWidth), starty, borderWidth, height); + } + } + + /** + * Draws borders. Borders are drawn in-memory and painted as a bitmap. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawQualityBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + Graphics2DAdapter g2a = getGraphics2DAdapter(); + final Rectangle.Float effBorderRect = new Rectangle2D.Float( + borderRect.x - (currentIPPosition / 1000f), + borderRect.y - (currentBPPosition / 1000f), + borderRect.width, + borderRect.height); + final Rectangle paintRect = new Rectangle( + (int)Math.round(borderRect.x * 1000f), + (int)Math.round(borderRect.y * 1000f), + (int)Math.floor(borderRect.width * 1000f) + 1, + (int)Math.floor(borderRect.height * 1000f) + 1); + //Add one pixel wide safety margin around the paint area + int pixelWidth = (int)Math.round(UnitConv.in2mpt(1) / userAgent.getTargetResolution()); + final int xoffset = (int)Math.round(-effBorderRect.x * 1000f) + pixelWidth; + final int yoffset = pixelWidth; + paintRect.x += xoffset; + paintRect.y += yoffset; + paintRect.width += 2 * pixelWidth; + paintRect.height += 2 * pixelWidth; + + RendererContext rc = createRendererContext(paintRect.x, paintRect.y, + paintRect.width, paintRect.height, null); + Map atts = new java.util.HashMap(); + atts.put(CONV_MODE, "bitmap"); + atts.put(SRC_TRANSPARENCY, "true"); + rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); + + Graphics2DImagePainter painter = new Graphics2DImagePainter() { + + public void paint(Graphics2D g2d, Rectangle2D area) { + g2d.translate(xoffset, yoffset); + g2d.scale(1000, 1000); + float startx = effBorderRect.x; + float starty = effBorderRect.y; + float width = effBorderRect.width; + float height = effBorderRect.height; + boolean[] b = new boolean[] { + (bpsBefore != null), (bpsEnd != null), + (bpsAfter != null), (bpsStart != null)}; + if (!b[0] && !b[1] && !b[2] && !b[3]) { + return; + } + float[] bw = new float[] { + (b[0] ? bpsBefore.width / 1000f : 0.0f), + (b[1] ? bpsEnd.width / 1000f : 0.0f), + (b[2] ? bpsAfter.width / 1000f : 0.0f), + (b[3] ? bpsStart.width / 1000f : 0.0f)}; + float[] clipw = new float[] { + BorderProps.getClippedWidth(bpsBefore) / 1000f, + BorderProps.getClippedWidth(bpsEnd) / 1000f, + BorderProps.getClippedWidth(bpsAfter) / 1000f, + BorderProps.getClippedWidth(bpsStart) / 1000f}; + starty += clipw[0]; + height -= clipw[0]; + height -= clipw[2]; + startx += clipw[3]; + width -= clipw[3]; + width -= clipw[1]; + + boolean[] slant = new boolean[] { + (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])}; + if (bpsBefore != null) { + //endTextObject(); + + float sx1 = startx; + float sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1); + float ex1 = startx + width; + float ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1); + float outery = starty - clipw[0]; + float clipy = outery + clipw[0]; + float innery = outery + bw[0]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(sx1, clipy); + float sx1a = sx1; + float ex1a = ex1; + if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[3]; + } + if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[1]; + } + lineTo(sx1a, outery); + lineTo(ex1a, outery); + } + lineTo(ex1, clipy); + lineTo(ex2, innery); + lineTo(sx2, innery); + closePath(); + //clip(); + g.clip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + sx1a, outery, ex1a - sx1a, innery - outery); + Java2DRenderer.drawBorderLine(lineRect, true, true, + bpsBefore.style, bpsBefore.color, g); + //restoreGraphicsState(); + } + if (bpsEnd != null) { + //endTextObject(); + + float sy1 = starty; + float sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1); + float ey1 = starty + height; + float ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1); + float outerx = startx + width + clipw[1]; + float clipx = outerx - clipw[1]; + float innerx = outerx - bw[1]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(clipx, sy1); + float sy1a = sy1; + float ey1a = ey1; + if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[0]; + } + if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[2]; + } + lineTo(outerx, sy1a); + lineTo(outerx, ey1a); + } + lineTo(clipx, ey1); + lineTo(innerx, ey2); + lineTo(innerx, sy2); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + innerx, sy1a, outerx - innerx, ey1a - sy1a); + Java2DRenderer.drawBorderLine(lineRect, false, false, + bpsEnd.style, bpsEnd.color, g); + //restoreGraphicsState(); + } + if (bpsAfter != null) { + //endTextObject(); + + float sx1 = startx; + float sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1); + float ex1 = startx + width; + float ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1); + float outery = starty + height + clipw[2]; + float clipy = outery - clipw[2]; + float innery = outery - bw[2]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(ex1, clipy); + float sx1a = sx1; + float ex1a = ex1; + if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[3]; + } + if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[1]; + } + lineTo(ex1a, outery); + lineTo(sx1a, outery); + } + lineTo(sx1, clipy); + lineTo(sx2, innery); + lineTo(ex2, innery); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + sx1a, innery, ex1a - sx1a, outery - innery); + Java2DRenderer.drawBorderLine(lineRect, true, false, + bpsAfter.style, bpsAfter.color, g); + //restoreGraphicsState(); + } + if (bpsStart != null) { + //endTextObject(); + + float sy1 = starty; + float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1); + float ey1 = sy1 + height; + float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1); + float outerx = startx - clipw[3]; + float clipx = outerx + clipw[3]; + float innerx = outerx + bw[3]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(clipx, ey1); + float sy1a = sy1; + float ey1a = ey1; + if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[0]; + } + if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[2]; + } + lineTo(outerx, ey1a); + lineTo(outerx, sy1a); + } + lineTo(clipx, sy1); + lineTo(innerx, sy2); + lineTo(innerx, ey2); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + outerx, sy1a, innerx - outerx, ey1a - sy1a); + Java2DRenderer.drawBorderLine(lineRect, false, false, + bpsStart.style, bpsStart.color, g); + //restoreGraphicsState(); + } + } + + public Dimension getImageSize() { + return paintRect.getSize(); + } + + }; + try { + g2a.paintImage(painter, rc, + paintRect.x - xoffset, paintRect.y, paintRect.width, paintRect.height); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererContext.java b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java new file mode 100644 index 000000000..ae5646db8 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java @@ -0,0 +1,71 @@ +/* + * 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.pcl; + +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.render.RendererContext; +import org.apache.fop.util.QName; + +/** + * Wrapper on the RendererContext to access the information structure for drawing + * the XML document. + */ +public class PCLRendererContext extends RendererContext.RendererContextWrapper { + + /** + * Wrap the render context to allow easier access to its values. + * + * @param context the renderer context + * @return the PCL-specific renderer context wrapper + */ + public static PCLRendererContext wrapRendererContext(RendererContext context) { + PCLRendererContext pcli = new PCLRendererContext(context); + return pcli; + } + + /** + * Main constructor + * @param context the RendererContent instance + */ + public PCLRendererContext(RendererContext context) { + super(context); + } + + /** @return true if the SVG image should be rendered as a bitmap */ + public boolean paintAsBitmap() { + QName qName = new QName(ExtensionElementMapping.URI, null, "conversion-mode"); + return getForeignAttributes() != null + && "bitmap".equalsIgnoreCase((String)getForeignAttributes().get(qName)); + } + + /** @return true if clipping is disabled inside the PCLGraphics2D. */ + public boolean isClippingDisabled() { + QName qName = new QName(ExtensionElementMapping.URI, null, "disable-clipping"); + return getForeignAttributes() != null + && "true".equalsIgnoreCase((String)getForeignAttributes().get(qName)); + } + + public boolean isSourceTransparency() { + QName qName = new QName(ExtensionElementMapping.URI, null, "source-transparency"); + return getForeignAttributes() != null + && "true".equalsIgnoreCase((String)getForeignAttributes().get(qName)); + } + + +} \ No newline at end of file diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererMaker.java b/src/java/org/apache/fop/render/pcl/PCLRendererMaker.java new file mode 100644 index 000000000..2e39d355a --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLRendererMaker.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005 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.pcl; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.render.AbstractRendererMaker; +import org.apache.fop.render.Renderer; + +/** + * RendererMaker for the PCL Renderer. + */ +public class PCLRendererMaker extends AbstractRendererMaker { + + private static final String[] MIMES = new String[] { + MimeConstants.MIME_PCL, + MimeConstants.MIME_PCL_ALT}; + + + /**@see org.apache.fop.render.AbstractRendererMaker */ + public Renderer makeRenderer(FOUserAgent ua) { + return new PCLRenderer(); + } + + /** @see org.apache.fop.render.AbstractRendererMaker#needsOutputStream() */ + public boolean needsOutputStream() { + return true; + } + + /** @see org.apache.fop.render.AbstractRendererMaker#getSupportedMimeTypes() */ + public String[] getSupportedMimeTypes() { + return MIMES; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java b/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java new file mode 100644 index 000000000..7c1a45a0d --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java @@ -0,0 +1,39 @@ +/* + * 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.pcl; + +// FOP +import org.apache.fop.render.AbstractGenericSVGHandler; +import org.apache.fop.render.Renderer; + +/** + * PCL XML handler for SVG. Uses Apache Batik for SVG processing. + * This handler handles XML for foreign objects when rendering to HP GL/2. + * It renders SVG to HP GL/2 using the PCLGraphics2D. + * @see PCLGraphics2DAdapter + */ +public class PCLSVGHandler extends AbstractGenericSVGHandler { + + /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */ + public boolean supportsRenderer(Renderer renderer) { + return (renderer instanceof PCLRenderer); + } + +} + diff --git a/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java b/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java new file mode 100644 index 000000000..b25ab9321 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java @@ -0,0 +1,51 @@ +/* + * 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: AFPElementMapping.java 397875 2006-04-28 11:58:33Z manuel $ */ + +package org.apache.fop.render.pcl.extensions; + +import java.util.HashMap; + +import org.apache.fop.fo.ElementMapping; + +/** + * PCL-specific extensions for Apache FOP. + */ +public class PCLElementMapping extends ElementMapping { + + /** The namespace used for PCL extensions */ + public static final String NAMESPACE = "http://xmlgraphics.apache.org/fop/extensions/pcl"; + + /** The usual namespace prefix used for PCL extensions */ + public static final String NAMESPACE_PREFIX = "pcl"; + + /** Main constructor */ + public PCLElementMapping() { + this.namespaceURI = NAMESPACE; + } + + /** @see org.apache.fop.fo.ElementMapping#initialize() */ + protected void initialize() { + + if (foObjs == null) { + foObjs = new HashMap(); + //No extension elements, yet, only attributes + } + + } + +} diff --git a/src/java/org/apache/fop/render/pcl/package.html b/src/java/org/apache/fop/render/pcl/package.html new file mode 100644 index 000000000..ba47bd289 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/package.html @@ -0,0 +1,22 @@ + + + +org.apache.fop.render.pcl Package + +

PCL Renderer (Supports PCL5 and HP GL/2)

+ + \ No newline at end of file diff --git a/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java deleted file mode 100644 index 43580446f..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.pcl; - -import java.awt.RenderingHints; -import java.awt.color.ColorSpace; -import java.awt.image.BufferedImage; -import java.awt.image.ColorConvertOp; -import java.awt.image.RenderedImage; - -/** - * Default implementation of the MonochromeBitmapConverter which uses the Java Class Library - * to convert grayscale bitmaps to monochrome bitmaps. - */ -public class DefaultMonochromeBitmapConverter implements - MonochromeBitmapConverter { - - /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */ - public void setHint(String name, String value) { - //ignore, not supported - } - - /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */ - public RenderedImage convertToMonochrome(BufferedImage img) { - BufferedImage buf = new BufferedImage(img.getWidth(), img.getHeight(), - BufferedImage.TYPE_BYTE_BINARY); - RenderingHints hints = new RenderingHints(null); - //This hint doesn't seem to make a difference :-( - hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); - ColorConvertOp op = new ColorConvertOp( - ColorSpace.getInstance(ColorSpace.CS_GRAY), hints); - op.filter(img, buf); - return buf; - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java deleted file mode 100644 index 4818b2b1d..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.pcl; - -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; -import java.awt.image.renderable.ParameterBlock; - -import javax.media.jai.ColorCube; -import javax.media.jai.ImageLayout; -import javax.media.jai.JAI; -import javax.media.jai.KernelJAI; -import javax.media.jai.LookupTableJAI; -import javax.media.jai.PlanarImage; - -/** - * Implementation of the MonochromeBitmapConverter which uses Java Advanced Imaging (JAI) - * to convert grayscale bitmaps to monochrome bitmaps. JAI provides better dithering options - * including error diffusion dithering. - *

- * If you call setHint("quality", "true") on the instance you can enabled error diffusion - * dithering which produces a nicer result but is also a lot slower. - */ -public class JAIMonochromeBitmapConverter implements - MonochromeBitmapConverter { - - private boolean isErrorDiffusion = false; - - /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */ - public void setHint(String name, String value) { - if ("quality".equalsIgnoreCase(name)) { - isErrorDiffusion = "true".equalsIgnoreCase(value); - } - } - - /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */ - public RenderedImage convertToMonochrome(BufferedImage img) { - if (img.getColorModel().getColorSpace().getNumComponents() != 1) { - throw new IllegalArgumentException("Source image must be a grayscale image!"); - } - - // Load the ParameterBlock for the dithering operation - // and set the operation name. - ParameterBlock pb = new ParameterBlock(); - pb.addSource(img); - String opName = null; - if (isErrorDiffusion) { - opName = "errordiffusion"; - LookupTableJAI lut = new LookupTableJAI(new byte[] {(byte)0x00, (byte)0xff}); - pb.add(lut); - pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG); - } else { - opName = "ordereddither"; - //Create the color cube. - ColorCube colorMap = ColorCube.createColorCube(DataBuffer.TYPE_BYTE, - 0, new int[] {2}); - pb.add(colorMap); - pb.add(KernelJAI.DITHER_MASK_441); - } - - //Create an image layout for a monochrome b/w image - ImageLayout layout = new ImageLayout(); - byte[] map = new byte[] {(byte)0x00, (byte)0xff}; - ColorModel cm = new IndexColorModel(1, 2, map, map, map); - layout.setColorModel(cm); - - // Create a hint containing the layout. - RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); - - // Dither the image. - PlanarImage dst = JAI.create(opName, pb, hints); - - //Convert it to a BufferedImage - return dst.getAsBufferedImage(); - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java deleted file mode 100644 index f2db9d798..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.pcl; - -import java.awt.image.BufferedImage; -import java.awt.image.RenderedImage; - -/** - * Interface for converters that convert grayscale images to monochrome (1-bit) bitmap images. - */ -public interface MonochromeBitmapConverter { - - /** - * Sets a hint to the implementation - * @param name the name of the hint - * @param value the value - */ - void setHint(String name, String value); - - /** - * Converts a grayscale bitmap image to a monochrome (1-bit) b/w bitmap image. - * @param img the grayscale image - * @return the converted monochrome image - */ - RenderedImage convertToMonochrome(BufferedImage img); - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java b/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java deleted file mode 100644 index 8612a51e6..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java +++ /dev/null @@ -1,751 +0,0 @@ -/* - * 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.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.IndexColorModel; -import java.awt.image.LookupOp; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.io.OutputStream; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.util.Locale; - -import org.apache.fop.util.UnitConv; -import org.apache.xmlgraphics.image.GraphicsUtil; - -/** - * This class provides methods for generating PCL print files. - */ -public class PCLGenerator { - - /** The ESC (escape) character */ - public static final char ESC = '\033'; - - /** A list of all supported resolutions in PCL (values in dpi) */ - public static final int[] PCL_RESOLUTIONS = new int[] {75, 100, 150, 200, 300, 600}; - - private final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); - private final DecimalFormat df2 = new DecimalFormat("0.##", symbols); - private final DecimalFormat df4 = new DecimalFormat("0.####", symbols); - - private OutputStream out; - - private boolean currentSourceTransparency = true; - private boolean currentPatternTransparency = true; - - private int maxBitmapResolution = PCL_RESOLUTIONS[PCL_RESOLUTIONS.length - 1]; - - /** - * Main constructor. - * @param out the OutputStream to write the PCL stream to - */ - public PCLGenerator(OutputStream out) { - this.out = out; - } - - /** - * Main constructor. - * @param out the OutputStream to write the PCL stream to - * @param maxResolution the maximum resolution to encode bitmap images at - */ - public PCLGenerator(OutputStream out, int maxResolution) { - this(out); - boolean found = false; - for (int i = 0; i < PCL_RESOLUTIONS.length; i++) { - if (PCL_RESOLUTIONS[i] == maxResolution) { - found = true; - break; - } - } - if (!found) { - throw new IllegalArgumentException("Illegal value for maximum resolution!"); - } - this.maxBitmapResolution = maxResolution; - } - - /** @return the OutputStream that this generator writes to */ - public OutputStream getOutputStream() { - return this.out; - } - - /** @return the maximum resolution to encode bitmap images at */ - public int getMaximumBitmapResolution() { - return this.maxBitmapResolution; - } - - /** - * Writes a PCL escape command to the output stream. - * @param cmd the command (without the ESCAPE character) - * @throws IOException In case of an I/O error - */ - public void writeCommand(String cmd) throws IOException { - out.write(27); //ESC - out.write(cmd.getBytes("US-ASCII")); - } - - /** - * Writes raw text (in ISO-8859-1 encoding) to the output stream. - * @param s the text - * @throws IOException In case of an I/O error - */ - public void writeText(String s) throws IOException { - out.write(s.getBytes("ISO-8859-1")); - } - - /** - * Formats a double value with two decimal positions for PCL output. - * - * @param value value to format - * @return the formatted value - */ - public final String formatDouble2(double value) { - return df2.format(value); - } - - /** - * Formats a double value with four decimal positions for PCL output. - * - * @param value value to format - * @return the formatted value - */ - public final String formatDouble4(double value) { - return df4.format(value); - } - - /** - * Sends the universal end of language command (UEL). - * @throws IOException In case of an I/O error - */ - public void universalEndOfLanguage() throws IOException { - writeCommand("%-12345X"); - } - - /** - * Resets the printer and restores the user default environment. - * @throws IOException In case of an I/O error - */ - public void resetPrinter() throws IOException { - writeCommand("E"); - } - - /** - * Sends the job separation command. - * @throws IOException In case of an I/O error - */ - public void separateJobs() throws IOException { - writeCommand("&l1T"); - } - - /** - * Sends the form feed character. - * @throws IOException In case of an I/O error - */ - public void formFeed() throws IOException { - out.write(12); //=OC ("FF", Form feed) - } - - /** - * Sets the unit of measure. - * @param value the resolution value (units per inch) - * @throws IOException In case of an I/O error - */ - public void setUnitOfMeasure(int value) throws IOException { - writeCommand("&u" + value + "D"); - } - - /** - * Sets the raster graphics resolution - * @param value the resolution value (units per inch) - * @throws IOException In case of an I/O error - */ - public void setRasterGraphicsResolution(int value) throws IOException { - writeCommand("*t" + value + "R"); - } - - /** - * Selects the page size. - * @param selector the integer representing the page size - * @throws IOException In case of an I/O error - */ - public void selectPageSize(int selector) throws IOException { - writeCommand("&l" + selector + "A"); - } - - /** - * Selects the paper source. The parameter is usually printer-specific. Usually, "1" is the - * default tray, "2" is the manual paper feed, "3" is the manual envelope feed, "4" is the - * "lower" tray and "7" is "auto-select". Consult the technical reference for your printer - * for all available values. - * @param selector the integer representing the paper source/tray - * @throws IOException In case of an I/O error - */ - public void selectPaperSource(int selector) throws IOException { - writeCommand("&l" + selector + "H"); - } - - /** - * Clears the horizontal margins. - * @throws IOException In case of an I/O error - */ - public void clearHorizontalMargins() throws IOException { - writeCommand("9"); - } - - /** - * The Top Margin command designates the number of lines between - * the top of the logical page and the top of the text area. - * @param numberOfLines the number of lines (See PCL specification for details) - * @throws IOException In case of an I/O error - */ - public void setTopMargin(int numberOfLines) throws IOException { - writeCommand("&l" + numberOfLines + "E"); - } - - /** - * The Text Length command can be used to define the bottom border. See the PCL specification - * for details. - * @param numberOfLines the number of lines - * @throws IOException In case of an I/O error - */ - public void setTextLength(int numberOfLines) throws IOException { - writeCommand("&l" + numberOfLines + "F"); - } - - /** - * Sets the Vertical Motion Index (VMI). - * @param value the VMI value - * @throws IOException In case of an I/O error - */ - public void setVMI(double value) throws IOException { - writeCommand("&l" + formatDouble4(value) + "C"); - } - - /** - * Sets the cursor to a new absolute coordinate. - * @param x the X coordinate (in millipoints) - * @param y the Y coordinate (in millipoints) - * @throws IOException In case of an I/O error - */ - public void setCursorPos(double x, double y) throws IOException { - if (x < 0) { - //A negative x value will result in a relative movement so go to "0" first. - //But this will most probably have no effect anyway since you can't paint to the left - //of the logical page - writeCommand("&a0h" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); - } else { - writeCommand("&a" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); - } - } - - /** - * Pushes the current cursor position on a stack (stack size: max 20 entries) - * @throws IOException In case of an I/O error - */ - public void pushCursorPos() throws IOException { - writeCommand("&f0S"); - } - - /** - * Pops the current cursor position from the stack. - * @throws IOException In case of an I/O error - */ - public void popCursorPos() throws IOException { - writeCommand("&f1S"); - } - - /** - * Changes the current print direction while maintaining the current cursor position. - * @param rotate the rotation angle (counterclockwise), one of 0, 90, 180 and 270. - * @throws IOException In case of an I/O error - */ - public void changePrintDirection(int rotate) throws IOException { - writeCommand("&a" + rotate + "P"); - } - - /** - * Enters the HP GL/2 mode. - * @param restorePreviousHPGL2Cursor true if the previous HP GL/2 pen position should be - * restored, false if the current position is maintained - * @throws IOException In case of an I/O error - */ - public void enterHPGL2Mode(boolean restorePreviousHPGL2Cursor) throws IOException { - if (restorePreviousHPGL2Cursor) { - writeCommand("%0B"); - } else { - writeCommand("%1B"); - } - } - - /** - * Enters the PCL mode. - * @param restorePreviousPCLCursor true if the previous PCL cursor position should be restored, - * false if the current position is maintained - * @throws IOException In case of an I/O error - */ - public void enterPCLMode(boolean restorePreviousPCLCursor) throws IOException { - if (restorePreviousPCLCursor) { - writeCommand("%0A"); - } else { - writeCommand("%1A"); - } - } - - /** - * Generate a filled rectangle at the current cursor position. - * - * @param w the width in millipoints - * @param h the height in millipoints - * @param col the fill color - * @throws IOException In case of an I/O error - */ - protected void fillRect(int w, int h, Color col) throws IOException { - if ((w == 0) || (h == 0)) { - return; - } - if (h < 0) { - h *= -1; - } else { - //y += h; - } - - setPatternTransparencyMode(false); - writeCommand("*c" + formatDouble4(w / 100) + "h" - + formatDouble4(h / 100) + "V"); - int lineshade = convertToPCLShade(col); - writeCommand("*c" + lineshade + "G"); - writeCommand("*c2P"); - // Reset pattern transparency mode. - setPatternTransparencyMode(true); - } - - /** - * Sets the source transparency mode. - * @param transparent true if transparent, false for opaque - * @throws IOException In case of an I/O error - */ - public void setSourceTransparencyMode(boolean transparent) throws IOException { - setTransparencyMode(transparent, currentPatternTransparency); - } - - /** - * Sets the pattern transparency mode. - * @param transparent true if transparent, false for opaque - * @throws IOException In case of an I/O error - */ - public void setPatternTransparencyMode(boolean transparent) throws IOException { - setTransparencyMode(currentSourceTransparency, transparent); - } - - /** - * Sets the transparency modes. - * @param source source transparency: true if transparent, false for opaque - * @param pattern pattern transparency: true if transparent, false for opaque - * @throws IOException In case of an I/O error - */ - public void setTransparencyMode(boolean source, boolean pattern) throws IOException { - if (source != currentSourceTransparency && pattern != currentPatternTransparency) { - writeCommand("*v" + (source ? '0' : '1') + "n" + (pattern ? '0' : '1') + "O"); - } else if (source != currentSourceTransparency) { - writeCommand("*v" + (source ? '0' : '1') + "N"); - } else if (pattern != currentPatternTransparency) { - writeCommand("*v" + (pattern ? '0' : '1') + "O"); - } - this.currentSourceTransparency = source; - this.currentPatternTransparency = pattern; - } - - /** - * Convert an RGB color value to a grayscale from 0 to 100. - * @param r the red component - * @param g the green component - * @param b the blue component - * @return the gray value - */ - public final int convertToGray(int r, int g, int b) { - return (r * 30 + g * 59 + b * 11) / 100; - } - - /** - * Convert a Color value to a PCL shade value (0-100). - * @param col the color - * @return the PCL shade value (100=black) - */ - public final int convertToPCLShade(Color col) { - float gray = convertToGray(col.getRed(), col.getGreen(), col.getBlue()) / 255f; - return (int)(100 - (gray * 100f)); - } - - /** - * Select the current pattern - * @param patternID the pattern ID (*c#G command) - * @param pattern the pattern type (*v#T command) - * @throws IOException In case of an I/O error - */ - public void selectCurrentPattern(int patternID, int pattern) throws IOException { - if (pattern > 1) { - writeCommand("*c" + patternID + "G"); - } - writeCommand("*v" + pattern + "T"); - } - - /** - * Indicates whether an image is a monochrome (b/w) image. - * @param img the image - * @return true if it's a monochrome image - */ - public static boolean isMonochromeImage(RenderedImage img) { - ColorModel cm = img.getColorModel(); - if (cm instanceof IndexColorModel) { - IndexColorModel icm = (IndexColorModel)cm; - return icm.getMapSize() == 2; - } else { - return false; - } - } - - /** - * Indicates whether an image is a grayscale image. - * @param img the image - * @return true if it's a grayscale image - */ - public static boolean isGrayscaleImage(RenderedImage img) { - return (img.getColorModel().getColorSpace().getNumComponents() == 1); - } - - private MonochromeBitmapConverter createMonochromeBitmapConverter() { - MonochromeBitmapConverter converter = null; - try { - String clName = "org.apache.fop.render.pcl.JAIMonochromeBitmapConverter"; - Class clazz = Class.forName(clName); - converter = (MonochromeBitmapConverter)clazz.newInstance(); - } catch (ClassNotFoundException cnfe) { - // Class was not compiled so is not available. Simply ignore. - } catch (LinkageError le) { - // This can happen if fop was build with support for a - // particular provider (e.g. a binary fop distribution) - // but the required support files (i.e. JAI) are not - // available in the current runtime environment. - // Simply continue with the backup implementation. - } catch (InstantiationException e) { - // Problem instantiating the class, simply continue with the backup implementation - } catch (IllegalAccessException e) { - // Problem instantiating the class, simply continue with the backup implementation - } - if (converter == null) { - converter = new DefaultMonochromeBitmapConverter(); - } - return converter; - } - - private int calculatePCLResolution(int resolution) { - return calculatePCLResolution(resolution, false); - } - - /** - * Calculates the ideal PCL resolution for a given resolution. - * @param resolution the input resolution - * @param increased true if you want to go to a higher resolution, for example if you - * convert grayscale or color images to monochrome images so dithering has - * a chance to generate better quality. - * @return the resulting PCL resolution (one of 75, 100, 150, 200, 300, 600) - */ - private int calculatePCLResolution(int resolution, boolean increased) { - int choice = -1; - for (int i = PCL_RESOLUTIONS.length - 2; i >= 0; i--) { - if (resolution > PCL_RESOLUTIONS[i]) { - int idx = i + 1; - if (idx < PCL_RESOLUTIONS.length - 2) { - idx += increased ? 2 : 0; - } else if (idx < PCL_RESOLUTIONS.length - 1) { - idx += increased ? 1 : 0; - } - choice = idx; - break; - //return PCL_RESOLUTIONS[idx]; - } - } - if (choice < 0) { - choice = (increased ? 2 : 0); - } - while (choice > 0 && PCL_RESOLUTIONS[choice] > getMaximumBitmapResolution()) { - choice--; - } - return PCL_RESOLUTIONS[choice]; - } - - private boolean isValidPCLResolution(int resolution) { - return resolution == calculatePCLResolution(resolution); - } - - private Dimension getAdjustedDimension(Dimension orgDim, double orgResolution, - int pclResolution) { - if (orgResolution == pclResolution) { - return orgDim; - } else { - Dimension result = new Dimension(); - result.width = (int)Math.round((double)orgDim.width * pclResolution / orgResolution); - result.height = (int)Math.round((double)orgDim.height * pclResolution / orgResolution); - return result; - } - } - - //Threshold table to convert an alpha channel (8-bit) into a clip mask (1-bit) - private static final byte[] THRESHOLD_TABLE = new byte[256]; - static { // Initialize the arrays - for (int i = 0; i < 256; i++) { - THRESHOLD_TABLE[i] = (byte) ((i < 240) ? 255 : 0); - } - } - - private RenderedImage getMask(RenderedImage img, Dimension targetDim) { - ColorModel cm = img.getColorModel(); - if (cm.hasAlpha()) { - BufferedImage alpha = new BufferedImage(img.getWidth(), img.getHeight(), - BufferedImage.TYPE_BYTE_GRAY); - Raster raster = img.getData(); - GraphicsUtil.copyBand(raster, cm.getNumColorComponents(), alpha.getRaster(), 0); - - BufferedImageOp op1 = new LookupOp(new ByteLookupTable(0, THRESHOLD_TABLE), null); - BufferedImage alphat = op1.filter(alpha, null); - - BufferedImage mask; - if (true) { - mask = new BufferedImage(targetDim.width, targetDim.height, - BufferedImage.TYPE_BYTE_BINARY); - } else { - byte[] arr = {(byte)0, (byte)0xff}; - ColorModel colorModel = new IndexColorModel(1, 2, arr, arr, arr); - WritableRaster wraster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, - targetDim.width, targetDim.height, 1, 1, null); - mask = new BufferedImage(colorModel, wraster, false, null); - } - - Graphics2D g2d = mask.createGraphics(); - try { - AffineTransform at = new AffineTransform(); - double sx = targetDim.getWidth() / img.getWidth(); - double sy = targetDim.getHeight() / img.getHeight(); - at.scale(sx, sy); - g2d.drawRenderedImage(alphat, at); - } finally { - g2d.dispose(); - } - /* - try { - BatchDiffer.saveAsPNG(alpha, new java.io.File("D:/out-alpha.png")); - BatchDiffer.saveAsPNG(mask, new java.io.File("D:/out-mask.png")); - } catch (IOException e) { - e.printStackTrace(); - }*/ - return mask; - } else { - return null; - } - } - - /** - * Paint a bitmap at the current cursor position. The bitmap is converted to a monochrome - * (1-bit) bitmap image. - * @param img the bitmap image - * @param targetDim the target Dimention (in mpt) - * @param sourceTransparency true if the background should not be erased - * @throws IOException In case of an I/O error - */ - public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency) - throws IOException { - double targetResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width); - 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); - boolean scaled = !orgDim.equals(effDim); - - boolean monochrome = isMonochromeImage(img); - if (!monochrome) { - //Transparency mask disabled. Doesn't work reliably - final boolean transparencyDisabled = true; - RenderedImage mask = (transparencyDisabled ? null : getMask(img, effDim)); - if (mask != null) { - pushCursorPos(); - selectCurrentPattern(0, 1); //Solid white - setTransparencyMode(true, true); - paintMonochromeBitmap(mask, effResolution); - popCursorPos(); - } - - BufferedImage src = null; - if (img instanceof BufferedImage && !scaled) { - if (!isGrayscaleImage(img) || img.getColorModel().hasAlpha()) { - src = new BufferedImage(effDim.width, effDim.height, - BufferedImage.TYPE_BYTE_GRAY); - ColorConvertOp op = new ColorConvertOp( - ColorSpace.getInstance(ColorSpace.CS_GRAY), null); - op.filter((BufferedImage)img, src); - } else { - src = (BufferedImage)img; - } - } - if (src == null) { - src = new BufferedImage(effDim.width, effDim.height, - BufferedImage.TYPE_BYTE_GRAY); - Graphics2D g2d = src.createGraphics(); - try { - AffineTransform at = new AffineTransform(); - double sx = effDim.getWidth() / orgDim.getWidth(); - double sy = effDim.getHeight() / orgDim.getHeight(); - at.scale(sx, sy); - g2d.drawRenderedImage(img, at); - } finally { - g2d.dispose(); - } - } - MonochromeBitmapConverter converter = createMonochromeBitmapConverter(); - converter.setHint("quality", "false"); - - BufferedImage buf = (BufferedImage)converter.convertToMonochrome(src); - - RenderedImage red = buf; - selectCurrentPattern(0, 0); //Solid black - setTransparencyMode(sourceTransparency || mask != null, true); - paintMonochromeBitmap(red, effResolution); - } else { - //TODO untested! - RenderedImage effImg = img; - if (scaled) { - BufferedImage buf = new BufferedImage(effDim.width, effDim.height, - BufferedImage.TYPE_BYTE_BINARY); - Graphics2D g2d = buf.createGraphics(); - try { - AffineTransform at = new AffineTransform(); - double sx = effDim.getWidth() / orgDim.getWidth(); - double sy = effDim.getHeight() / orgDim.getHeight(); - at.scale(sx, sy); - g2d.drawRenderedImage(img, at); - } finally { - g2d.dispose(); - } - effImg = buf; - } - setSourceTransparencyMode(sourceTransparency); - selectCurrentPattern(0, 0); //Solid black - paintMonochromeBitmap(effImg, effResolution); - } - } - - /** - * Paint a bitmap at the current cursor position. The bitmap must be a monochrome - * (1-bit) bitmap image. - * @param img the bitmap image (must be 1-bit b/w) - * @param resolution the resolution of the image (must be a PCL resolution) - * @throws IOException In case of an I/O error - */ - public void paintMonochromeBitmap(RenderedImage img, int resolution) throws IOException { - if (!isValidPCLResolution(resolution)) { - throw new IllegalArgumentException("Invalid PCL resolution: " + resolution); - } - setRasterGraphicsResolution(resolution); - writeCommand("*r0f" + img.getHeight() + "t" + img.getWidth() + "s1A"); - Raster raster = img.getData(); - boolean monochrome = isMonochromeImage(img); - if (!monochrome) { - throw new IllegalArgumentException("img must be a monochrome image"); - } - - int x = 0; - int y = 0; - int imgw = img.getWidth(); - int imgh = img.getHeight(); - int bytewidth = (imgw / 8); - if ((imgw % 8) != 0) { - bytewidth++; - } - byte ib; - byte[] rle = new byte[bytewidth * 2]; //compressed (RLE) - byte[] uncompressed = new byte[bytewidth]; //uncompressed - int lastcount = -1; - byte lastbyte = 0; - int rlewidth = 0; - - // Transfer graphics data - for (y = 0; y < imgh; y++) { - ib = 0; - for (x = 0; x < imgw; x++) { - int sample = raster.getSample(x, y, 0); - //Set image bit for black - if ((sample == 0)) { - ib |= (1 << (7 - (x % 8))); - } - - //RLE encoding - if ((x % 8) == 7 || ((x + 1) == imgw)) { - if (rlewidth < bytewidth) { - if (lastcount >= 0) { - if (ib == lastbyte) { - lastcount++; - } else { - rle[rlewidth++] = (byte)(lastcount & 0xFF); - rle[rlewidth++] = lastbyte; - lastbyte = ib; - lastcount = 0; - } - } else { - lastbyte = ib; - lastcount = 0; - } - if (lastcount == 255 || ((x + 1) == imgw)) { - rle[rlewidth++] = (byte)(lastcount & 0xFF); - rle[rlewidth++] = lastbyte; - lastbyte = 0; - lastcount = -1; - } - } - uncompressed[x / 8] = ib; - ib = 0; - } - } - if (rlewidth < bytewidth) { - writeCommand("*b1m" + rlewidth + "W"); - this.out.write(rle, 0, rlewidth); - } else { - writeCommand("*b0m" + bytewidth + "W"); - this.out.write(uncompressed); - } - lastcount = -1; - rlewidth = 0; - } - - // End raster graphics - writeCommand("*rB"); - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java deleted file mode 100644 index 0e27a8d29..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java +++ /dev/null @@ -1,615 +0,0 @@ -/* - * 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.pcl; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Paint; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; -import java.awt.image.BufferedImage; -import java.awt.image.ImageObserver; -import java.awt.image.RenderedImage; -import java.awt.image.renderable.RenderableImage; -import java.io.IOException; -import java.text.AttributedCharacterIterator; - -import org.apache.fop.util.UnitConv; -import org.apache.xmlgraphics.java2d.AbstractGraphics2D; -import org.apache.xmlgraphics.java2d.GraphicContext; - -/** - * Graphics2D implementation implementing PCL and HP GL/2. - * Note: This class cannot be used stand-alone to create full PCL documents. - */ -public class PCLGraphics2D extends AbstractGraphics2D { - - /** The PCL generator */ - protected PCLGenerator gen; - - private boolean failOnUnsupportedFeature = true; - private boolean clippingDisabled = false; - - /** - * Create a new PCLGraphics2D. - * @param gen the PCL Generator to paint with - */ - public PCLGraphics2D(PCLGenerator gen) { - super(true); - this.gen = gen; - } - - /** - * Copy constructor - * @param g parent PCLGraphics2D - */ - public PCLGraphics2D(PCLGraphics2D g) { - super(true); - this.gen = g.gen; - } - - /** @see java.awt.Graphics#create() */ - public Graphics create() { - PCLGraphics2D copy = new PCLGraphics2D(this); - copy.setGraphicContext((GraphicContext)getGraphicContext().clone()); - return copy; - } - - /** @see java.awt.Graphics#dispose() */ - public void dispose() { - this.gen = null; - } - - /** - * Sets the GraphicContext - * @param c GraphicContext to use - */ - public void setGraphicContext(GraphicContext c) { - this.gc = c; - } - - /** - * Allows to disable all clipping operations. - * @param value true if clipping should be disabled. - */ - public void setClippingDisabled(boolean value) { - this.clippingDisabled = value; - } - - /** - * Central handler for IOExceptions for this class. - * @param ioe IOException to handle - */ - public void handleIOException(IOException ioe) { - //TODO Surely, there's a better way to do this. - ioe.printStackTrace(); - } - - /** - * Raises an UnsupportedOperationException if this instance is configured to do so and an - * unsupported feature has been requested. Clients can make use of this to fall back to - * a more compatible way of painting a PCL graphic. - * @param msg the error message to be displayed - */ - protected void handleUnsupportedFeature(String msg) { - if (this.failOnUnsupportedFeature) { - throw new UnsupportedOperationException(msg); - } - } - - /** @see java.awt.Graphics2D#getDeviceConfiguration() */ - public GraphicsConfiguration getDeviceConfiguration() { - return GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration(); - } - - /** - * Applies a new Stroke object. - * @param stroke Stroke object to use - * @throws IOException In case of an I/O problem - */ - protected void applyStroke(Stroke stroke) throws IOException { - if (stroke instanceof BasicStroke) { - BasicStroke bs = (BasicStroke)stroke; - - float[] da = bs.getDashArray(); - if (da != null) { - - gen.writeText("UL1,"); - int len = Math.min(20, da.length); - float patternLen = 0.0f; - for (int idx = 0; idx < len; idx++) { - patternLen += da[idx]; - } - if (len == 1) { - patternLen *= 2; - } - for (int idx = 0; idx < len; idx++) { - float perc = da[idx] * 100 / patternLen; - gen.writeText(gen.formatDouble2(perc)); - if (idx < da.length - 1) { - gen.writeText(","); - } - } - if (len == 1) { - gen.writeText("," + gen.formatDouble2(da[0] * 100 / patternLen )); - - } - gen.writeText(";"); - /* TODO Dash phase NYI - float offset = bs.getDashPhase(); - gen.writeln(gen.formatDouble4(offset) + " setdash"); - */ - Point2D ptLen = new Point2D.Double(patternLen, 0); - //interpret as absolute length - getTransform().deltaTransform(ptLen, ptLen); - double transLen = UnitConv.pt2mm(ptLen.distance(0, 0)); - gen.writeText("LT1," + gen.formatDouble4(transLen) + ",1;"); - } else { - gen.writeText("LT;"); - } - - gen.writeText("LA1"); //line cap - int ec = bs.getEndCap(); - switch (ec) { - case BasicStroke.CAP_BUTT: - gen.writeText(",1"); - break; - case BasicStroke.CAP_ROUND: - gen.writeText(",4"); - break; - case BasicStroke.CAP_SQUARE: - gen.writeText(",2"); - break; - default: System.err.println("Unsupported line cap: " + ec); - } - - gen.writeText(",2"); //line join - int lj = bs.getLineJoin(); - switch (lj) { - case BasicStroke.JOIN_MITER: - gen.writeText(",1"); - break; - case BasicStroke.JOIN_ROUND: - gen.writeText(",4"); - break; - case BasicStroke.JOIN_BEVEL: - gen.writeText(",5"); - break; - default: System.err.println("Unsupported line join: " + lj); - } - - float ml = bs.getMiterLimit(); - gen.writeText(",3" + gen.formatDouble4(ml)); - - float lw = bs.getLineWidth(); - Point2D ptSrc = new Point2D.Double(lw, 0); - //Pen widths are set as absolute metric values (WU0;) - Point2D ptDest = getTransform().deltaTransform(ptSrc, null); - double transDist = UnitConv.pt2mm(ptDest.distance(0, 0)); - //System.out.println("--" + ptDest.distance(0, 0) + " " + transDist); - gen.writeText(";PW" + gen.formatDouble4(transDist) + ";"); - - } else { - handleUnsupportedFeature("Unsupported Stroke: " + stroke.getClass().getName()); - } - } - - /** - * Applies a new Paint object. - * @param paint Paint object to use - * @throws IOException In case of an I/O problem - */ - protected void applyPaint(Paint paint) throws IOException { - if (paint instanceof Color) { - Color col = (Color)paint; - int shade = gen.convertToPCLShade(col); - gen.writeText("TR0;FT10," + shade + ";"); - } else { - handleUnsupportedFeature("Unsupported Paint: " + paint.getClass().getName()); - } - } - - private void writeClip(Shape imclip) throws IOException { - if (clippingDisabled) { - return; - } - if (imclip == null) { - //gen.writeText("IW;"); - } else { - handleUnsupportedFeature("Clipping is not supported. Shape: " + imclip); - /* This is an attempt to clip using the "InputWindow" (IW) but this only allows to - * clip a rectangular area. Force falling back to bitmap mode for now. - Rectangle2D bounds = imclip.getBounds2D(); - Point2D p1 = new Point2D.Double(bounds.getX(), bounds.getY()); - Point2D p2 = new Point2D.Double( - bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()); - getTransform().transform(p1, p1); - getTransform().transform(p2, p2); - gen.writeText("IW" + gen.formatDouble4(p1.getX()) - + "," + gen.formatDouble4(p2.getY()) - + "," + gen.formatDouble4(p2.getX()) - + "," + gen.formatDouble4(p1.getY()) + ";"); - */ - } - } - - /** @see java.awt.Graphics2D#draw(java.awt.Shape) */ - public void draw(Shape s) { - try { - AffineTransform trans = getTransform(); - - Shape imclip = getClip(); - writeClip(imclip); - - if (!Color.black.equals(getColor())) { - //TODO PCL 5 doesn't support colored pens, PCL5c has a pen color (PC) command - handleUnsupportedFeature("Only black is supported as stroke color: " + getColor()); - } - applyStroke(getStroke()); - - PathIterator iter = s.getPathIterator(trans); - processPathIteratorStroke(iter); - writeClip(null); - } catch (IOException ioe) { - handleIOException(ioe); - } - } - - /** @see java.awt.Graphics2D#fill(java.awt.Shape) */ - public void fill(Shape s) { - try { - AffineTransform trans = getTransform(); - Shape imclip = getClip(); - writeClip(imclip); - - applyPaint(getPaint()); - - PathIterator iter = s.getPathIterator(trans); - processPathIteratorFill(iter); - writeClip(null); - } catch (IOException ioe) { - handleIOException(ioe); - } - } - - /** - * Processes a path iterator generating the nexessary painting operations. - * @param iter PathIterator to process - * @throws IOException In case of an I/O problem. - */ - public void processPathIteratorStroke(PathIterator iter) throws IOException { - gen.writeText("\n"); - double[] vals = new double[6]; - boolean penDown = false; - double x = 0; - double y = 0; - StringBuffer sb = new StringBuffer(256); - penUp(sb); - while (!iter.isDone()) { - int type = iter.currentSegment(vals); - if (type == PathIterator.SEG_CLOSE) { - gen.writeText("PM;"); - gen.writeText(sb.toString()); - gen.writeText("PM2;EP;"); - sb.setLength(0); - iter.next(); - continue; - } else if (type == PathIterator.SEG_MOVETO) { - gen.writeText(sb.toString()); - sb.setLength(0); - if (penDown) { - penUp(sb); - penDown = false; - } - } else { - if (!penDown) { - penDown(sb); - penDown = true; - } - } - switch (type) { - case PathIterator.SEG_CLOSE: - break; - case PathIterator.SEG_MOVETO: - x = vals[0]; - y = vals[1]; - plotAbsolute(x, y, sb); - gen.writeText(sb.toString()); - sb.setLength(0); - break; - case PathIterator.SEG_LINETO: - x = vals[0]; - y = vals[1]; - plotAbsolute(x, y, sb); - break; - case PathIterator.SEG_CUBICTO: - x = vals[4]; - y = vals[5]; - bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); - break; - case PathIterator.SEG_QUADTO: - double originX = x; - double originY = y; - x = vals[2]; - y = vals[3]; - quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); - break; - default: - break; - } - iter.next(); - } - sb.append("\n"); - gen.writeText(sb.toString()); - } - - /** - * Processes a path iterator generating the nexessary painting operations. - * @param iter PathIterator to process - * @throws IOException In case of an I/O problem. - */ - public void processPathIteratorFill(PathIterator iter) throws IOException { - gen.writeText("\n"); - double[] vals = new double[6]; - boolean penDown = false; - double x = 0; - double y = 0; - boolean pendingPM0 = true; - StringBuffer sb = new StringBuffer(256); - penUp(sb); - while (!iter.isDone()) { - int type = iter.currentSegment(vals); - if (type == PathIterator.SEG_CLOSE) { - sb.append("PM1;"); - iter.next(); - continue; - } else if (type == PathIterator.SEG_MOVETO) { - if (penDown) { - penUp(sb); - penDown = false; - } - } else { - if (!penDown) { - penDown(sb); - penDown = true; - } - } - switch (type) { - case PathIterator.SEG_MOVETO: - x = vals[0]; - y = vals[1]; - plotAbsolute(x, y, sb); - break; - case PathIterator.SEG_LINETO: - x = vals[0]; - y = vals[1]; - plotAbsolute(x, y, sb); - break; - case PathIterator.SEG_CUBICTO: - x = vals[4]; - y = vals[5]; - bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); - break; - case PathIterator.SEG_QUADTO: - double originX = x; - double originY = y; - x = vals[2]; - y = vals[3]; - quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); - break; - default: - throw new IllegalStateException("Must not get here"); - } - if (pendingPM0) { - pendingPM0 = false; - sb.append("PM;"); - } - iter.next(); - } - sb.append("PM2;"); - fillPolygon(iter.getWindingRule(), sb); - sb.append("\n"); - gen.writeText(sb.toString()); - } - - private void fillPolygon(int windingRule, StringBuffer sb) { - int fillMethod = (windingRule == PathIterator.WIND_EVEN_ODD ? 0 : 1); - sb.append("FP").append(fillMethod).append(";"); - } - - private void plotAbsolute(double x, double y, StringBuffer sb) { - sb.append("PA").append(gen.formatDouble4(x)); - sb.append(",").append(gen.formatDouble4(y)).append(";"); - } - - private void bezierAbsolute(double x1, double y1, double x2, double y2, double x3, double y3, - StringBuffer sb) { - sb.append("BZ").append(gen.formatDouble4(x1)); - sb.append(",").append(gen.formatDouble4(y1)); - sb.append(",").append(gen.formatDouble4(x2)); - sb.append(",").append(gen.formatDouble4(y2)); - sb.append(",").append(gen.formatDouble4(x3)); - sb.append(",").append(gen.formatDouble4(y3)).append(";"); - } - - private void quadraticBezierAbsolute(double originX, double originY, - double x1, double y1, double x2, double y2, StringBuffer sb) { - //Quadratic Bezier curve can be mapped to a normal bezier curve - //See http://pfaedit.sourceforge.net/bezier.html - double nx1 = originX + (2.0 / 3.0) * (x1 - originX); - double ny1 = originY + (2.0 / 3.0) * (y1 - originY); - - double nx2 = nx1 + (1.0 / 3.0) * (x2 - originX); - double ny2 = ny1 + (1.0 / 3.0) * (y2 - originY); - - bezierAbsolute(nx1, ny1, nx2, ny2, x2, y2, sb); - } - - private void penDown(StringBuffer sb) { - sb.append("PD;"); - } - - private void penUp(StringBuffer sb) { - sb.append("PU;"); - } - - /** @see java.awt.Graphics2D#drawString(java.lang.String, float, float) */ - public void drawString(String s, float x, float y) { - java.awt.Font awtFont = getFont(); - FontRenderContext frc = getFontRenderContext(); - GlyphVector gv = awtFont.createGlyphVector(frc, s); - Shape glyphOutline = gv.getOutline(x, y); - fill(glyphOutline); - } - - /** @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) */ - public void drawString(AttributedCharacterIterator iterator, float x, - float y) { - // TODO Auto-generated method stub - handleUnsupportedFeature("drawString NYI"); - } - - /** - * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage, - * java.awt.geom.AffineTransform) - */ - public void drawRenderedImage(RenderedImage img, AffineTransform xform) { - handleUnsupportedFeature("Bitmap images are not supported"); - } - - /** - * @see java.awt.Graphics2D#drawRenderableImage(java.awt.image.renderable.RenderableImage, - * java.awt.geom.AffineTransform) - */ - public void drawRenderableImage(RenderableImage img, AffineTransform xform) { - handleUnsupportedFeature("Bitmap images are not supported"); - } - - /** - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, - * java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, int width, int height, - ImageObserver observer) { - handleUnsupportedFeature("Bitmap images are not supported"); - return false; - } - - /** - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - handleUnsupportedFeature("Bitmap images are not supported"); - return false; - /* - * First attempt disabled. - * Reasons: Lack of transparency control, positioning and rotation issues - final int width = img.getWidth(observer); - final int height = img.getHeight(observer); - if (width == -1 || height == -1) { - return false; - } - - Dimension size = new Dimension(width, height); - BufferedImage buf = buildBufferedImage(size); - - java.awt.Graphics2D g = buf.createGraphics(); - try { - g.setComposite(AlphaComposite.SrcOver); - g.setBackground(new Color(255, 255, 255)); - g.setPaint(new Color(255, 255, 255)); - g.fillRect(0, 0, width, height); - g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight())); - - if (!g.drawImage(img, 0, 0, observer)) { - return false; - } - } finally { - g.dispose(); - } - - try { - AffineTransform at = getTransform(); - gen.enterPCLMode(false); - //Shape imclip = getClip(); Clipping is not available in PCL - Point2D p1 = new Point2D.Double(x, y); - at.transform(p1, p1); - pclContext.getTransform().transform(p1, p1); - gen.setCursorPos(p1.getX(), p1.getY()); - gen.paintBitmap(buf, 72); - gen.enterHPGL2Mode(false); - } catch (IOException ioe) { - handleIOException(ioe); - } - - return true;*/ - } - - /** @see java.awt.Graphics#copyArea(int, int, int, int, int, int) */ - public void copyArea(int x, int y, int width, int height, int dx, int dy) { - // TODO Auto-generated method stub - handleUnsupportedFeature("copyArea NYI"); - } - - /** @see java.awt.Graphics#setXORMode(java.awt.Color) */ - public void setXORMode(Color c1) { - // TODO Auto-generated method stub - handleUnsupportedFeature("setXORMode NYI"); - } - - /** - * Used to create proper font metrics - */ - private Graphics2D fmg; - - { - BufferedImage bi = new BufferedImage(1, 1, - BufferedImage.TYPE_INT_ARGB); - - fmg = bi.createGraphics(); - } - - /** - * Creates a buffered image. - * @param size dimensions of the image to be created - * @return the buffered image - */ - protected BufferedImage buildBufferedImage(Dimension size) { - return new BufferedImage(size.width, size.height, - BufferedImage.TYPE_BYTE_GRAY); - } - - /** @see java.awt.Graphics#getFontMetrics(java.awt.Font) */ - public java.awt.FontMetrics getFontMetrics(java.awt.Font f) { - return fmg.getFontMetrics(f); - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java deleted file mode 100644 index d2e91f8e6..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.pcl; - -import java.awt.Dimension; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -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.fop.render.AbstractGraphics2DAdapter; -import org.apache.fop.render.Graphics2DImagePainter; -import org.apache.fop.render.RendererContext; -import org.apache.fop.util.UnitConv; -import org.apache.xmlgraphics.java2d.GraphicContext; - -/** - * Graphics2DAdapter implementation for PCL and HP GL/2. - */ -public class PCLGraphics2DAdapter extends AbstractGraphics2DAdapter { - - /** logging instance */ - private static Log log = LogFactory.getLog(PCLGraphics2DAdapter.class); - - /** - * Main constructor - */ - public PCLGraphics2DAdapter() { - } - - /** @see org.apache.fop.render.Graphics2DAdapter */ - public void paintImage(Graphics2DImagePainter painter, - RendererContext context, - int x, int y, int width, int height) throws IOException { - PCLRendererContext pclContext = PCLRendererContext.wrapRendererContext(context); - PCLRenderer pcl = (PCLRenderer)context.getRenderer(); - PCLGenerator gen = pcl.gen; - - // get the 'width' and 'height' attributes of the image/document - Dimension dim = painter.getImageSize(); - float imw = (float)dim.getWidth(); - float imh = (float)dim.getHeight(); - - boolean painted = false; - boolean paintAsBitmap = pclContext.paintAsBitmap(); - if (!paintAsBitmap) { - ByteArrayOutputStream baout = new ByteArrayOutputStream(); - PCLGenerator tempGen = new PCLGenerator(baout, gen.getMaximumBitmapResolution()); - try { - GraphicContext ctx = (GraphicContext)pcl.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(pclContext.isClippingDisabled()); - Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh); - painter.paint(graphics, area); - - //If we arrive here, the graphic is natively paintable, so write the graphic - pcl.saveGraphicsState(); - pcl.setCursorPos(x, y); - gen.writeCommand("*c" + gen.formatDouble4(width / 100f) + "x" - + gen.formatDouble4(height / 100f) + "Y"); - gen.writeCommand("*c0T"); - gen.enterHPGL2Mode(false); - gen.writeText("\nIN;"); - gen.writeText("SP1;"); - //One Plotter unit is 0.025mm! - double scale = imw / UnitConv.mm2pt(imw * 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); - pcl.restoreGraphicsState(); - 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 - int resolution = (int)Math.round(context.getUserAgent().getTargetResolution()); - BufferedImage bi = paintToBufferedImage(painter, pclContext, resolution, true, false); - - pcl.setCursorPos(x, y); - gen.paintBitmap(bi, new Dimension(width, height), pclContext.isSourceTransparency()); - } - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java b/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java deleted file mode 100644 index 43978e954..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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.pcl; - -import java.awt.Dimension; -import java.awt.Rectangle; -import java.util.Iterator; -import java.util.List; - -import org.apache.fop.util.UnitConv; - -/** - * This class represents a page format with PCL-specific properties. - */ -public class PCLPageDefinition { - - private static List pageDefinitions; - private static PCLPageDefinition defaultPageDefinition; - - private String name; - private int selector; - private Dimension physicalPageSize; - private Rectangle logicalPageRect; - private boolean landscape; - - static { - createPageDefinitions(); - } - - /** - * Main constructor - * @param name the name of the page definition - * @param selector the selector used by the &l#A command (page size) - * @param physicalPageSize the physical page size - * @param logicalPageRect the rectangle defining the logical page - * @param landscape true if it is a landscape format - */ - public PCLPageDefinition(String name, int selector, Dimension physicalPageSize, - Rectangle logicalPageRect, boolean landscape) { - this.name = name; - this.selector = selector; - this.physicalPageSize = physicalPageSize; - this.logicalPageRect = logicalPageRect; - this.landscape = landscape; - } - - /** @return the name of the page definition */ - public String getName() { - return this.name; - } - - /** @return the selector used by the &l#A command (page size) */ - public int getSelector() { - return this.selector; - } - - /** @return true if it is a landscape format */ - public boolean isLandscapeFormat() { - return this.landscape; - } - - /** @return the physical page size */ - public Dimension getPhysicalPageSize() { - return this.physicalPageSize; - } - - /** @return the rectangle defining the logical page */ - public Rectangle getLogicalPageRect() { - return this.logicalPageRect; - } - - private boolean matches(long width, long height, int errorMargin) { - return (Math.abs(this.physicalPageSize.width - width) < errorMargin) - && (Math.abs(this.physicalPageSize.height - height) < errorMargin); - } - - /** @see java.lang.Object#toString() */ - public String toString() { - return getName(); - } - - /** - * Tries to determine a matching page definition. - * @param width the physical page width (in mpt) - * @param height the physical page height (in mpt) - * @param errorMargin the error margin for detecting the right page definition - * @return the page definition or null if no match was found - */ - public static PCLPageDefinition getPageDefinition(long width, long height, int errorMargin) { - Iterator iter = pageDefinitions.iterator(); - while (iter.hasNext()) { - PCLPageDefinition def = (PCLPageDefinition)iter.next(); - if (def.matches(width, height, errorMargin)) { - return def; - } - } - return null; - } - - /** @return the default page definition (letter) */ - public static PCLPageDefinition getDefaultPageDefinition() { - return defaultPageDefinition; - } - - /** - * Converts an offset values for logical pages to millipoints. The values are given as pixels - * in a 300dpi environment. - * @param offset the offset as given in the PCL 5 specification (under "Printable Area") - * @return the converted value in millipoints - */ - private static int convert300dpiDotsToMpt(int offset) { - return (int)Math.round(((double)offset) * 72000 / 300); - } - - private static Dimension createPhysicalPageSizeInch(float width, float height) { - return new Dimension( - (int)Math.round(UnitConv.in2mpt(width)), - (int)Math.round(UnitConv.in2mpt(height))); - } - - private static Dimension createPhysicalPageSizeMm(float width, float height) { - return new Dimension( - (int)Math.round(UnitConv.mm2mpt(width)), - (int)Math.round(UnitConv.mm2mpt(height))); - } - - private static Rectangle createLogicalPageRect(int x, int y, int width, int height) { - return new Rectangle(convert300dpiDotsToMpt(x), convert300dpiDotsToMpt(y), - convert300dpiDotsToMpt(width), convert300dpiDotsToMpt(height)); - } - - private static void createPageDefinitions() { - pageDefinitions = new java.util.ArrayList(); - pageDefinitions.add(new PCLPageDefinition("Letter", 2, - createPhysicalPageSizeInch(8.5f, 11), - createLogicalPageRect(75, 0, 2400, 3300), false)); - defaultPageDefinition = new PCLPageDefinition("Legal", 3, - createPhysicalPageSizeInch(8.5f, 14), - createLogicalPageRect(75, 0, 2400, 4200), false); - pageDefinitions.add(defaultPageDefinition); - pageDefinitions.add(new PCLPageDefinition("Executive", 1, - createPhysicalPageSizeInch(7.25f, 10.5f), - createLogicalPageRect(75, 0, 2025, 3150), false)); - pageDefinitions.add(new PCLPageDefinition("Ledger", 6, - createPhysicalPageSizeInch(11, 17), - createLogicalPageRect(75, 0, 3150, 5100), false)); - pageDefinitions.add(new PCLPageDefinition("A4", 26, - createPhysicalPageSizeMm(210, 297), - createLogicalPageRect(71, 0, 2338, 3507), false)); - pageDefinitions.add(new PCLPageDefinition("A3", 27, - createPhysicalPageSizeMm(297, 420), - createLogicalPageRect(71, 0, 3365, 4960), false)); - - //TODO Add envelope definitions - - pageDefinitions.add(new PCLPageDefinition("LetterL", 2, - createPhysicalPageSizeInch(11, 8.5f), - createLogicalPageRect(60, 0, 3180, 2550), true)); - pageDefinitions.add(new PCLPageDefinition("LegalL", 3, - createPhysicalPageSizeInch(14, 8.5f), - createLogicalPageRect(60, 0, 4080, 2550), true)); - pageDefinitions.add(new PCLPageDefinition("ExecutiveL", 1, - createPhysicalPageSizeInch(10.5f, 7.25f), - createLogicalPageRect(60, 0, 3030, 2175), true)); - pageDefinitions.add(new PCLPageDefinition("LedgerL", 6, - createPhysicalPageSizeInch(17, 11), - createLogicalPageRect(60, 0, 4980, 3300), true)); - pageDefinitions.add(new PCLPageDefinition("A4L", 26, - createPhysicalPageSizeMm(297, 210), - createLogicalPageRect(59, 0, 3389, 2480), true)); - pageDefinitions.add(new PCLPageDefinition("A3L", 27, - createPhysicalPageSizeMm(420, 297), - createLogicalPageRect(59, 0, 4842, 3507), true)); - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java b/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java deleted file mode 100644 index 05952d933..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java +++ /dev/null @@ -1,1448 +0,0 @@ -/* - * Copyright 1999-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.pcl; - -//Java -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.color.ColorSpace; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.ComponentColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.PixelInterleavedSampleModel; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; -import java.util.Stack; - -import org.w3c.dom.Document; - -import org.apache.xmlgraphics.java2d.GraphicContext; - -// FOP -import org.apache.avalon.framework.configuration.Configuration; -import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.apps.FOPException; -import org.apache.fop.apps.MimeConstants; -import org.apache.fop.area.Area; -import org.apache.fop.area.Block; -import org.apache.fop.area.BlockViewport; -import org.apache.fop.area.CTM; -import org.apache.fop.area.PageViewport; -import org.apache.fop.area.Trait; -import org.apache.fop.area.inline.AbstractTextArea; -import org.apache.fop.area.inline.ForeignObject; -import org.apache.fop.area.inline.Image; -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.area.inline.SpaceArea; -import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.inline.Viewport; -import org.apache.fop.area.inline.WordArea; -import org.apache.fop.fo.extensions.ExtensionElementMapping; -import org.apache.fop.fonts.Font; -import org.apache.fop.fonts.FontInfo; -import org.apache.fop.image.EPSImage; -import org.apache.fop.image.FopImage; -import org.apache.fop.image.ImageFactory; -import org.apache.fop.image.XMLImage; -import org.apache.fop.render.Graphics2DAdapter; -import org.apache.fop.render.Graphics2DImagePainter; -import org.apache.fop.render.PrintRenderer; -import org.apache.fop.render.RendererContext; -import org.apache.fop.render.RendererContextConstants; -import org.apache.fop.render.java2d.FontMetricsMapper; -import org.apache.fop.render.java2d.FontSetup; -import org.apache.fop.render.java2d.Java2DRenderer; -import org.apache.fop.render.pcl.extensions.PCLElementMapping; -import org.apache.fop.traits.BorderProps; -import org.apache.fop.util.QName; -import org.apache.fop.util.UnitConv; - -/** - * Renderer for the PCL 5 printer language. It also uses HP GL/2 for certain graphic elements. - */ -public class PCLRenderer extends PrintRenderer { - - /** logging instance */ - private static Log log = LogFactory.getLog(PCLRenderer.class); - - /** The MIME type for PCL */ - public static final String MIME_TYPE = MimeConstants.MIME_PCL_ALT; - - private static final QName CONV_MODE - = new QName(ExtensionElementMapping.URI, null, "conversion-mode"); - private static final QName SRC_TRANSPARENCY - = new QName(ExtensionElementMapping.URI, null, "source-transparency"); - - /** The OutputStream to write the PCL stream to */ - protected OutputStream out; - - /** The PCL generator */ - protected PCLGenerator gen; - private boolean ioTrouble = false; - - private Stack graphicContextStack = new Stack(); - private GraphicContext graphicContext = new GraphicContext(); - - private PCLPageDefinition currentPageDefinition; - private int currentPrintDirection = 0; - private GeneralPath currentPath = null; - 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). - */ - 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; - - /** - * Create the PCL renderer - */ - public PCLRenderer() { - } - - /** - * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) - */ - public void configure(Configuration cfg) throws ConfigurationException { - super.configure(cfg); - String rendering = cfg.getChild("rendering").getValue(null); - if ("quality".equalsIgnoreCase(rendering)) { - this.qualityBeforeSpeed = true; - } else if ("speed".equalsIgnoreCase(rendering)) { - this.qualityBeforeSpeed = false; - } else if (rendering != null) { - throw new ConfigurationException( - "Valid values for 'rendering' are 'quality' and 'speed'. Value found: " - + rendering); - } - String textRendering = cfg.getChild("text-rendering").getValue(null); - if ("bitmap".equalsIgnoreCase(textRendering)) { - this.allTextAsBitmaps = true; - } else if ("auto".equalsIgnoreCase(textRendering)) { - this.allTextAsBitmaps = false; - } else if (textRendering != null) { - throw new ConfigurationException( - "Valid values for 'text-rendering' are 'auto' and 'bitmap'. Value found: " - + textRendering); - } - } - - /** - * @see org.apache.fop.render.Renderer#setupFontInfo(org.apache.fop.fonts.FontInfo) - */ - public void setupFontInfo(FontInfo inFontInfo) { - //Don't call super.setupFontInfo() here! - //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 g = fontImage.createGraphics(); - //The next line is important to get accurate font metrics! - g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_ON); - FontSetup.setup(fontInfo, g); - } - - /** - * Central exception handler for I/O exceptions. - * @param ioe IOException to handle - */ - protected void handleIOTrouble(IOException ioe) { - if (!ioTrouble) { - log.error("Error while writing to target file", ioe); - ioTrouble = true; - } - } - - /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */ - public Graphics2DAdapter getGraphics2DAdapter() { - return new PCLGraphics2DAdapter(); - } - - /** @return the GraphicContext used to track coordinate system transformations */ - public GraphicContext getGraphicContext() { - return this.graphicContext; - } - - /** @return the target resolution */ - protected int getResolution() { - int resolution = (int)Math.round(userAgent.getTargetResolution()); - if (resolution <= 300) { - return 300; - } else { - return 600; - } - } - - /** - * 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; - } - - /** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */ - public void startRenderer(OutputStream outputStream) throws IOException { - log.debug("Rendering areas to PCL..."); - this.out = outputStream; - this.gen = new PCLGenerator(out, getResolution()); - - gen.universalEndOfLanguage(); - gen.writeText("@PJL COMMENT Produced by " + userAgent.getProducer() + "\n"); - if (userAgent.getTitle() != null) { - gen.writeText("@PJL JOB NAME = \"" + userAgent.getTitle() + "\"\n"); - } - gen.writeText("@PJL SET RESOLUTION = " + getResolution() + "\n"); - gen.writeText("@PJL ENTER LANGUAGE = PCL\n"); - gen.resetPrinter(); - gen.setUnitOfMeasure(getResolution()); - gen.setRasterGraphicsResolution(getResolution()); - } - - /** @see org.apache.fop.render.Renderer#stopRenderer() */ - public void stopRenderer() throws IOException { - gen.separateJobs(); - gen.resetPrinter(); - gen.universalEndOfLanguage(); - } - - /** @see org.apache.fop.render.AbstractRenderer */ - public String getMimeType() { - return MIME_TYPE; - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderPage(org.apache.fop.area.PageViewport) - */ - public void renderPage(PageViewport page) throws IOException, FOPException { - saveGraphicsState(); - - //Paper source - String paperSource = page.getForeignAttributeValue( - new QName(PCLElementMapping.NAMESPACE, null, "paper-source")); - if (paperSource != null) { - gen.selectPaperSource(Integer.parseInt(paperSource)); - } - - //Page size - final long pagewidth = Math.round(page.getViewArea().getWidth()); - final long pageheight = Math.round(page.getViewArea().getHeight()); - selectPageFormat(pagewidth, pageheight); - - super.renderPage(page); - - //Eject page - gen.formFeed(); - restoreGraphicsState(); - } - - private void selectPageFormat(long pagewidth, long pageheight) throws IOException { - this.currentPageDefinition = PCLPageDefinition.getPageDefinition( - pagewidth, pageheight, 1000); - - if (this.currentPageDefinition == null) { - this.currentPageDefinition = PCLPageDefinition.getDefaultPageDefinition(); - log.warn("Paper type could not be determined. Falling back to: " - + this.currentPageDefinition.getName()); - } - log.debug("page size: " + currentPageDefinition.getPhysicalPageSize()); - log.debug("logical page: " + currentPageDefinition.getLogicalPageRect()); - if (this.currentPageDefinition.isLandscapeFormat()) { - gen.writeCommand("&l1O"); //Orientation - } else { - gen.writeCommand("&l0O"); //Orientation - } - gen.selectPageSize(this.currentPageDefinition.getSelector()); - - gen.clearHorizontalMargins(); - gen.setTopMargin(0); - } - - /** Saves the current graphics state on the stack. */ - protected void saveGraphicsState() { - graphicContextStack.push(graphicContext); - graphicContext = (GraphicContext)graphicContext.clone(); - } - - /** Restores the last graphics state from the stack. */ - protected void restoreGraphicsState() { - graphicContext = (GraphicContext)graphicContextStack.pop(); - } - - /** - * Clip an area. write a clipping operation given coordinates in the current - * transform. Coordinates are in points. - * - * @param x the x coordinate - * @param y the y coordinate - * @param width the width of the area - * @param height the height of the area - */ - protected void clipRect(float x, float y, float width, float height) { - //PCL cannot clip (only HP GL/2 can) - } - - private Point2D transformedPoint(float x, float y) { - return transformedPoint(Math.round(x), Math.round(y)); - } - - 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; - } - - private void changePrintDirection() { - AffineTransform at = graphicContext.getTransform(); - int newDir; - try { - if (at.getScaleX() == 0 && at.getScaleY() == 0 - && at.getShearX() == 1 && at.getShearY() == -1) { - newDir = 90; - } else if (at.getScaleX() == -1 && at.getScaleY() == -1 - && at.getShearX() == 0 && at.getShearY() == 0) { - newDir = 180; - } else if (at.getScaleX() == 0 && at.getScaleY() == 0 - && at.getShearX() == -1 && at.getShearY() == 1) { - newDir = 270; - } else { - newDir = 0; - } - if (newDir != this.currentPrintDirection) { - this.currentPrintDirection = newDir; - gen.changePrintDirection(this.currentPrintDirection); - } - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - - /** - * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D) - */ - protected void startVParea(CTM ctm, Rectangle2D clippingRect) { - saveGraphicsState(); - AffineTransform at = new AffineTransform(ctm.toArray()); - graphicContext.transform(at); - changePrintDirection(); - if (log.isDebugEnabled()) { - log.debug("startVPArea: " + at + " --> " + graphicContext.getTransform()); - } - } - - /** - * @see org.apache.fop.render.AbstractRenderer#endVParea() - */ - protected void endVParea() { - restoreGraphicsState(); - changePrintDirection(); - if (log.isDebugEnabled()) { - log.debug("endVPArea() --> " + graphicContext.getTransform()); - } - } - - /** - * Handle block traits. - * The block could be any sort of block with any positioning - * so this should render the traits such as border and background - * in its position. - * - * @param block the block to render the traits - */ - protected void handleBlockTraits(Block block) { - int borderPaddingStart = block.getBorderAndPaddingWidthStart(); - int borderPaddingBefore = block.getBorderAndPaddingWidthBefore(); - - float startx = currentIPPosition / 1000f; - float starty = currentBPPosition / 1000f; - float width = block.getIPD() / 1000f; - float height = block.getBPD() / 1000f; - - startx += block.getStartIndent() / 1000f; - startx -= block.getBorderAndPaddingWidthStart() / 1000f; - - width += borderPaddingStart / 1000f; - width += block.getBorderAndPaddingWidthEnd() / 1000f; - height += borderPaddingBefore / 1000f; - height += block.getBorderAndPaddingWidthAfter() / 1000f; - - drawBackAndBorders(block, startx, starty, width, height); - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea) - */ - protected void renderText(final TextArea text) { - renderInlineAreaBackAndBorders(text); - - String fontname = getInternalFontNameForArea(text); - int fontsize = text.getTraitAsInteger(Trait.FONT_SIZE); - - //Determine position - int saveIP = currentIPPosition; - int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); - int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); - - try { - final Color col = (Color)text.getTrait(Trait.COLOR); - boolean pclFont = allTextAsBitmaps - ? false - : setFont(fontname, fontsize, text.getText()); - if (pclFont) { - //this.currentFill = col; - if (col != null) { - //useColor(ct); - gen.setTransparencyMode(true, false); - gen.selectCurrentPattern(gen.convertToPCLShade(col), 2); - } - - saveGraphicsState(); - graphicContext.translate(rx, bl); - setCursorPos(0, 0); - gen.setTransparencyMode(true, true); - - super.renderText(text); //Updates IPD and renders words and spaces - restoreGraphicsState(); - } else { - //Use Java2D to paint different fonts via bitmap - 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( - 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); - Map atts = new java.util.HashMap(); - atts.put(CONV_MODE, "bitmap"); - atts.put(SRC_TRANSPARENCY, "true"); - rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); - - 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); - } - - public Dimension getImageSize() { - return paintRect.getSize(); - } - - }; - g2a.paintImage(painter, rc, - paintRect.x, paintRect.y, paintRect.width, paintRect.height); - currentIPPosition = saveIP + text.getAllocIPD(); - } - - //renderTextDecoration(tf, fontsize, area, bl, rx); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - - /** - * Sets the current cursor position. The coordinates are transformed to the absolute position - * on the logical PCL page and then passed on to the PCLGenerator. - * @param x the x coordinate (in millipoints) - * @param y the y coordinate (in millipoints) - */ - void setCursorPos(float x, float y) { - try { - Point2D transPoint = transformedPoint(x, y); - gen.setCursorPos(transPoint.getX(), transPoint.getY()); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - - /** - * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() - */ - protected void clip() { - if (currentPath == null) { - throw new IllegalStateException("No current path available!"); - } - //TODO Find a good way to do clipping. PCL itself cannot clip. - currentPath = null; - } - - /** - * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() - */ - protected void closePath() { - currentPath.closePath(); - } - - /** - * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) - */ - protected void lineTo(float x, float y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.lineTo(x, y); - } - - /** - * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) - */ - protected void moveTo(float x, float y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.moveTo(x, y); - } - - /** - * Fill a rectangular area. - * @param x the x coordinate (in pt) - * @param y the y coordinate (in pt) - * @param width the width of the rectangle - * @param height the height of the rectangle - */ - protected void fillRect(float x, float y, float width, float height) { - try { - setCursorPos(x * 1000, y * 1000); - gen.fillRect((int)(width * 1000), (int)(height * 1000), - this.currentFillColor); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - - /** - * Sets the new current fill color. - * @param color the color - */ - protected void updateFillColor(java.awt.Color color) { - this.currentFillColor = color; - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderWord(org.apache.fop.area.inline.WordArea) - */ - protected void renderWord(WordArea word) { - //Font font = getFontFromArea(word.getParentArea()); - - String s = word.getWord(); - - try { - gen.writeText(s); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - - super.renderWord(word); - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderSpace(org.apache.fop.area.inline.SpaceArea) - */ - protected void renderSpace(SpaceArea space) { - AbstractTextArea textArea = (AbstractTextArea)space.getParentArea(); - String s = space.getSpace(); - char sp = s.charAt(0); - Font font = getFontFromArea(textArea); - - int tws = (space.isAdjustable() - ? textArea.getTextWordSpaceAdjust() - + 2 * textArea.getTextLetterSpaceAdjust() - : 0); - - double dx = (font.getCharWidth(sp) + tws) / 100f; - try { - gen.writeCommand("&a+" + gen.formatDouble2(dx) + "H"); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - super.renderSpace(space); - } - - /** - * Render an inline viewport. - * This renders an inline viewport by clipping if necessary. - * @param viewport the viewport to handle - * @todo Copied from AbstractPathOrientedRenderer - */ - public void renderViewport(Viewport viewport) { - - float x = currentIPPosition / 1000f; - float y = (currentBPPosition + viewport.getOffset()) / 1000f; - float width = viewport.getIPD() / 1000f; - float height = viewport.getBPD() / 1000f; - // TODO: Calculate the border rect correctly. - float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f; - float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f; - float bpwidth = borderPaddingStart - + (viewport.getBorderAndPaddingWidthEnd() / 1000f); - float bpheight = borderPaddingBefore - + (viewport.getBorderAndPaddingWidthAfter() / 1000f); - - drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight); - - if (viewport.getClip()) { - saveGraphicsState(); - - clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height); - } - super.renderViewport(viewport); - - if (viewport.getClip()) { - restoreGraphicsState(); - } - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List) - */ - protected void renderBlockViewport(BlockViewport bv, List children) { - // clip and position viewport if necessary - - // save positions - int saveIP = currentIPPosition; - int saveBP = currentBPPosition; - //String saveFontName = currentFontName; - - CTM ctm = bv.getCTM(); - int borderPaddingStart = bv.getBorderAndPaddingWidthStart(); - int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore(); - float x, y; - x = (float)(bv.getXOffset() + containingIPPosition) / 1000f; - y = (float)(bv.getYOffset() + containingBPPosition) / 1000f; - //This is the content-rect - float width = (float)bv.getIPD() / 1000f; - float height = (float)bv.getBPD() / 1000f; - - - if (bv.getPositioning() == Block.ABSOLUTE - || bv.getPositioning() == Block.FIXED) { - - currentIPPosition = bv.getXOffset(); - currentBPPosition = bv.getYOffset(); - - //For FIXED, we need to break out of the current viewports to the - //one established by the page. We save the state stack for restoration - //after the block-container has been painted. See below. - List breakOutList = null; - if (bv.getPositioning() == Block.FIXED) { - breakOutList = breakOutOfStateStack(); - } - - CTM tempctm = new CTM(containingIPPosition, containingBPPosition); - ctm = tempctm.multiply(ctm); - - //Adjust for spaces (from margin or indirectly by start-indent etc. - x += bv.getSpaceStart() / 1000f; - currentIPPosition += bv.getSpaceStart(); - - y += bv.getSpaceBefore() / 1000f; - currentBPPosition += bv.getSpaceBefore(); - - float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f; - float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f; - - drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight); - - //Now adjust for border/padding - currentIPPosition += borderPaddingStart; - currentBPPosition += borderPaddingBefore; - - Rectangle2D clippingRect = null; - if (bv.getClip()) { - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - } - - startVParea(ctm, clippingRect); - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - endVParea(); - - if (breakOutList != null) { - restoreStateStackAfterBreakOut(breakOutList); - } - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - } else { - - currentBPPosition += bv.getSpaceBefore(); - - //borders and background in the old coordinate system - handleBlockTraits(bv); - - //Advance to start of content area - currentIPPosition += bv.getStartIndent(); - - CTM tempctm = new CTM(containingIPPosition, currentBPPosition); - ctm = tempctm.multiply(ctm); - - //Now adjust for border/padding - currentBPPosition += borderPaddingBefore; - - Rectangle2D clippingRect = null; - if (bv.getClip()) { - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - } - - startVParea(ctm, clippingRect); - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - endVParea(); - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - - currentBPPosition += (int)(bv.getAllocBPD()); - } - //currentFontName = saveFontName; - } - - private List breakOutOfStateStack() { - log.debug("Block.FIXED --> break out"); - List breakOutList = new java.util.ArrayList(); - while (!this.graphicContextStack.empty()) { - breakOutList.add(0, this.graphicContext); - restoreGraphicsState(); - } - return breakOutList; - } - - private void restoreStateStackAfterBreakOut(List breakOutList) { - log.debug("Block.FIXED --> restoring context after break-out"); - for (int i = 0, c = breakOutList.size(); i < c; i++) { - saveGraphicsState(); - this.graphicContext = (GraphicContext)breakOutList.get(i); - } - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D) - */ - public void renderImage(Image image, Rectangle2D pos) { - drawImage(image.getURL(), pos, image.getForeignAttributes()); - } - - /** - * Draw an image at the indicated location. - * @param url the URI/URL of the image - * @param pos the position of the image - * @param foreignAttributes an optional Map with foreign attributes, may be null - */ - protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { - url = ImageFactory.getURL(url); - ImageFactory fact = userAgent.getFactory().getImageFactory(); - FopImage fopimage = fact.getImage(url, userAgent); - if (fopimage == null) { - return; - } - if (!fopimage.load(FopImage.DIMENSIONS)) { - return; - } - String mime = fopimage.getMimeType(); - if ("text/xml".equals(mime)) { - if (!fopimage.load(FopImage.ORIGINAL_DATA)) { - return; - } - Document doc = ((XMLImage) fopimage).getDocument(); - String ns = ((XMLImage) fopimage).getNameSpace(); - - renderDocument(doc, ns, pos, foreignAttributes); - } else if ("image/svg+xml".equals(mime)) { - if (!fopimage.load(FopImage.ORIGINAL_DATA)) { - return; - } - Document doc = ((XMLImage) fopimage).getDocument(); - String ns = ((XMLImage) fopimage).getNameSpace(); - - renderDocument(doc, ns, pos, foreignAttributes); - } else if (fopimage instanceof EPSImage) { - log.warn("EPS images are not supported by this renderer"); - } else { - if (!fopimage.load(FopImage.BITMAP)) { - log.error("Bitmap image could not be processed: " + fopimage); - return; - } - byte[] imgmap = fopimage.getBitmaps(); - - ColorModel cm = new ComponentColorModel( - ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), - new int[] {8, 8, 8}, - false, false, - ColorModel.OPAQUE, DataBuffer.TYPE_BYTE); - int imgw = fopimage.getWidth(); - int imgh = fopimage.getHeight(); - SampleModel sampleModel = new PixelInterleavedSampleModel( - DataBuffer.TYPE_BYTE, imgw, imgh, 3, imgw * 3, new int[] {0, 1, 2}); - DataBuffer dbuf = new DataBufferByte(imgmap, imgw * imgh * 3); - - WritableRaster raster = Raster.createWritableRaster(sampleModel, - dbuf, null); - - // Combine the color model and raster into a buffered image - RenderedImage img = new BufferedImage(cm, raster, false, null); - - try { - setCursorPos(this.currentIPPosition + (int)pos.getX(), - this.currentBPPosition + (int)pos.getY()); - gen.paintBitmap(img, - new Dimension((int)pos.getWidth(), (int)pos.getHeight()), - false); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - } - - /** - * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D) - */ - public void renderForeignObject(ForeignObject fo, Rectangle2D pos) { - Document doc = fo.getDocument(); - String ns = fo.getNameSpace(); - renderDocument(doc, ns, pos, fo.getForeignAttributes()); - } - - /** - * Common method to render the background and borders for any inline area. - * The all borders and padding are drawn outside the specified area. - * @param area the inline area for which the background, border and padding is to be - * rendered - * @todo Copied from AbstractPathOrientedRenderer - */ - protected void renderInlineAreaBackAndBorders(InlineArea area) { - float x = currentIPPosition / 1000f; - float y = (currentBPPosition + area.getOffset()) / 1000f; - float width = area.getIPD() / 1000f; - float height = area.getBPD() / 1000f; - float borderPaddingStart = area.getBorderAndPaddingWidthStart() / 1000f; - float borderPaddingBefore = area.getBorderAndPaddingWidthBefore() / 1000f; - float bpwidth = borderPaddingStart - + (area.getBorderAndPaddingWidthEnd() / 1000f); - float bpheight = borderPaddingBefore - + (area.getBorderAndPaddingWidthAfter() / 1000f); - - if (height != 0.0f || bpheight != 0.0f && bpwidth != 0.0f) { - drawBackAndBorders(area, x, y - borderPaddingBefore - , width + bpwidth - , height + bpheight); - } - } - - /** - * Draw the background and borders. This draws the background and border - * traits for an area given the position. - * - * @param area the area whose traits are used - * @param startx the start x position - * @param starty the start y position - * @param width the width of the area - * @param height the height of the area - */ - protected void drawBackAndBorders(Area area, float startx, float starty, - float width, float height) { - BorderProps bpsBefore = (BorderProps) area.getTrait(Trait.BORDER_BEFORE); - BorderProps bpsAfter = (BorderProps) area.getTrait(Trait.BORDER_AFTER); - BorderProps bpsStart = (BorderProps) area.getTrait(Trait.BORDER_START); - BorderProps bpsEnd = (BorderProps) area.getTrait(Trait.BORDER_END); - - // draw background - Trait.Background back; - back = (Trait.Background) area.getTrait(Trait.BACKGROUND); - if (back != null) { - - // Calculate padding rectangle - float sx = startx; - float sy = starty; - float paddRectWidth = width; - float paddRectHeight = height; - - if (bpsStart != null) { - sx += bpsStart.width / 1000f; - paddRectWidth -= bpsStart.width / 1000f; - } - if (bpsBefore != null) { - sy += bpsBefore.width / 1000f; - paddRectHeight -= bpsBefore.width / 1000f; - } - if (bpsEnd != null) { - paddRectWidth -= bpsEnd.width / 1000f; - } - if (bpsAfter != null) { - paddRectHeight -= bpsAfter.width / 1000f; - } - - if (back.getColor() != null) { - updateFillColor(back.getColor()); - fillRect(sx, sy, paddRectWidth, paddRectHeight); - } - - // background image - if (back.getFopImage() != null) { - FopImage fopimage = back.getFopImage(); - if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) { - saveGraphicsState(); - clipRect(sx, sy, paddRectWidth, paddRectHeight); - int horzCount = (int) ((paddRectWidth * 1000 / fopimage - .getIntrinsicWidth()) + 1.0f); - int vertCount = (int) ((paddRectHeight * 1000 / fopimage - .getIntrinsicHeight()) + 1.0f); - if (back.getRepeat() == EN_NOREPEAT) { - horzCount = 1; - vertCount = 1; - } else if (back.getRepeat() == EN_REPEATX) { - vertCount = 1; - } else if (back.getRepeat() == EN_REPEATY) { - horzCount = 1; - } - // change from points to millipoints - sx *= 1000; - sy *= 1000; - if (horzCount == 1) { - sx += back.getHoriz(); - } - if (vertCount == 1) { - sy += back.getVertical(); - } - for (int x = 0; x < horzCount; x++) { - for (int y = 0; y < vertCount; y++) { - // place once - Rectangle2D pos; - // Image positions are relative to the currentIP/BP - pos = new Rectangle2D.Float( - sx - currentIPPosition - + (x * fopimage.getIntrinsicWidth()), - sy - currentBPPosition - + (y * fopimage.getIntrinsicHeight()), - fopimage.getIntrinsicWidth(), - fopimage.getIntrinsicHeight()); - drawImage(back.getURL(), pos, null); - } - } - restoreGraphicsState(); - } else { - log.warn( - "Can't find background image: " + back.getURL()); - } - } - } - - Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height); - drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); - } - - /** - * Draws borders. - * @param borderRect the border rectangle - * @param bpsBefore the border specification on the before side - * @param bpsAfter the border specification on the after side - * @param bpsStart the border specification on the start side - * @param bpsEnd the border specification on the end side - */ - protected void drawBorders(Rectangle2D.Float borderRect, - final BorderProps bpsBefore, final BorderProps bpsAfter, - final BorderProps bpsStart, final BorderProps bpsEnd) { - if (bpsBefore == null && bpsAfter == null && bpsStart == null && bpsEnd == null) { - return; //no borders to paint - } - if (qualityBeforeSpeed) { - drawQualityBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); - } else { - drawFastBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); - } - } - - /** - * Draws borders. Borders are drawn as shaded rectangles with no clipping. - * @param borderRect the border rectangle - * @param bpsBefore the border specification on the before side - * @param bpsAfter the border specification on the after side - * @param bpsStart the border specification on the start side - * @param bpsEnd the border specification on the end side - */ - protected void drawFastBorders(Rectangle2D.Float borderRect, - final BorderProps bpsBefore, final BorderProps bpsAfter, - final BorderProps bpsStart, final BorderProps bpsEnd) { - float startx = borderRect.x; - float starty = borderRect.y; - float width = borderRect.width; - float height = borderRect.height; - if (bpsBefore != null) { - float borderWidth = bpsBefore.width / 1000f; - updateFillColor(bpsBefore.color); - fillRect(startx, starty, width, borderWidth); - } - if (bpsAfter != null) { - float borderWidth = bpsAfter.width / 1000f; - updateFillColor(bpsAfter.color); - fillRect(startx, (starty + height - borderWidth), - width, borderWidth); - } - if (bpsStart != null) { - float borderWidth = bpsStart.width / 1000f; - updateFillColor(bpsStart.color); - fillRect(startx, starty, borderWidth, height); - } - if (bpsEnd != null) { - float borderWidth = bpsEnd.width / 1000f; - updateFillColor(bpsEnd.color); - fillRect((startx + width - borderWidth), starty, borderWidth, height); - } - } - - /** - * Draws borders. Borders are drawn in-memory and painted as a bitmap. - * @param borderRect the border rectangle - * @param bpsBefore the border specification on the before side - * @param bpsAfter the border specification on the after side - * @param bpsStart the border specification on the start side - * @param bpsEnd the border specification on the end side - */ - protected void drawQualityBorders(Rectangle2D.Float borderRect, - final BorderProps bpsBefore, final BorderProps bpsAfter, - final BorderProps bpsStart, final BorderProps bpsEnd) { - Graphics2DAdapter g2a = getGraphics2DAdapter(); - final Rectangle.Float effBorderRect = new Rectangle2D.Float( - borderRect.x - (currentIPPosition / 1000f), - borderRect.y - (currentBPPosition / 1000f), - borderRect.width, - borderRect.height); - final Rectangle paintRect = new Rectangle( - (int)Math.round(borderRect.x * 1000f), - (int)Math.round(borderRect.y * 1000f), - (int)Math.floor(borderRect.width * 1000f) + 1, - (int)Math.floor(borderRect.height * 1000f) + 1); - //Add one pixel wide safety margin around the paint area - int pixelWidth = (int)Math.round(UnitConv.in2mpt(1) / userAgent.getTargetResolution()); - final int xoffset = (int)Math.round(-effBorderRect.x * 1000f) + pixelWidth; - final int yoffset = pixelWidth; - paintRect.x += xoffset; - paintRect.y += yoffset; - paintRect.width += 2 * pixelWidth; - paintRect.height += 2 * pixelWidth; - - RendererContext rc = createRendererContext(paintRect.x, paintRect.y, - paintRect.width, paintRect.height, null); - Map atts = new java.util.HashMap(); - atts.put(CONV_MODE, "bitmap"); - atts.put(SRC_TRANSPARENCY, "true"); - rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); - - Graphics2DImagePainter painter = new Graphics2DImagePainter() { - - public void paint(Graphics2D g2d, Rectangle2D area) { - g2d.translate(xoffset, yoffset); - g2d.scale(1000, 1000); - float startx = effBorderRect.x; - float starty = effBorderRect.y; - float width = effBorderRect.width; - float height = effBorderRect.height; - boolean[] b = new boolean[] { - (bpsBefore != null), (bpsEnd != null), - (bpsAfter != null), (bpsStart != null)}; - if (!b[0] && !b[1] && !b[2] && !b[3]) { - return; - } - float[] bw = new float[] { - (b[0] ? bpsBefore.width / 1000f : 0.0f), - (b[1] ? bpsEnd.width / 1000f : 0.0f), - (b[2] ? bpsAfter.width / 1000f : 0.0f), - (b[3] ? bpsStart.width / 1000f : 0.0f)}; - float[] clipw = new float[] { - BorderProps.getClippedWidth(bpsBefore) / 1000f, - BorderProps.getClippedWidth(bpsEnd) / 1000f, - BorderProps.getClippedWidth(bpsAfter) / 1000f, - BorderProps.getClippedWidth(bpsStart) / 1000f}; - starty += clipw[0]; - height -= clipw[0]; - height -= clipw[2]; - startx += clipw[3]; - width -= clipw[3]; - width -= clipw[1]; - - boolean[] slant = new boolean[] { - (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])}; - if (bpsBefore != null) { - //endTextObject(); - - float sx1 = startx; - float sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1); - float ex1 = startx + width; - float ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1); - float outery = starty - clipw[0]; - float clipy = outery + clipw[0]; - float innery = outery + bw[0]; - - //saveGraphicsState(); - Graphics2D g = (Graphics2D)g2d.create(); - moveTo(sx1, clipy); - float sx1a = sx1; - float ex1a = ex1; - if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipw[3]; - } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipw[1]; - } - lineTo(sx1a, outery); - lineTo(ex1a, outery); - } - lineTo(ex1, clipy); - lineTo(ex2, innery); - lineTo(sx2, innery); - closePath(); - //clip(); - g.clip(currentPath); - currentPath = null; - Rectangle2D.Float lineRect = new Rectangle2D.Float( - sx1a, outery, ex1a - sx1a, innery - outery); - Java2DRenderer.drawBorderLine(lineRect, true, true, - bpsBefore.style, bpsBefore.color, g); - //restoreGraphicsState(); - } - if (bpsEnd != null) { - //endTextObject(); - - float sy1 = starty; - float sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1); - float ey1 = starty + height; - float ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1); - float outerx = startx + width + clipw[1]; - float clipx = outerx - clipw[1]; - float innerx = outerx - bw[1]; - - //saveGraphicsState(); - Graphics2D g = (Graphics2D)g2d.create(); - moveTo(clipx, sy1); - float sy1a = sy1; - float ey1a = ey1; - if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - sy1a -= clipw[0]; - } - if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - ey1a += clipw[2]; - } - lineTo(outerx, sy1a); - lineTo(outerx, ey1a); - } - lineTo(clipx, ey1); - lineTo(innerx, ey2); - lineTo(innerx, sy2); - closePath(); - //clip(); - g.setClip(currentPath); - currentPath = null; - Rectangle2D.Float lineRect = new Rectangle2D.Float( - innerx, sy1a, outerx - innerx, ey1a - sy1a); - Java2DRenderer.drawBorderLine(lineRect, false, false, - bpsEnd.style, bpsEnd.color, g); - //restoreGraphicsState(); - } - if (bpsAfter != null) { - //endTextObject(); - - float sx1 = startx; - float sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1); - float ex1 = startx + width; - float ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1); - float outery = starty + height + clipw[2]; - float clipy = outery - clipw[2]; - float innery = outery - bw[2]; - - //saveGraphicsState(); - Graphics2D g = (Graphics2D)g2d.create(); - moveTo(ex1, clipy); - float sx1a = sx1; - float ex1a = ex1; - if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipw[3]; - } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipw[1]; - } - lineTo(ex1a, outery); - lineTo(sx1a, outery); - } - lineTo(sx1, clipy); - lineTo(sx2, innery); - lineTo(ex2, innery); - closePath(); - //clip(); - g.setClip(currentPath); - currentPath = null; - Rectangle2D.Float lineRect = new Rectangle2D.Float( - sx1a, innery, ex1a - sx1a, outery - innery); - Java2DRenderer.drawBorderLine(lineRect, true, false, - bpsAfter.style, bpsAfter.color, g); - //restoreGraphicsState(); - } - if (bpsStart != null) { - //endTextObject(); - - float sy1 = starty; - float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1); - float ey1 = sy1 + height; - float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1); - float outerx = startx - clipw[3]; - float clipx = outerx + clipw[3]; - float innerx = outerx + bw[3]; - - //saveGraphicsState(); - Graphics2D g = (Graphics2D)g2d.create(); - moveTo(clipx, ey1); - float sy1a = sy1; - float ey1a = ey1; - if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - sy1a -= clipw[0]; - } - if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - ey1a += clipw[2]; - } - lineTo(outerx, ey1a); - lineTo(outerx, sy1a); - } - lineTo(clipx, sy1); - lineTo(innerx, sy2); - lineTo(innerx, ey2); - closePath(); - //clip(); - g.setClip(currentPath); - currentPath = null; - Rectangle2D.Float lineRect = new Rectangle2D.Float( - outerx, sy1a, innerx - outerx, ey1a - sy1a); - Java2DRenderer.drawBorderLine(lineRect, false, false, - bpsStart.style, bpsStart.color, g); - //restoreGraphicsState(); - } - } - - public Dimension getImageSize() { - return paintRect.getSize(); - } - - }; - try { - g2a.paintImage(painter, rc, - paintRect.x - xoffset, paintRect.y, paintRect.width, paintRect.height); - } catch (IOException ioe) { - handleIOTrouble(ioe); - } - } - - - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java b/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java deleted file mode 100644 index ae5646db8..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.pcl; - -import org.apache.fop.fo.extensions.ExtensionElementMapping; -import org.apache.fop.render.RendererContext; -import org.apache.fop.util.QName; - -/** - * Wrapper on the RendererContext to access the information structure for drawing - * the XML document. - */ -public class PCLRendererContext extends RendererContext.RendererContextWrapper { - - /** - * Wrap the render context to allow easier access to its values. - * - * @param context the renderer context - * @return the PCL-specific renderer context wrapper - */ - public static PCLRendererContext wrapRendererContext(RendererContext context) { - PCLRendererContext pcli = new PCLRendererContext(context); - return pcli; - } - - /** - * Main constructor - * @param context the RendererContent instance - */ - public PCLRendererContext(RendererContext context) { - super(context); - } - - /** @return true if the SVG image should be rendered as a bitmap */ - public boolean paintAsBitmap() { - QName qName = new QName(ExtensionElementMapping.URI, null, "conversion-mode"); - return getForeignAttributes() != null - && "bitmap".equalsIgnoreCase((String)getForeignAttributes().get(qName)); - } - - /** @return true if clipping is disabled inside the PCLGraphics2D. */ - public boolean isClippingDisabled() { - QName qName = new QName(ExtensionElementMapping.URI, null, "disable-clipping"); - return getForeignAttributes() != null - && "true".equalsIgnoreCase((String)getForeignAttributes().get(qName)); - } - - public boolean isSourceTransparency() { - QName qName = new QName(ExtensionElementMapping.URI, null, "source-transparency"); - return getForeignAttributes() != null - && "true".equalsIgnoreCase((String)getForeignAttributes().get(qName)); - } - - -} \ No newline at end of file diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRendererMaker.java b/src/sandbox/org/apache/fop/render/pcl/PCLRendererMaker.java deleted file mode 100644 index 2e39d355a..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLRendererMaker.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2005 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.pcl; - -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.apps.MimeConstants; -import org.apache.fop.render.AbstractRendererMaker; -import org.apache.fop.render.Renderer; - -/** - * RendererMaker for the PCL Renderer. - */ -public class PCLRendererMaker extends AbstractRendererMaker { - - private static final String[] MIMES = new String[] { - MimeConstants.MIME_PCL, - MimeConstants.MIME_PCL_ALT}; - - - /**@see org.apache.fop.render.AbstractRendererMaker */ - public Renderer makeRenderer(FOUserAgent ua) { - return new PCLRenderer(); - } - - /** @see org.apache.fop.render.AbstractRendererMaker#needsOutputStream() */ - public boolean needsOutputStream() { - return true; - } - - /** @see org.apache.fop.render.AbstractRendererMaker#getSupportedMimeTypes() */ - public String[] getSupportedMimeTypes() { - return MIMES; - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java b/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java deleted file mode 100644 index 7c1a45a0d..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.pcl; - -// FOP -import org.apache.fop.render.AbstractGenericSVGHandler; -import org.apache.fop.render.Renderer; - -/** - * PCL XML handler for SVG. Uses Apache Batik for SVG processing. - * This handler handles XML for foreign objects when rendering to HP GL/2. - * It renders SVG to HP GL/2 using the PCLGraphics2D. - * @see PCLGraphics2DAdapter - */ -public class PCLSVGHandler extends AbstractGenericSVGHandler { - - /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */ - public boolean supportsRenderer(Renderer renderer) { - return (renderer instanceof PCLRenderer); - } - -} - diff --git a/src/sandbox/org/apache/fop/render/pcl/extensions/PCLElementMapping.java b/src/sandbox/org/apache/fop/render/pcl/extensions/PCLElementMapping.java deleted file mode 100644 index b25ab9321..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/extensions/PCLElementMapping.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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: AFPElementMapping.java 397875 2006-04-28 11:58:33Z manuel $ */ - -package org.apache.fop.render.pcl.extensions; - -import java.util.HashMap; - -import org.apache.fop.fo.ElementMapping; - -/** - * PCL-specific extensions for Apache FOP. - */ -public class PCLElementMapping extends ElementMapping { - - /** The namespace used for PCL extensions */ - public static final String NAMESPACE = "http://xmlgraphics.apache.org/fop/extensions/pcl"; - - /** The usual namespace prefix used for PCL extensions */ - public static final String NAMESPACE_PREFIX = "pcl"; - - /** Main constructor */ - public PCLElementMapping() { - this.namespaceURI = NAMESPACE; - } - - /** @see org.apache.fop.fo.ElementMapping#initialize() */ - protected void initialize() { - - if (foObjs == null) { - foObjs = new HashMap(); - //No extension elements, yet, only attributes - } - - } - -} diff --git a/src/sandbox/org/apache/fop/render/pcl/package.html b/src/sandbox/org/apache/fop/render/pcl/package.html deleted file mode 100644 index ba47bd289..000000000 --- a/src/sandbox/org/apache/fop/render/pcl/package.html +++ /dev/null @@ -1,22 +0,0 @@ - - - -org.apache.fop.render.pcl Package - -

PCL Renderer (Supports PCL5 and HP GL/2)

- - \ No newline at end of file