</p>
<source><![CDATA[
<images mode="b+w" bits-per-pixel="1" dithering-quality="maximum"/>]]></source>
+ <p>
+ By default, JPEG images are rasterized to a bitmap and the bitmap is included in the AFP doc.
+ However it is possible to encode in a lossless way to maintain maximum quality. But due
+ to lack of support for compression schemes like LZW (patent concerns), bitmap data is currently
+ not compressed resulting in large AFP files. Using the "allow-embedding" attribute on jpeg child
+ element allows the user to pass the JPEG as is in the document. The default is set to "false" since
+ there are compatibility concerns as some AFP printers don't support JPEG decoding. Using the
+ "bitmap-encoding-quality" attribute it is possible to enable lossy compression (JPEG baseline
+ DCT). The default is "1.0" which means lossless encoding. Setting a value lower than 1.0, JPEG
+ compression is enabled and the setting is used as the quality setting when encoding bitmap data.
+ Note that this setting does not always have an effect. Bi-level (1 bit) bitmaps are not compressed
+ using JPEG. Example:
+ </p>
+ <source><![CDATA[
+ <images mode="color" cmyk="true">
+ <jpeg allow-embedding="false" bitmap-encoding-quality="0.8"/>
+ </images>]]></source>
</section>
<section id="afp-goca-config">
<title>GOCA (Vector Graphics)</title>
org.apache.fop.render.afp.AFPImageHandlerGraphics2D
org.apache.fop.render.afp.AFPImageHandlerRawStream
org.apache.fop.render.afp.AFPImageHandlerRawCCITTFax
+org.apache.fop.render.afp.AFPImageHandlerRawJPEG
org.apache.fop.render.afp.AFPImageHandlerSVG
case TIFFImage.COMP_FAX_G4_2D:
imageObj.setEncoding(ImageContent.COMPID_G3_MMR);
break;
+ case ImageContent.COMPID_JPEG:
+ imageObj.setEncoding((byte)compression);
+ break;
default:
throw new IllegalStateException(
"Invalid compression scheme: " + compression);
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);
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo(
+ (int) Math.round(origin.getX()),
+ (int) Math.round(origin.getY()),
+ Math.round(width), Math.round(height), resolution, rotation);
imageObjectInfo.setObjectAreaInfo(objectAreaInfo);
//Create rectangle
* @return true if this image uses compression
*/
public boolean hasCompression() {
- return compression > -1;
+ return compression != -1;
}
/**
}
/** {@inheritDoc} */
+ @Override
public String toString() {
return "AFPImageObjectInfo{" + super.toString()
+ ", compression=" + compression
* dimensions and resolutions of data objects.
*/
public class AFPObjectAreaInfo {
- private int x;
- private int y;
- private int width;
- private int height;
+ private final int x;
+ private final int y;
+ private final int width;
+ private final int height;
private int widthRes;
private int heightRes;
- private int rotation = 0;
+ private final int rotation;
/**
- * Sets the x position of the data object
+ * Constructor
*
- * @param x the x position of the data object
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @param resolution the resolution (sets both width and height resolutions)
+ * @param rotation the rotation angle
*/
- public void setX(int x) {
+ public AFPObjectAreaInfo(int x, int y, int width, int height, int resolution, int rotation) {
this.x = x;
- }
-
- /**
- * Sets the y position of the data object
- *
- * @param y the y position of the data object
- */
- public void setY(int y) {
this.y = y;
- }
-
- /**
- * Sets the data object width
- *
- * @param width the width of the data object
- */
- public void setWidth(int width) {
this.width = width;
+ this.height = height;
+ this.rotation = rotation;
+ this.widthRes = resolution;
+ this.heightRes = resolution;
}
/**
- * Sets the data object height
+ * Sets both the width and the height resolutions.
*
- * @param height the height of the data object
+ * @param resolution the resolution
*/
- public void setHeight(int height) {
- this.height = height;
+ public void setResolution(int resolution) {
+ this.widthRes = resolution;
+ this.heightRes = resolution;
}
/**
- * Sets the width resolution
+ * Sets the width resolution.
*
- * @param widthRes the width resolution
+ * @param resolution the resolution
*/
- public void setWidthRes(int widthRes) {
- this.widthRes = widthRes;
+ public void setWidthRes(int resolution) {
+ this.widthRes = resolution;
}
/**
- * Sets the height resolution
+ * Sets the height resolution.
*
- * @param heightRes the height resolution
+ * @param resolution the resolution
*/
- public void setHeightRes(int heightRes) {
- this.heightRes = heightRes;
+ public void setHeightRes(int resolution) {
+ this.heightRes = resolution;
}
/**
/**
* Returns the width resolution of this data object
*
- * @return the width resolution of this data object
+ * @return the resolution of this data object
*/
public int getWidthRes() {
return widthRes;
/**
* Returns the height resolution of this data object
*
- * @return the height resolution of this data object
+ * @return the resolution of this data object
*/
public int getHeightRes() {
return heightRes;
return rotation;
}
- /**
- * Sets the data object rotation
- *
- * @param rotation the data object rotation
- */
- public void setRotation(int rotation) {
- this.rotation = rotation;
- }
-
/** {@inheritDoc} */
public String toString() {
return "x=" + x
- + ", y=" + y
- + ", width=" + width
- + ", height=" + height
- + ", widthRes=" + widthRes
- + ", heightRes=" + heightRes
- + ", rotation=" + rotation;
+ + ", y=" + y
+ + ", width=" + width
+ + ", height=" + height
+ + ", widthRes=" + widthRes
+ + ", heigtRes=" + heightRes
+ + ", rotation=" + rotation;
}
}
/** dithering quality setting (0.0f..1.0f) */
private float ditheringQuality;
+ /** image encoding quality setting (0.0f..1.0f) */
+ private float bitmapEncodingQuality;
+
/** color image handler */
private ColorConverter colorConverter = GrayScaleColorConverter.getInstance();
* format.
*/
private boolean nativeImagesSupported = false;
+
+ private boolean canEmbedJpeg = false;
+
/**
* true if CMYK images (requires IOCA FS45 suppport on the target platform)
* may be generated
return this.nativeImagesSupported;
}
+ /**
+ * Set whether or not JPEG images can be embedded within an AFP document.
+ *
+ * @param canEmbed true if the JPEG image can be embedded
+ */
+ public void setCanEmbedJpeg(boolean canEmbed) {
+ canEmbedJpeg = canEmbed;
+ }
+
+ /**
+ * Returns true if JPEGs can be embedded in an AFP document.
+ *
+ * @return true if JPEG embedding is allowed
+ */
+ public boolean canEmbedJpeg() {
+ return canEmbedJpeg;
+ }
+
/**
* Controls whether CMYK images (IOCA FS45) are enabled. By default, support
* is disabled for wider compatibility. When disabled, any CMYK image is
this.ditheringQuality = quality;
}
+ /**
+ * Gets the image encoding quality setting to use when encoding bitmap images.
+ * @return the encoding quality (a value between 0.0f and 1.0f, 1.0 meaning loss-less)
+ */
+ public float getBitmapEncodingQuality() {
+ return this.bitmapEncodingQuality;
+ }
+
+ /**
+ * Sets the image encoding quality setting to use when encoding bitmap images.
+ * @param quality Defines the desired quality level for the conversion.
+ * Valid values: a value between 0.0f (lowest) and 1.0f (best, loss-less)
+ */
+ public void setBitmapEncodingQuality(float quality) {
+ quality = Math.max(quality, 0.0f);
+ quality = Math.min(quality, 1.0f);
+ this.bitmapEncodingQuality = quality;
+ }
+
/**
* Sets the output/device resolution
*
private void includeObject(AFPDataObjectInfo dataObjectInfo,
String objectName) {
- IncludeObject includeObject
- = dataObjectFactory.createInclude(objectName, dataObjectInfo);
+ IncludeObject includeObject = dataObjectFactory.createInclude(objectName, dataObjectInfo);
dataStream.getCurrentPage().addObject(includeObject);
}
import java.util.List;
+import org.apache.fop.fonts.FontTriplet;
+
/**
* FontInfo contains meta information on fonts
public class AFPFontInfo {
private AFPFont font;
- private List/*<FontTriplet>*/ tripletList;
+ private List<FontTriplet> tripletList;
/**
* Main constructor
* @param afpFont The AFP Font
* @param tripletList List of font triplets to associate with this font
*/
- public AFPFontInfo(AFPFont afpFont, List/*<FontTriplet>*/ tripletList) {
+ public AFPFontInfo(AFPFont afpFont, List<FontTriplet> tripletList) {
this.font = afpFont;
this.tripletList = tripletList;
}
*
* @return List of font triplets
*/
- public List/*<FontTriplet>*/ getFontTriplets() {
+ public List<FontTriplet> getFontTriplets() {
return tripletList;
}
*/
public static final byte COMPID_G3_MMR = (byte)0x82;
+ /** JPEG algorithms (usually baseline DCT). */
+ public static final byte COMPID_JPEG = (byte)0x83;
+
/** the image size parameter */
private ImageSizeParameter imageSizeParameter = null;
private byte ideSize = 1;
/** the image compression */
- private byte compression = (byte)0xC0;
+ private byte compression = (byte)0xC0; //Baseline DCT in case of JPEG compression
/** the image data */
private byte[] data;
* @param color the IDE color model.
* @deprecated use {@link #setIDEStructureParameter(IDEStructureParameter)} instead
*/
+ @Deprecated
public void setImageIDEColorModel(byte color) {
needIDEStructureParameter().setColorModel(color);
}
* @param subtractive true for subtractive mode, false for additive mode
* @deprecated use {@link #setIDEStructureParameter(IDEStructureParameter)} instead
*/
+ @Deprecated
public void setSubtractive(boolean subtractive) {
needIDEStructureParameter().setSubtractive(subtractive);
}
private static final int MAX_DATA_LEN = 65535;
/** {@inheritDoc} */
+ @Override
protected void writeContent(OutputStream os) throws IOException {
if (imageSizeParameter != null) {
imageSizeParameter.writeToStream(os);
}
/** {@inheritDoc} */
+ @Override
protected void writeStart(OutputStream os) throws IOException {
final byte[] startData = new byte[] {
(byte)0x91, // ID
}
/** {@inheritDoc} */
+ @Override
protected void writeEnd(OutputStream os) throws IOException {
final byte[] endData = new byte[] {
(byte)0x93, // ID
(byte)0x95, // ID
0x02, // Length
encoding,
- 0x01, // RECID
+ (byte)(encoding == COMPID_JPEG ? 0xFE : 0x01), // RECID
};
return encodingData;
}
* @return byte[] The data stream.
*/
private byte[] getExternalAlgorithmParameter() {
- if (encoding == (byte)0x83 && compression != 0) {
+ if (encoding == COMPID_JPEG && compression != 0) {
final byte[] extAlgData = new byte[] {
- (byte)0x95, // ID
+ (byte)0x9F, // ID
0x00, // Length
0x10, // ALGTYPE = Compression Algorithm
0x00, // Reserved
- (byte)0x83, // COMPRID = JPEG
+ COMPID_JPEG, // COMPRID = JPEG
0x00, // Reserved
0x00, // Reserved
0x00, // Reserved
- compression, // MARKER
+ compression, // MARKER
0x00, // Reserved
0x00, // Reserved
0x00, // Reserved
int height = objectAreaInfo.getHeight();
int widthRes = objectAreaInfo.getWidthRes();
int heightRes = objectAreaInfo.getHeightRes();
- ObjectAreaDescriptor objectAreaDescriptor
- = factory.createObjectAreaDescriptor(width, height, widthRes, heightRes);
+ ObjectAreaDescriptor objectAreaDescriptor = factory.createObjectAreaDescriptor(width,
+ height, widthRes, heightRes);
getObjectEnvironmentGroup().setObjectAreaDescriptor(objectAreaDescriptor);
// object area position
final int leftEdge = 0;
final int topEdge = 0;
GraphicsDataDescriptor graphicsDataDescriptor = factory.createGraphicsDataDescriptor(
- leftEdge, width, topEdge, height, widthRes, heightRes);
+ leftEdge, width, topEdge, height, widthRes, heightRes);
getObjectEnvironmentGroup().setDataDescriptor(graphicsDataDescriptor);
}
ContainerDataDescriptor containerDataDescriptor
= factory.createContainerDataDescriptor(
- dataWidth, dataHeight, widthRes, heightRes);
+ dataWidth, dataHeight, widthRes, heightRes);
getObjectEnvironmentGroup().setDataDescriptor(containerDataDescriptor);
}
}
package org.apache.fop.afp.modca;
import java.util.Collections;
+import java.util.HashMap;
import org.apache.xmlgraphics.util.MimeConstants;
private static final byte COMPID_TRUETYPE_OPENTYPE_FONT_COLLECTION_RESOURCE_OBJECT = 53;
/** mime type entry mapping */
- private final java.util.Map/*<String, ObjectType>*/ mimeObjectTypeMap
- = Collections.synchronizedMap(
- new java.util.HashMap/*<String, ObjectType>*/());
+ private final java.util.Map<String, ObjectType> mimeObjectTypeMap
+ = Collections.synchronizedMap(new HashMap<String, ObjectType>());
/** singleton instance */
private static Registry instance = null;
* @return the MOD:CA object type
*/
public ObjectType getObjectType(String mimeType) {
- return (ObjectType)mimeObjectTypeMap.get(mimeType);
+ return mimeObjectTypeMap.get(mimeType);
}
/**
/** the logger */
private static Log log = LogFactory.getLog(ImageHandlerRegistry.class);
- private static final Comparator HANDLER_COMPARATOR = new Comparator() {
- public int compare(Object o1, Object o2) {
- ImageHandler h1 = (ImageHandler)o1;
- ImageHandler h2 = (ImageHandler)o2;
+ private static final Comparator<ImageHandler> HANDLER_COMPARATOR
+ = new Comparator<ImageHandler>() {
+ public int compare(ImageHandler o1, ImageHandler o2) {
+ ImageHandler h1 = o1;
+ ImageHandler h2 = o2;
return h1.getPriority() - h2.getPriority();
}
};
/** Map containing image handlers for various {@link Image} subclasses. */
- private Map handlers = new java.util.HashMap();
+ private Map<Class<? extends Image>, ImageHandler> handlers
+ = new java.util.HashMap<Class<? extends Image>, ImageHandler>();
/** List containing the same handlers as above but ordered by priority */
- private List handlerList = new java.util.LinkedList();
+ private List<ImageHandler> handlerList = new java.util.LinkedList<ImageHandler>();
private int handlerRegistrations;
* @param handler the ImageHandler instance
*/
public synchronized void addHandler(ImageHandler handler) {
- Class imageClass = handler.getSupportedImageClass();
+ Class<? extends Image> imageClass = handler.getSupportedImageClass();
//List
this.handlers.put(imageClass, handler);
//Sorted insert (sort by priority)
- ListIterator iter = this.handlerList.listIterator();
+ ListIterator<ImageHandler> iter = this.handlerList.listIterator();
while (iter.hasNext()) {
- ImageHandler h = (ImageHandler)iter.next();
+ ImageHandler h = iter.next();
if (HANDLER_COMPARATOR.compare(handler, h) < 0) {
iter.previous();
break;
* @return the image handler responsible for handling the image or null if none is available
*/
public ImageHandler getHandler(RenderingContext targetContext, Image image) {
- ListIterator iter = this.handlerList.listIterator();
- while (iter.hasNext()) {
- ImageHandler h = (ImageHandler)iter.next();
+ for (ImageHandler h : this.handlerList) {
if (h.isCompatible(targetContext, image)) {
//Return the first handler in the prioritized list that is compatible
return h;
*/
public synchronized ImageFlavor[] getSupportedFlavors(RenderingContext context) {
//Extract all ImageFlavors into a single array
- List flavors = new java.util.ArrayList();
- Iterator iter = this.handlerList.iterator();
- while (iter.hasNext()) {
- ImageHandler handler = (ImageHandler)iter.next();
+ List<ImageFlavor> flavors = new java.util.ArrayList<ImageFlavor>();
+ for (ImageHandler handler : this.handlerList) {
if (handler.isCompatible(context, null)) {
ImageFlavor[] f = handler.getSupportedImageFlavors();
for (int i = 0; i < f.length; i++) {
}
}
}
- return (ImageFlavor[])flavors.toArray(new ImageFlavor[flavors.size()]);
+ return flavors.toArray(new ImageFlavor[flavors.size()]);
}
/**
*/
void setDitheringQuality(float quality);
+ /**
+ * Sets the image encoding quality setting to use when encoding bitmap images.
+ * The default setting is 1.0 which means loss-less encoding. Settings of less than 1.0
+ * allow loss-less encoding schemes like JPEG. The value serves as quality setting for
+ * the encoders in that case.
+ * @param quality Defines the desired quality level.
+ * Valid values: a value between 0.0f (lowest) and 1.0f (best, loss-less)
+ */
+ void setBitmapEncodingQuality(float quality);
+
/**
* Sets the output/device resolution
*
*/
void setResourceLevelDefaults(AFPResourceLevelDefaults defaults);
+ /**
+ * Sets whether or not to JPEG images can be embedded in the AFP document.
+ *
+ * @param canEmbed whether or not to embed JPEG image
+ */
+ void canEmbedJpeg(boolean canEmbed);
+
}
this.paintingState.setDitheringQuality(quality);
}
+ /** {@inheritDoc} */
+ public void setBitmapEncodingQuality(float quality) {
+ this.paintingState.setBitmapEncodingQuality(quality);
+ }
+
/** {@inheritDoc} */
public void setShadingMode(AFPShadingMode shadingMode) {
this.shadingMode = shadingMode;
return pageSegmentMap.get(uri);
}
+ /** {@inheritDoc} */
+ public void canEmbedJpeg(boolean canEmbed) {
+ paintingState.setCanEmbedJpeg(canEmbed);
+ }
+
}
*/
public static AFPObjectAreaInfo createObjectAreaInfo(AFPPaintingState paintingState,
Rectangle targetRect) {
- AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
AFPUnitConverter unitConv = paintingState.getUnitConverter();
int[] coords = unitConv.mpts2units(new float[] {targetRect.x, targetRect.y});
- objectAreaInfo.setX(coords[X]);
- objectAreaInfo.setY(coords[Y]);
int width = Math.round(unitConv.mpt2units(targetRect.width));
- objectAreaInfo.setWidth(width);
int height = Math.round(unitConv.mpt2units(targetRect.height));
- objectAreaInfo.setHeight(height);
int resolution = paintingState.getResolution();
- objectAreaInfo.setHeightRes(resolution);
- objectAreaInfo.setWidthRes(resolution);
-
- objectAreaInfo.setRotation(paintingState.getRotation());
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo(coords[X], coords[Y], width,
+ height, resolution, paintingState.getRotation());
return objectAreaInfo;
}
package org.apache.fop.render.afp;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
ImageFlavor.RAW_CCITTFAX,
};
+ /** logging instance */
+ private final Log log = LogFactory.getLog(AFPImageHandlerRawJPEG.class);
+
/** {@inheritDoc} */
+ @Override
protected void setAdditionalParameters(AFPDataObjectInfo dataObjectInfo,
ImageRawStream image) {
AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)dataObjectInfo;
}
/** {@inheritDoc} */
+ @Override
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ log.debug("Embedding undecoded CCITT data as data container...");
+ super.handleImage(context, image, pos);
+ }
+
+ /** {@inheritDoc} */
+ @Override
protected AFPDataObjectInfo createDataObjectInfo() {
return new AFPImageObjectInfo();
}
--- /dev/null
+/*
+ * 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.awt.Rectangle;
+import java.awt.color.ColorSpace;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.image.loader.impl.JPEGConstants;
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.fop.afp.AFPImageObjectInfo;
+import org.apache.fop.afp.AFPObjectAreaInfo;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceInfo;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.afp.ioca.ImageContent;
+import org.apache.fop.afp.modca.ResourceObject;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * {@link ImageHandler} implementation which handles ImageRawJPEG instances. JPEG data is
+ * embedded directly (not decoded) into IOCA images (FS11 or FS45).
+ */
+public class AFPImageHandlerRawJPEG extends AFPImageHandler implements ImageHandler {
+
+ /** logging instance */
+ private final Log log = LogFactory.getLog(AFPImageHandlerRawJPEG.class);
+
+ private void setDefaultResourceLevel(AFPImageObjectInfo imageObjectInfo,
+ AFPResourceManager resourceManager) {
+ AFPResourceInfo resourceInfo = imageObjectInfo.getResourceInfo();
+ if (!resourceInfo.levelChanged()) {
+ resourceInfo.setLevel(resourceManager.getResourceLevelDefaults()
+ .getDefaultResourceLevel(ResourceObject.TYPE_IMAGE));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AFPDataObjectInfo createDataObjectInfo() {
+ return new AFPImageObjectInfo();
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 150;
+ }
+
+ /** {@inheritDoc} */
+ public Class<?> getSupportedImageClass() {
+ return ImageRawJPEG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return new ImageFlavor[] {ImageFlavor.RAW_JPEG};
+ }
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ AFPRenderingContext afpContext = (AFPRenderingContext)context;
+
+ AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)createDataObjectInfo();
+ AFPPaintingState paintingState = afpContext.getPaintingState();
+
+ // set resource information
+ setResourceInformation(imageObjectInfo,
+ image.getInfo().getOriginalURI(),
+ afpContext.getForeignAttributes());
+ setDefaultResourceLevel(imageObjectInfo, afpContext.getResourceManager());
+
+ // Positioning
+ imageObjectInfo.setObjectAreaInfo(createObjectAreaInfo(paintingState, pos));
+ updateIntrinsicSize(imageObjectInfo, paintingState, image.getSize());
+
+ // Image content
+ ImageRawJPEG jpeg = (ImageRawJPEG)image;
+ imageObjectInfo.setCompression(ImageContent.COMPID_JPEG);
+ ColorSpace cs = jpeg.getColorSpace();
+ switch (cs.getType()) {
+ case ColorSpace.TYPE_GRAY:
+ imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS11);
+ imageObjectInfo.setColor(false);
+ imageObjectInfo.setBitsPerPixel(8);
+ break;
+ case ColorSpace.TYPE_RGB:
+ imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS11);
+ imageObjectInfo.setColor(true);
+ imageObjectInfo.setBitsPerPixel(24);
+ break;
+ case ColorSpace.TYPE_CMYK:
+ imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS45);
+ imageObjectInfo.setColor(true);
+ imageObjectInfo.setBitsPerPixel(32);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Color space of JPEG image not supported: " + cs);
+ }
+
+ boolean included = afpContext.getResourceManager().tryIncludeObject(imageObjectInfo);
+ if (!included) {
+ log.debug("Embedding undecoded JPEG as IOCA image...");
+ InputStream inputStream = jpeg.createInputStream();
+ try {
+ imageObjectInfo.setData(IOUtils.toByteArray(inputStream));
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+
+ // Create image
+ afpContext.getResourceManager().createObject(imageObjectInfo);
+ }
+ }
+
+ private void updateIntrinsicSize(AFPImageObjectInfo imageObjectInfo,
+ AFPPaintingState paintingState, ImageSize targetSize) {
+ //Update image object info
+ imageObjectInfo.setDataHeightRes((int)Math.round(
+ targetSize.getDpiHorizontal() * 10));
+ imageObjectInfo.setDataWidthRes((int)Math.round(
+ targetSize.getDpiVertical() * 10));
+ imageObjectInfo.setDataHeight(targetSize.getHeightPx());
+ imageObjectInfo.setDataWidth(targetSize.getWidthPx());
+
+ // set object area info
+ int resolution = paintingState.getResolution();
+ AFPObjectAreaInfo objectAreaInfo = imageObjectInfo.getObjectAreaInfo();
+ objectAreaInfo.setResolution(resolution);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ if (!(targetContext instanceof AFPRenderingContext)) {
+ return false; //AFP-specific image handler
+ }
+ AFPRenderingContext context = (AFPRenderingContext)targetContext;
+ AFPPaintingState paintingState = context.getPaintingState();
+ if (!paintingState.canEmbedJpeg()) {
+ return false;
+ }
+ if (paintingState.getBitsPerPixel() < 8) {
+ return false; //This would stand in the way of dithering and cause exceptions
+ }
+ if (image == null) {
+ return true; //Don't know the image format, yet
+ }
+ if (image instanceof ImageRawJPEG) {
+ ImageRawJPEG jpeg = (ImageRawJPEG)image;
+ ColorSpace cs = jpeg.getColorSpace();
+ switch (cs.getType()) {
+ case ColorSpace.TYPE_GRAY:
+ case ColorSpace.TYPE_RGB:
+ //ok
+ break;
+ case ColorSpace.TYPE_CMYK:
+ if (!paintingState.isCMYKImagesSupported()) {
+ return false; //CMYK is disabled
+ //Note: you may need to disable this image handler through configuration
+ //if you want to paint a CMYK JPEG on 24bit and less configurations.
+ }
+ break;
+ default:
+ return false; //not supported
+ }
+
+ if (jpeg.getSOFType() != JPEGConstants.SOF0) {
+ return false; //We'll let only baseline DCT through.
+ }
+ return true;
+ }
+ return false;
+ }
+
+}
package org.apache.fop.render.afp;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS;
ImageFlavor.RAW_EPS,
};
+ /** logging instance */
+ private final Log log = LogFactory.getLog(AFPImageHandlerRawJPEG.class);
+
/** {@inheritDoc} */
public int getPriority() {
return 200;
}
/** {@inheritDoc} */
+ @Override
protected AFPDataObjectInfo createDataObjectInfo() {
return new AFPDataObjectInfo();
}
+ /** {@inheritDoc} */
+ @Override
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Embedding undecoded image data (" + image.getInfo().getMimeType()
+ + ") as data container...");
+ }
+ super.handleImage(context, image, pos);
+ }
+
/** {@inheritDoc} */
public boolean isCompatible(RenderingContext targetContext, Image image) {
if (targetContext instanceof AFPRenderingContext) {
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.writer.ImageWriter;
+import org.apache.xmlgraphics.image.writer.ImageWriterParams;
+import org.apache.xmlgraphics.image.writer.ImageWriterRegistry;
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.xmlgraphics.util.MimeConstants;
import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPResourceInfo;
import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.afp.ioca.ImageContent;
import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.render.ImageHandler;
import org.apache.fop.render.RenderingContext;
functionSet = 45; //IOCA FS45 required for CMYK
}
- helper.encode(baos);
+ //Lossy or loss-less?
+ if (!paintingState.canEmbedJpeg()
+ && paintingState.getBitmapEncodingQuality() < 1.0f) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Encoding using baseline DCT (JPEG, q="
+ + paintingState.getBitmapEncodingQuality() + ")...");
+ }
+ encodeToBaselineDCT(renderedImage,
+ paintingState.getBitmapEncodingQuality(),
+ paintingState.getResolution(),
+ baos);
+ imageObjectInfo.setCompression(ImageContent.COMPID_JPEG);
+ } catch (IOException ioe) {
+ //Some JPEG codecs cannot encode CMYK
+ helper.encode(baos);
+ }
+ } else {
+ helper.encode(baos);
+ }
imageData = baos.toByteArray();
}
}
return false;
}
- }
+ private void encodeToBaselineDCT(RenderedImage image,
+ float quality, int resolution, OutputStream out) throws IOException {
+ ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor("image/jpeg");
+ ImageWriterParams params = new ImageWriterParams();
+ params.setJPEGQuality(quality, true);
+ params.setResolution(resolution);
+ writer.writeImage(image, out, params);
+ }
+ }
}
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.List;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
+
import org.apache.fop.afp.AFPResourceLevel;
import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.fonts.AFPFont;
log.error("Mandatory font configuration element '<font-triplet...' is missing");
return null;
}
- for (int j = 0; j < triple.length; j++) {
- int weight = FontUtil.parseCSS2FontWeight(triple[j].getAttribute("weight"));
- FontTriplet triplet = new FontTriplet(triple[j].getAttribute("name"),
- triple[j].getAttribute("style"),
+ for (Configuration config : triple) {
+ int weight = FontUtil.parseCSS2FontWeight(config.getAttribute("weight"));
+ FontTriplet triplet = new FontTriplet(config.getAttribute("name"),
+ config.getAttribute("style"),
weight);
tripletList.add(triplet);
}
if (base14 != null) {
try {
- Class<?> clazz = Class.forName(
- "org.apache.fop.fonts.base14." + base14);
+ Class<? extends Typeface> clazz = Class.forName(
+ "org.apache.fop.fonts.base14." + base14).asSubclass(Typeface.class);
try {
- Typeface tf = (Typeface)clazz.newInstance();
+ Typeface tf = clazz.newInstance();
font.addCharacterSet(sizeMpt,
CharacterSetBuilder.getInstance()
.build(characterset, codepage, encoding, tf));
String base14 = afpFontCfg.getAttribute("base14-font", null);
if (base14 != null) {
try {
- Class<?> clazz = Class.forName("org.apache.fop.fonts.base14."
- + base14);
+ Class<? extends Typeface> clazz = Class.forName("org.apache.fop.fonts.base14."
+ + base14).asSubclass(Typeface.class);
try {
- Typeface tf = (Typeface)clazz.newInstance();
+ Typeface tf = clazz.newInstance();
characterSet = CharacterSetBuilder.getInstance()
.build(characterset, codepage, encoding, tf);
} catch (Exception ie) {
if (log.isDebugEnabled()) {
log.debug("Adding font " + afi.getAFPFont().getFontName());
}
- List/*<FontTriplet>*/ fontTriplets = afi.getFontTriplets();
+ List<FontTriplet> fontTriplets = afi.getFontTriplets();
for (int j = 0; j < fontTriplets.size(); ++j) {
FontTriplet triplet = (FontTriplet) fontTriplets.get(j);
if (log.isDebugEnabled()) {
boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
customizable.setNativeImagesSupported(nativeImageSupport);
+ Configuration jpegConfig = imagesCfg.getChild("jpeg");
+ boolean allowEmbedding = false;
+ float ieq = 1.0f;
+ if (jpegConfig != null) {
+ allowEmbedding = jpegConfig.getAttributeAsBoolean("allow-embedding", false);
+ String bitmapEncodingQuality = jpegConfig.getAttribute("bitmap-encoding-quality", null);
+
+ if (bitmapEncodingQuality != null) {
+ try {
+ ieq = Float.parseFloat(bitmapEncodingQuality);
+ } catch (NumberFormatException nfe) {
+ //ignore and leave the default above
+ }
+ }
+ }
+ customizable.canEmbedJpeg(allowEmbedding);
+ customizable.setBitmapEncodingQuality(ieq);
+
// shading (filled rectangles)
Configuration shadingCfg = cfg.getChild("shading");
AFPShadingMode shadingMode = AFPShadingMode.valueOf(
public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
throws FOPException {
FontManager fontManager = userAgent.getFactory().getFontManager();
- List<FontCollection> fontCollections = new java.util.ArrayList<FontCollection>();
+ List<AFPFontCollection> fontCollections = new ArrayList<AFPFontCollection>();
Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
if (cfg != null) {
private AFPObjectAreaInfo createObjectAreaInfo(AFPPaintingState paintingState,
int x, int y, int width, int height, int resolution) {
// set the data object parameters
- AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
AffineTransform at = paintingState.getData().getTransform();
at.translate(x, y);
- objectAreaInfo.setX((int)Math.round(at.getTranslateX()));
- objectAreaInfo.setY((int)Math.round(at.getTranslateY()));
-
- objectAreaInfo.setWidthRes(resolution);
- objectAreaInfo.setHeightRes(resolution);
-
AFPUnitConverter unitConv = paintingState.getUnitConverter();
- objectAreaInfo.setWidth(Math.round(unitConv.mpt2units(width)));
- objectAreaInfo.setHeight(Math.round(unitConv.mpt2units(height)));
int rotation = paintingState.getRotation();
- objectAreaInfo.setRotation(rotation);
-
+ int objX = (int) Math.round(at.getTranslateX());
+ int objY = (int) Math.round(at.getTranslateY());
+ int objWidth = Math.round(unitConv.mpt2units(width));
+ int objHeight = Math.round(unitConv.mpt2units(height));
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo(objX, objY, objWidth, objHeight,
+ resolution, rotation);
return objectAreaInfo;
}
AFPPaintingState paintingState = afpContext.getPaintingState();
int resolution = paintingState.getResolution();
AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo();
- objectAreaInfo.setWidthRes(resolution);
- objectAreaInfo.setHeightRes(resolution);
+ objectAreaInfo.setResolution(resolution);
// Image content
ImageRawStream imageStream = (ImageRawStream)image;
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Code" dev="PH" type="add" fixes-bug="52089" due-to="JM, Mehdi Houshmand">
+ Allow JPEG images to be embedded in an AFP document as is, without being decoded and
+ encoded.
+ </action>
<action context="Code" dev="PH" type="add" fixes-bug="52010" due-to="Mehdi Houshmand">
Simplification of the build: Reduced code duplication and layout engine tests.
</action>
--- /dev/null
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test case for {@link AFPObjectAreaInfo}.
+ */
+public class AFPObjectAreaInfoTestCase {
+
+ private AFPObjectAreaInfo sut;
+
+ /**
+ * Instantiate the system under test
+ */
+ @Before
+ public void setUp() {
+ sut = new AFPObjectAreaInfo(1, 2, 3, 4, 5, 6);
+ }
+
+ /**
+ * Test the getter functions with arbitrary data.
+ */
+ @Test
+ public void testGetters() {
+ assertEquals(1, sut.getX());
+ assertEquals(2, sut.getY());
+ assertEquals(3, sut.getWidth());
+ assertEquals(4, sut.getHeight());
+ assertEquals(5, sut.getWidthRes());
+ assertEquals(5, sut.getHeightRes());
+ assertEquals(6, sut.getRotation());
+ }
+
+ /**
+ * Test the resolution setters with arbitrary data.
+ */
+ @Test
+ public void testSetters() {
+ assertEquals(5, sut.getWidthRes());
+ assertEquals(5, sut.getHeightRes());
+
+ sut.setResolution(20);
+ assertEquals(20, sut.getWidthRes());
+ assertEquals(20, sut.getHeightRes());
+
+ sut.setHeightRes(10);
+ assertEquals(20, sut.getWidthRes());
+ assertEquals(10, sut.getHeightRes());
+
+ sut.setWidthRes(9);
+ assertEquals(9, sut.getWidthRes());
+ assertEquals(10, sut.getHeightRes());
+ }
+}
--- /dev/null
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test case for {@link AFPPaintingState}.
+ */
+public class AFPPaintingStateTestCase {
+ private AFPPaintingState sut;
+
+ /**
+ * Set up the system under test
+ */
+ @Before
+ public void setUp() {
+ sut = new AFPPaintingState();
+ }
+
+ /**
+ * Test {get,set}BitmapEncodingQuality()
+ */
+ @Test
+ public void testGetSetBitmapEncodingQuality() {
+ sut.setBitmapEncodingQuality(0.5f);
+ assertEquals(0.5f, sut.getBitmapEncodingQuality(), 0.01f);
+
+ sut.setBitmapEncodingQuality(0.9f);
+ assertEquals(0.9f, sut.getBitmapEncodingQuality(), 0.01f);
+ }
+
+ /**
+ * Test {,set}CanEmbedJpeg
+ */
+ public void testGetSetCanEmbedJpeg() {
+ assertEquals(false, sut.canEmbedJpeg());
+ sut.setCanEmbedJpeg(true);
+ assertEquals(true, sut.canEmbedJpeg());
+ }
+}
--- /dev/null
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.xmlgraphics.util.MimeConstants;
+
+/**
+ * Test case for {@link AFPResourceManager}.
+ */
+public class AFPResourceManagerTestCase {
+
+ private AFPResourceManager sut;
+
+ @Before
+ public void setUp() throws IOException {
+ sut = new AFPResourceManager();
+ AFPPaintingState paintingState = new AFPPaintingState();
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ DataStream stream = sut.createDataStream(paintingState, outStream);
+ stream.startPage(0, 0, 0, 10, 10);
+ }
+
+ /**
+ * Ensures that if tryIncludeObject() is called with a new object, it returns false suggesting
+ * that we have to create said object. However, if it is called with an object that has already
+ * been created, it returns true suggesting that we don't have to create that object again.
+ * Page-segment is false.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ @Test
+ public void testTryIncludeObjectWithPageSegFalse() throws IOException {
+ AFPDataObjectInfo dataInfo = createAFPDataObjectInfo();
+ // An empty object needs to be created every time!
+ assertFalse(sut.tryIncludeObject(dataInfo));
+ sut.createObject(dataInfo);
+ assertTrue(sut.tryIncludeObject(dataInfo));
+ }
+
+ /**
+ * {@code testTryIncludeObjectWithPageSegFalse()} but with page-segment true.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ @Test
+ public void testTryIncludeObjectWithPageSegTrue() throws IOException {
+ AFPDataObjectInfo dataInfo = createAFPDataObjectInfo();
+ dataInfo.setCreatePageSegment(true);
+ // An empty object needs to be created every time!
+ assertFalse(sut.tryIncludeObject(dataInfo));
+ sut.createObject(dataInfo);
+ assertTrue(sut.tryIncludeObject(dataInfo));
+ }
+
+ private AFPDataObjectInfo createAFPDataObjectInfo() {
+ AFPDataObjectInfo dataInfo = new AFPDataObjectInfo();
+ dataInfo.setMimeType(MimeConstants.MIME_TIFF);
+ dataInfo.setData(new byte[1]);
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo(0, 0, 10, 10, 1, 0);
+ dataInfo.setObjectAreaInfo(objectAreaInfo);
+ return dataInfo;
+ }
+}
package org.apache.fop.afp;
-import org.apache.fop.afp.modca.IncludeObjectTestCase;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
+import org.apache.fop.afp.modca.IncludeObjectTestCase;
+
/**
* Test suite for FOP's AFP classes.
*/
@RunWith(Suite.class)
@SuiteClasses({
- IncludeObjectTestCase.class,
- AFPResourceUtilTestCase.class
+ IncludeObjectTestCase.class,
+ AFPResourceUtilTestCase.class,
+ AFPObjectAreaInfoTestCase.class,
+ AFPPaintingStateTestCase.class
})
public class AFPTestSuite {
}
--- /dev/null
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+
+/**
+ * Test case for {@link AFPRendererConfigurator}.
+ */
+public class AFPRendererConfiguratorTestCase {
+ private static FOUserAgent userAgent;
+
+ private AFPRendererConfigurator sut;
+
+ /**
+ * The FOUserAgent only needs to be created once.
+ */
+ @BeforeClass
+ public static void createUserAgent() {
+ userAgent = FopFactory.newInstance().newFOUserAgent();
+ }
+
+ /**
+ * Assigns an FOUserAgen with a config file at <code>uri</code>
+ *
+ * @param uri the URI of the config file
+ */
+ private void setConfigFile(String uri) {
+ String confTestsDir = "test/resources/conf/afp/";
+ try {
+ userAgent.getFactory().setUserConfig(confTestsDir + uri);
+ sut = new AFPRendererConfigurator(userAgent);
+ } catch (IOException ioe) {
+ fail("IOException: " + ioe);
+ } catch (SAXException se) {
+ fail("SAXException: " + se);
+ }
+ }
+
+ /**
+ * Test several config files relating to JPEG images in AFP.
+ *
+ * @throws FOPException if an error is thrown
+ */
+ @Test
+ public void testJpegImageConfig() throws FOPException {
+ testJpegSettings("no_image_config.xconf", 1.0f, false);
+ testJpegSettings("can_embed_jpeg.xconf", 1.0f, true);
+ testJpegSettings("bitmap_encode_quality.xconf", 0.5f, false);
+ }
+
+ private void testJpegSettings(String uri, float bitmapEncodingQual, boolean canEmbed)
+ throws FOPException {
+ AFPDocumentHandler docHandler = new AFPDocumentHandler();
+
+ setConfigFile(uri);
+ sut.configure(docHandler);
+
+ AFPPaintingState paintingState = docHandler.getPaintingState();
+ assertEquals(bitmapEncodingQual, paintingState.getBitmapEncodingQuality(), 0.01f);
+ assertEquals(canEmbed, paintingState.canEmbedJpeg());
+ }
+}
* Test suite for FOP's AFP output.
*/
@RunWith(Suite.class)
-@SuiteClasses({ NoOperationTestCase.class })
+@SuiteClasses({
+ NoOperationTestCase.class,
+ AFPRendererConfiguratorTestCase.class })
public class AFPTestSuite {
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+ <base>.</base>
+ <source-resolution>72</source-resolution>
+ <target-resolution>72</target-resolution>
+ <image-loading>
+ </image-loading>
+ <default-page-settings height="11in" width="8.26in"/>
+ <renderers>
+ <renderer mime="application/x-afp">
+ <images>
+ <jpeg allow-embedding="false" bitmap-encoding-quality="0.5"/>
+ </images>
+ </renderer>
+ </renderers>
+</fop>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+ <base>.</base>
+ <source-resolution>72</source-resolution>
+ <target-resolution>72</target-resolution>
+ <image-loading>
+ </image-loading>
+ <default-page-settings height="11in" width="8.26in"/>
+ <renderers>
+ <renderer mime="application/x-afp">
+ <images>
+ <jpeg allow-embedding="true"/>
+ </images>
+ </renderer>
+ </renderers>
+</fop>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+ <base>.</base>
+ <source-resolution>72</source-resolution>
+ <target-resolution>72</target-resolution>
+ <image-loading>
+ </image-loading>
+ <default-page-settings height="11in" width="8.26in"/>
+ <renderers>
+ <renderer mime="application/x-afp">
+ </renderer>
+ </renderers>
+</fop>