git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@769445 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
@@ -679,6 +679,20 @@ out = proc.getOutputStream();]]></source> | |||
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".</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> |
@@ -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 : ""); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} |
@@ -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; |
@@ -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 | |||
* |
@@ -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); |
@@ -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); | |||
} | |||
} | |||
} | |||
@@ -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); |
@@ -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) { |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 |