aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/documentation/content/xdocs/trunk/output.xml14
-rw-r--r--src/java/org/apache/fop/afp/AFPDataObjectInfo.java22
-rw-r--r--src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java112
-rw-r--r--src/java/org/apache/fop/afp/AbstractAFPPainter.java5
-rw-r--r--src/java/org/apache/fop/afp/modca/ImageObject.java7
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java11
-rw-r--r--src/java/org/apache/fop/render/afp/AFPCustomizable.java6
-rw-r--r--src/java/org/apache/fop/render/afp/AFPDocumentHandler.java21
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java12
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRenderer.java31
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java6
-rw-r--r--src/java/org/apache/fop/render/afp/AFPShadingMode.java74
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLGenerator.java65
-rw-r--r--src/java/org/apache/fop/util/bitmap/DitherUtil.java153
-rw-r--r--status.xml3
15 files changed, 465 insertions, 77 deletions
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
@@ -680,6 +680,20 @@ out = proc.getOutputStream();]]></source>
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".</p>
</section>
+ <section id="afp-shading-config">
+ <title>Shading</title>
+ <p>
+ 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:
+ </p>
+ <source><![CDATA[
+ <shading>dithered</shading>
+]]></source>
+ </section>
<section id="afp-resource-group-file">
<title>Resource Group File</title>
<p>By default the AFP Renderer will place all data resource objects such as images within
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
@@ -51,6 +51,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
*
* @param 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();
@@ -309,6 +325,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
}
/** {@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} */
@@ -728,6 +748,11 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
}
/** {@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.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="JM" type="add">
+ Added setting to enable dithered painting of filled rectangles in AFP and PCL.
+ </action>
<action context="Layout" dev="VH" type="fix">
Bugfix: footnotes occurring within the forced height of a table row did not appear on the
output