From 54bd1f47b3c90f6110d47d8a400602a9e4844b79 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 28 Apr 2009 16:10:08 +0000 Subject: Added setting to enable dithered painting of filled rectangles in AFP and PCL. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@769445 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/trunk/output.xml | 14 ++ src/java/org/apache/fop/afp/AFPDataObjectInfo.java | 22 +++ .../fop/afp/AFPDitheredRectanglePainter.java | 112 +++++++++++++++ .../org/apache/fop/afp/AbstractAFPPainter.java | 5 +- src/java/org/apache/fop/afp/modca/ImageObject.java | 7 +- .../afp/modca/triplets/MappingOptionTriplet.java | 11 +- .../org/apache/fop/render/afp/AFPCustomizable.java | 6 + .../apache/fop/render/afp/AFPDocumentHandler.java | 21 +++ src/java/org/apache/fop/render/afp/AFPPainter.java | 12 +- .../org/apache/fop/render/afp/AFPRenderer.java | 31 ++++- .../fop/render/afp/AFPRendererConfigurator.java | 6 + .../org/apache/fop/render/afp/AFPShadingMode.java | 74 ++++++++++ .../org/apache/fop/render/pcl/PCLGenerator.java | 65 +-------- .../org/apache/fop/util/bitmap/DitherUtil.java | 153 +++++++++++++++++++++ status.xml | 3 + 15 files changed, 465 insertions(+), 77 deletions(-) create mode 100644 src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java create mode 100644 src/java/org/apache/fop/render/afp/AFPShadingMode.java create mode 100644 src/java/org/apache/fop/util/bitmap/DitherUtil.java diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index c2548b390..135d56516 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -679,6 +679,20 @@ out = proc.getOutputStream();]]> into the datastream using an object container rather than being converted into an IOCA FS45 image. Support for native image formats (e.g. JPEG, GIF) is not always available on printer implementations so by default this configuration option is set to "false".

+ +
+ Shading +

+ By default, filled rectangles are painted using their given color using a PTOCA I-axis rule + (DIR). But not all environments handle these colors correctly. That's why a setting is + supported that paints the rectangles using an ordered dither pattern (bi-level) with + an inline IOCA FS10 image that is used together with the "replicate and trim" mapping. + The optional "shading" element can be used to control the shading mode. Its default value + is "color". To enable the dithered mode, use "dithered". Example: +

+ dithered +]]>
Resource Group File diff --git a/src/java/org/apache/fop/afp/AFPDataObjectInfo.java b/src/java/org/apache/fop/afp/AFPDataObjectInfo.java index 011118683..82e51520d 100644 --- a/src/java/org/apache/fop/afp/AFPDataObjectInfo.java +++ b/src/java/org/apache/fop/afp/AFPDataObjectInfo.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.modca.Registry; +import org.apache.fop.afp.modca.triplets.MappingOptionTriplet; /** * A list of parameters associated with an AFP data objects @@ -57,6 +58,9 @@ public class AFPDataObjectInfo { /** controls whether to create a page segment or a simple object */ private boolean createPageSegment; + /** controls the mapping of the image data into the image area */ + private byte mappingOption = MappingOptionTriplet.SCALE_TO_FILL; + /** * Default constructor */ @@ -253,6 +257,23 @@ public class AFPDataObjectInfo { return this.createPageSegment; } + /** + * Sets the way an image is mapped into its target area. + * @param mappingOption the mapping option (Valid values: see Mapping Option Triplet) + */ + public void setMappingOption(byte mappingOption) { + this.mappingOption = mappingOption; + } + + /** + * Returns the way an image is mapped into its target area. By default, this is "scale to fill" + * behavior. + * @return the mapping option value from the Mapping Option Triplet + */ + public byte getMappingOption() { + return mappingOption; + } + /** {@inheritDoc} */ public String toString() { return "AFPDataObjectInfo{" @@ -264,4 +285,5 @@ public class AFPDataObjectInfo { + (objectAreaInfo != null ? ", objectAreaInfo=" + objectAreaInfo : "") + (resourceInfo != null ? ", resourceInfo=" + resourceInfo : ""); } + } diff --git a/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java b/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java new file mode 100644 index 000000000..3d517ae6a --- /dev/null +++ b/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.afp; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.ImageSize; +import org.apache.xmlgraphics.util.MimeConstants; + +import org.apache.fop.afp.modca.triplets.MappingOptionTriplet; +import org.apache.fop.util.bitmap.DitherUtil; + + +/** + * A painter of rectangles in AFP + */ +public class AFPDitheredRectanglePainter extends AbstractAFPPainter { + + private AFPResourceManager resourceManager; + + /** + * Main constructor + * + * @param paintingState the AFP painting state + * @param dataStream the AFP datastream + * @param resourceManager the resource manager + */ + public AFPDitheredRectanglePainter(AFPPaintingState paintingState, DataStream dataStream, + AFPResourceManager resourceManager) { + super(paintingState, dataStream); + this.resourceManager = resourceManager; + } + + /** {@inheritDoc} */ + public void paint(PaintingInfo paintInfo) throws IOException { + RectanglePaintingInfo rectanglePaintInfo = (RectanglePaintingInfo)paintInfo; + + int ditherMatrix = DitherUtil.DITHER_MATRIX_8X8; + Dimension ditherSize = new Dimension(ditherMatrix, ditherMatrix); + + //Prepare an FS10 bi-level image + AFPImageObjectInfo imageObjectInfo = new AFPImageObjectInfo(); + imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS10); + //imageObjectInfo.setCreatePageSegment(true); + imageObjectInfo.getResourceInfo().setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE)); + imageObjectInfo.getResourceInfo().setImageDimension(ditherSize); + imageObjectInfo.setBitsPerPixel(1); + imageObjectInfo.setColor(false); + //Note: the following may not be supported by older implementations + imageObjectInfo.setMappingOption(MappingOptionTriplet.REPLICATE_AND_TRIM); + + //Dither image size + int resolution = paintingState.getResolution(); + ImageSize ditherBitmapSize = new ImageSize( + ditherSize.width, ditherSize.height, resolution); + imageObjectInfo.setDataHeightRes((int)Math.round( + ditherBitmapSize.getDpiHorizontal() * 10)); + imageObjectInfo.setDataWidthRes((int)Math.round( + ditherBitmapSize.getDpiVertical() * 10)); + imageObjectInfo.setDataWidth(ditherSize.width); + imageObjectInfo.setDataHeight(ditherSize.height); + + //Create dither image + Color col = paintingState.getColor(); + byte[] dither = DitherUtil.getBayerDither(ditherMatrix, col, false); + imageObjectInfo.setData(dither); + + //Positioning + AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo(); + int rotation = paintingState.getRotation(); + AffineTransform at = paintingState.getData().getTransform(); + Point2D origin = at.transform(new Point2D.Float( + rectanglePaintInfo.getX() * 1000, + rectanglePaintInfo.getY() * 1000), null); + objectAreaInfo.setX((int)Math.round(origin.getX())); + objectAreaInfo.setY((int)Math.round(origin.getY())); + AFPUnitConverter unitConv = paintingState.getUnitConverter(); + float width = unitConv.pt2units(rectanglePaintInfo.getWidth()); + float height = unitConv.pt2units(rectanglePaintInfo.getHeight()); + objectAreaInfo.setWidth(Math.round(width)); + objectAreaInfo.setHeight(Math.round(height)); + objectAreaInfo.setHeightRes(resolution); + objectAreaInfo.setWidthRes(resolution); + objectAreaInfo.setRotation(rotation); + imageObjectInfo.setObjectAreaInfo(objectAreaInfo); + + //Create rectangle + resourceManager.createObject(imageObjectInfo); + } + +} diff --git a/src/java/org/apache/fop/afp/AbstractAFPPainter.java b/src/java/org/apache/fop/afp/AbstractAFPPainter.java index 576b8bb11..1358f8072 100644 --- a/src/java/org/apache/fop/afp/AbstractAFPPainter.java +++ b/src/java/org/apache/fop/afp/AbstractAFPPainter.java @@ -19,6 +19,8 @@ package org.apache.fop.afp; +import java.io.IOException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,6 +50,7 @@ public abstract class AbstractAFPPainter { * Paints the painting item * * @param paintInfo the painting information + * @throws IOException if an I/O error occurs */ - public abstract void paint(PaintingInfo paintInfo); + public abstract void paint(PaintingInfo paintInfo) throws IOException; } diff --git a/src/java/org/apache/fop/afp/modca/ImageObject.java b/src/java/org/apache/fop/afp/modca/ImageObject.java index bbbc25bea..65802f6ca 100644 --- a/src/java/org/apache/fop/afp/modca/ImageObject.java +++ b/src/java/org/apache/fop/afp/modca/ImageObject.java @@ -28,7 +28,6 @@ import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.AFPImageObjectInfo; import org.apache.fop.afp.Factory; import org.apache.fop.afp.ioca.ImageSegment; -import org.apache.fop.afp.modca.triplets.MappingOptionTriplet; /** * An IOCA Image Data Object @@ -66,10 +65,6 @@ public class ImageObject extends AbstractDataObject { int dataWidth = imageObjectInfo.getDataWidth(); int dataHeight = imageObjectInfo.getDataHeight(); -// AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo(); -// int widthRes = objectAreaInfo.getWidthRes(); -// int heightRes = objectAreaInfo.getHeightRes(); - int dataWidthRes = imageObjectInfo.getDataWidthRes(); int dataHeightRes = imageObjectInfo.getDataWidthRes(); ImageDataDescriptor imageDataDescriptor @@ -79,7 +74,7 @@ public class ImageObject extends AbstractDataObject { } getObjectEnvironmentGroup().setDataDescriptor(imageDataDescriptor); getObjectEnvironmentGroup().setMapImageObject( - new MapImageObject(MappingOptionTriplet.SCALE_TO_FILL)); + new MapImageObject(dataObjectInfo.getMappingOption())); getImageSegment().setImageSize(dataWidth, dataHeight, dataWidthRes, dataHeightRes); } diff --git a/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java index 0d20d0227..2f19eca83 100644 --- a/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java +++ b/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java @@ -50,9 +50,14 @@ public class MappingOptionTriplet extends AbstractTriplet { */ public static final byte CENTER_AND_TRIM = 0x30; -// public static final byte MIGRATION_MAPPING_1 = 0x41; -// public static final byte MIGRATION_MAPPING_2 = 0x42; -// public static final byte MIGRATION_MAPPING_3 = 0x50; + /** Migration mapping option: Image point-to-pel. */ + public static final byte IMAGE_POINT_TO_PEL = 0x41; + + /** Migration mapping option: Image point-to-pel with double dot. */ + public static final byte IMAGE_POINT_TO_PEL_DOUBLE_DOT = 0x42; + + /** Migration mapping option: Replicate and trim. */ + public static final byte REPLICATE_AND_TRIM = 0x50; /** the data object is centred, aspect ratio is not always preserved */ public static final byte SCALE_TO_FILL = 0x60; diff --git a/src/java/org/apache/fop/render/afp/AFPCustomizable.java b/src/java/org/apache/fop/render/afp/AFPCustomizable.java index ff4fb0100..ed1ea443b 100644 --- a/src/java/org/apache/fop/render/afp/AFPCustomizable.java +++ b/src/java/org/apache/fop/render/afp/AFPCustomizable.java @@ -50,6 +50,12 @@ public interface AFPCustomizable { */ void setNativeImagesSupported(boolean nativeImages); + /** + * Sets the shading mode for painting filled rectangles. + * @param shadingMode the shading mode + */ + void setShadingMode(AFPShadingMode shadingMode); + /** * Sets the output/device resolution * diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 8d29145cf..febfb9672 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -25,10 +25,13 @@ import java.awt.geom.AffineTransform; import java.io.IOException; import java.util.Map; +import org.apache.fop.afp.AFPDitheredRectanglePainter; import org.apache.fop.afp.AFPPaintingState; +import org.apache.fop.afp.AFPRectanglePainter; import org.apache.fop.afp.AFPResourceLevelDefaults; import org.apache.fop.afp.AFPResourceManager; import org.apache.fop.afp.AFPUnitConverter; +import org.apache.fop.afp.AbstractAFPPainter; import org.apache.fop.afp.DataStream; import org.apache.fop.afp.fonts.AFPFontCollection; import org.apache.fop.afp.fonts.AFPPageFonts; @@ -76,6 +79,9 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler private int location = LOC_ELSEWHERE; + /** the shading mode for filled rectangles */ + private AFPShadingMode shadingMode = AFPShadingMode.COLOR; + /** * Default constructor. */ @@ -125,6 +131,16 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler return this.resourceManager; } + AbstractAFPPainter createRectanglePainter() { + if (AFPShadingMode.DITHERED.equals(this.shadingMode)) { + return new AFPDitheredRectanglePainter( + getPaintingState(), getDataStream(), getResourceManager()); + } else { + return new AFPRectanglePainter( + getPaintingState(), getDataStream()); + } + } + /** {@inheritDoc} */ public void startDocument() throws IFException { super.startDocument(); @@ -308,6 +324,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler paintingState.setNativeImagesSupported(nativeImages); } + /** {@inheritDoc} */ + public void setShadingMode(AFPShadingMode shadingMode) { + this.shadingMode = shadingMode; + } + /** {@inheritDoc} */ public void setResolution(int resolution) { paintingState.setResolution(resolution); diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index a92be9d6e..e591812f9 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -35,8 +35,8 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.fop.afp.AFPBorderPainter; import org.apache.fop.afp.AFPPaintingState; -import org.apache.fop.afp.AFPRectanglePainter; import org.apache.fop.afp.AFPUnitConverter; +import org.apache.fop.afp.AbstractAFPPainter; import org.apache.fop.afp.BorderPaintingInfo; import org.apache.fop.afp.DataStream; import org.apache.fop.afp.RectanglePaintingInfo; @@ -79,7 +79,7 @@ public class AFPPainter extends AbstractIFPainter { /** the border painter */ private AFPBorderPainterAdapter borderPainter; /** the rectangle painter */ - private AFPRectanglePainter rectanglePainter; + private AbstractAFPPainter rectanglePainter; /** unit converter */ private final AFPUnitConverter unitConv; @@ -94,7 +94,7 @@ public class AFPPainter extends AbstractIFPainter { this.state = IFState.create(); this.borderPainter = new AFPBorderPainterAdapter( new AFPBorderPainter(getPaintingState(), getDataStream())); - this.rectanglePainter = new AFPRectanglePainter(getPaintingState(), getDataStream()); + this.rectanglePainter = documentHandler.createRectanglePainter(); this.unitConv = getPaintingState().getUnitConverter(); } @@ -222,7 +222,11 @@ public class AFPPainter extends AbstractIFPainter { } RectanglePaintingInfo rectanglePaintInfo = new RectanglePaintingInfo( toPoint(rect.x), toPoint(rect.y), toPoint(rect.width), toPoint(rect.height)); - rectanglePainter.paint(rectanglePaintInfo); + try { + rectanglePainter.paint(rectanglePaintInfo); + } catch (IOException ioe) { + throw new IFException("IO error while painting rectangle", ioe); + } } } diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java index 94fd05dc8..5024fa7b6 100644 --- a/src/java/org/apache/fop/render/afp/AFPRenderer.java +++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java @@ -43,6 +43,7 @@ import org.apache.xmlgraphics.ps.ImageEncodingHelper; import org.apache.fop.afp.AFPBorderPainter; import org.apache.fop.afp.AFPDataObjectInfo; +import org.apache.fop.afp.AFPDitheredRectanglePainter; import org.apache.fop.afp.AFPEventProducer; import org.apache.fop.afp.AFPPaintingState; import org.apache.fop.afp.AFPRectanglePainter; @@ -50,6 +51,7 @@ import org.apache.fop.afp.AFPResourceLevelDefaults; import org.apache.fop.afp.AFPResourceManager; import org.apache.fop.afp.AFPTextDataInfo; import org.apache.fop.afp.AFPUnitConverter; +import org.apache.fop.afp.AbstractAFPPainter; import org.apache.fop.afp.BorderPaintingInfo; import org.apache.fop.afp.DataStream; import org.apache.fop.afp.RectanglePaintingInfo; @@ -167,7 +169,10 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust /** the image handler registry */ private final AFPImageHandlerRegistry imageHandlerRegistry; - private AFPRectanglePainter rectanglePainter; + private AbstractAFPPainter rectanglePainter; + + /** the shading mode for filled rectangles */ + private AFPShadingMode shadingMode = AFPShadingMode.COLOR; /** * Constructor for AFPRenderer. @@ -201,11 +206,21 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust this.dataStream = resourceManager.createDataStream(paintingState, outputStream); this.borderPainter = new AFPBorderPainter(paintingState, dataStream); - this.rectanglePainter = new AFPRectanglePainter(paintingState, dataStream); + this.rectanglePainter = createRectanglePainter(); dataStream.startDocument(); } + AbstractAFPPainter createRectanglePainter() { + if (AFPShadingMode.DITHERED.equals(this.shadingMode)) { + return new AFPDitheredRectanglePainter( + this.paintingState, this.dataStream, this.resourceManager); + } else { + return new AFPRectanglePainter( + this.paintingState, this.dataStream); + } + } + /** {@inheritDoc} */ public void stopRenderer() throws IOException { dataStream.endDocument(); @@ -362,7 +377,12 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust /** {@inheritDoc} */ public void fillRect(float x, float y, float width, float height) { RectanglePaintingInfo rectanglePaintInfo = new RectanglePaintingInfo(x, y, width, height); - rectanglePainter.paint(rectanglePaintInfo); + try { + rectanglePainter.paint(rectanglePaintInfo); + } catch (IOException ioe) { + //TODO not ideal, but the AFPRenderer is legacy + throw new RuntimeException("I/O error while painting a filled rectangle", ioe); + } } /** {@inheritDoc} */ @@ -727,6 +747,11 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust paintingState.setNativeImagesSupported(nativeImages); } + /** {@inheritDoc} */ + public void setShadingMode(AFPShadingMode shadingMode) { + this.shadingMode = shadingMode; + } + /** {@inheritDoc} */ public void setResolution(int resolution) { paintingState.setResolution(resolution); diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index fcc1140c7..892018f9b 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -299,6 +299,12 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false); customizable.setNativeImagesSupported(nativeImageSupport); + // shading (filled rectangles) + Configuration shadingCfg = cfg.getChild("shading"); + AFPShadingMode shadingMode = AFPShadingMode.valueOf( + shadingCfg.getValue(AFPShadingMode.COLOR.getName())); + customizable.setShadingMode(shadingMode); + // renderer resolution Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false); if (rendererResolutionCfg != null) { diff --git a/src/java/org/apache/fop/render/afp/AFPShadingMode.java b/src/java/org/apache/fop/render/afp/AFPShadingMode.java new file mode 100644 index 000000000..b45c33a8e --- /dev/null +++ b/src/java/org/apache/fop/render/afp/AFPShadingMode.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.afp; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** Enumeration class for the AFP shading mode. */ +public final class AFPShadingMode implements Serializable { + + private static final long serialVersionUID = 8579867898716480779L; + + /** the color mode (the default) */ + public static final AFPShadingMode COLOR = new AFPShadingMode("COLOR"); + /** the dithered mode */ + public static final AFPShadingMode DITHERED = new AFPShadingMode("DITHERED"); + + private String name; + + /** + * Constructor to add a new named item. + * @param name Name of the item. + */ + private AFPShadingMode(String name) { + this.name = name; + } + + /** @return the name of the enumeration */ + public String getName() { + return this.name; + } + + /** + * Returns the enumeration/singleton object based on its name. + * @param name the name of the enumeration value + * @return the enumeration object + */ + public static AFPShadingMode valueOf(String name) { + if (COLOR.getName().equalsIgnoreCase(name)) { + return COLOR; + } else if (DITHERED.getName().equalsIgnoreCase(name)) { + return DITHERED; + } else { + throw new IllegalArgumentException("Illegal value for enumeration: " + name); + } + } + + private Object readResolve() throws ObjectStreamException { + return valueOf(getName()); + } + + /** {@inheritDoc} */ + public String toString() { + return getClass().getName() + ":" + name; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java index c36d2a66e..2146bb3b8 100644 --- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -48,6 +48,7 @@ import org.apache.xmlgraphics.image.GraphicsUtil; import org.apache.xmlgraphics.util.UnitConv; import org.apache.fop.util.bitmap.BitmapImageUtil; +import org.apache.fop.util.bitmap.DitherUtil; import org.apache.fop.util.bitmap.MonochromeBitmapConverter; /** @@ -65,11 +66,6 @@ public class PCLGenerator { /** 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}; - /** Selects a 4x4 Bayer dither matrix (17 grayscales) */ - public static final int DITHER_MATRIX_4X4 = 4; - /** Selects a 8x8 Bayer dither matrix (65 grayscales) */ - public static final int DITHER_MATRIX_8X8 = 8; - private final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); private final DecimalFormat df2 = new DecimalFormat("0.##", symbols); private final DecimalFormat df4 = new DecimalFormat("0.####", symbols); @@ -390,7 +386,7 @@ public class PCLGenerator { writeCommand("*c" + lineshade + "G"); writeCommand("*c2P"); //Shaded fill } else { - defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4); + defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4); writeCommand("*c" + formatDouble4(w / 100.0) + "h" + formatDouble4(h / 100.0) + "V"); @@ -401,34 +397,6 @@ public class PCLGenerator { setPatternTransparencyMode(true); } - //Bayer dither matrices (4x4 and 8x8 are derived from the 2x2 matrix) - private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1}; - private static final int[] BAYER_D4; - private static final int[] BAYER_D8; - - static { - BAYER_D4 = deriveBayerMatrix(BAYER_D2); - BAYER_D8 = deriveBayerMatrix(BAYER_D4); - } - - private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) { - int xoff = (part & 1) * half; - int yoff = (part & 2) * half * half; - int matrixIndex = yoff + ((idx / half) * half * 2) + (idx % half) + xoff; - dn[matrixIndex] = value; - } - - private static int[] deriveBayerMatrix(int[] d) { - int[] dn = new int[d.length * 4]; - int half = (int)Math.sqrt(d.length); - for (int part = 0; part < 4; part++) { - for (int i = 0, c = d.length; i < c; i++) { - setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]); - } - } - return dn; - } - /** * Generates a user-defined pattern for a dithering pattern matching the grayscale value * of the color given. @@ -453,35 +421,12 @@ public class PCLGenerator { byte[] pattern; if (ditherMatrixSize == 8) { - int gray65 = gray255 * 65 / 255; - - pattern = new byte[BAYER_D8.length / 8]; - - for (int i = 0, c = BAYER_D8.length; i < c; i++) { - boolean dot = !(BAYER_D8[i] < gray65 - 1); - if (dot) { - int byteIdx = i / 8; - pattern[byteIdx] |= 1 << (i % 8); - } - } + pattern = DitherUtil.getBayerDither(DitherUtil.DITHER_MATRIX_8X8, gray255, false); } else { - int gray17 = gray255 * 17 / 255; - //Since a 4x4 pattern did not work, the 4x4 pattern is applied 4 times to an //8x8 pattern. Maybe this could be changed to use an 8x8 bayer dither pattern //instead of the 4x4 one. - pattern = new byte[BAYER_D4.length / 8 * 4]; - - for (int i = 0, c = BAYER_D4.length; i < c; i++) { - boolean dot = !(BAYER_D4[i] < gray17 - 1); - if (dot) { - int byteIdx = i / 4; - pattern[byteIdx] |= 1 << (i % 4); - pattern[byteIdx] |= 1 << ((i % 4) + 4); - pattern[byteIdx + 4] |= 1 << (i % 4); - pattern[byteIdx + 4] |= 1 << ((i % 4) + 4); - } - } + pattern = DitherUtil.getBayerDither(DitherUtil.DITHER_MATRIX_4X4, gray255, true); } data.write(pattern); if ((baout.size() % 2) > 0) { @@ -564,7 +509,7 @@ public class PCLGenerator { if (usePCLShades) { selectCurrentPattern(convertToPCLShade(col), 2); } else { - defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4); + defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4); selectCurrentPattern(32, 4); } } diff --git a/src/java/org/apache/fop/util/bitmap/DitherUtil.java b/src/java/org/apache/fop/util/bitmap/DitherUtil.java new file mode 100644 index 000000000..c61befc9c --- /dev/null +++ b/src/java/org/apache/fop/util/bitmap/DitherUtil.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.bitmap; + +import java.awt.Color; + +/** + * Utility methods for dithering. + */ +public class DitherUtil { + + /** Selects a 2x2 Bayer dither matrix (5 grayscales) */ + public static final int DITHER_MATRIX_2X2 = 2; + /** Selects a 4x4 Bayer dither matrix (17 grayscales) */ + public static final int DITHER_MATRIX_4X4 = 4; + /** Selects a 8x8 Bayer dither matrix (65 grayscales) */ + public static final int DITHER_MATRIX_8X8 = 8; + + //Bayer dither matrices (4x4 and 8x8 are derived from the 2x2 matrix) + private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1}; + private static final int[] BAYER_D4; + private static final int[] BAYER_D8; + + static { + BAYER_D4 = deriveBayerMatrix(BAYER_D2); + BAYER_D8 = deriveBayerMatrix(BAYER_D4); + } + + private static int[] deriveBayerMatrix(int[] d) { + int[] dn = new int[d.length * 4]; + int half = (int)Math.sqrt(d.length); + for (int part = 0; part < 4; part++) { + for (int i = 0, c = d.length; i < c; i++) { + setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]); + } + } + return dn; + } + + private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) { + int xoff = (part & 1) * half; + int yoff = (part & 2) * half * half; + int matrixIndex = yoff + ((idx / half) * half * 2) + (idx % half) + xoff; + dn[matrixIndex] = value; + } + + /** + * Returns the Bayer dither base pattern for a particular matrix size. + * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} + * or {@link #DITHER_MATRIX_8X8}) + * @return the base pattern for the given size + */ + public static int[] getBayerBasePattern(int matrix) { + int[] result = new int[matrix * matrix]; + switch (matrix) { + case DITHER_MATRIX_2X2: + System.arraycopy(BAYER_D2, 0, result, 0, BAYER_D2.length); + break; + case DITHER_MATRIX_4X4: + System.arraycopy(BAYER_D4, 0, result, 0, BAYER_D4.length); + break; + case DITHER_MATRIX_8X8: + System.arraycopy(BAYER_D8, 0, result, 0, BAYER_D8.length); + break; + default: + throw new IllegalArgumentException("Unsupported dither matrix: " + matrix); + } + return result; + } + + /** + * Returns a byte array containing the dither pattern for the given 8-bit gray value. + * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} + * or {@link #DITHER_MATRIX_8X8}) + * @param gray255 the gray value (0-255) + * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8 + * @return the dither pattern + */ + public static byte[] getBayerDither(int matrix, int gray255, boolean doubleMatrix) { + int ditherIndex; + byte[] dither; + int[] bayer; + switch (matrix) { + case DITHER_MATRIX_4X4: + ditherIndex = gray255 * 17 / 255; + bayer = BAYER_D4; + break; + case DITHER_MATRIX_8X8: + ditherIndex = gray255 * 65 / 255; + bayer = BAYER_D8; + break; + default: + throw new IllegalArgumentException("Unsupported dither matrix: " + matrix); + } + if (doubleMatrix) { + if (doubleMatrix && (matrix != DITHER_MATRIX_4X4)) { + throw new IllegalArgumentException("doubleMatrix=true is only allowed for 4x4"); + } + dither = new byte[bayer.length / 8 * 4]; + for (int i = 0, c = bayer.length; i < c; i++) { + boolean dot = !(bayer[i] < ditherIndex - 1); + if (dot) { + int byteIdx = i / 4; + dither[byteIdx] |= 1 << (i % 4); + dither[byteIdx] |= 1 << ((i % 4) + 4); + dither[byteIdx + 4] |= 1 << (i % 4); + dither[byteIdx + 4] |= 1 << ((i % 4) + 4); + } + } + } else { + dither = new byte[bayer.length / 8]; + for (int i = 0, c = bayer.length; i < c; i++) { + boolean dot = !(bayer[i] < ditherIndex - 1); + if (dot) { + int byteIdx = i / 8; + dither[byteIdx] |= 1 << (i % 8); + } + } + } + return dither; + } + + /** + * Returns a byte array containing the dither pattern for the given 8-bit gray value. + * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} + * or {@link #DITHER_MATRIX_8X8}) + * @param col the color + * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8 + * @return the dither pattern + */ + public static byte[] getBayerDither(int matrix, Color col, boolean doubleMatrix) { + float black = BitmapImageUtil.convertToGray(col.getRGB()) / 256f; + return getBayerDither(matrix, Math.round(black * 256), doubleMatrix); + } + +} diff --git a/status.xml b/status.xml index 0450fbbe8..e54410352 100644 --- a/status.xml +++ b/status.xml @@ -58,6 +58,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Added setting to enable dithered painting of filled rectangles in AFP and PCL. + Bugfix: footnotes occurring within the forced height of a table row did not appear on the output -- cgit v1.2.3