is, without being decoded and encoded. It also allows lossy JPEG compression. Patch by Jeremias Maerki and Mehdi Houshmand. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1195952 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_1rc1old
@@ -796,6 +796,23 @@ Note that the value of the encoding attribute in the example is the double-byte | |||
</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> |
@@ -17,4 +17,5 @@ org.apache.fop.render.afp.AFPImageHandlerRenderedImage | |||
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 |
@@ -108,6 +108,9 @@ public class AFPDataObjectFactory { | |||
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); |
@@ -90,22 +90,18 @@ public class AFPDitheredRectanglePainter extends AbstractAFPPainter { | |||
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 |
@@ -85,7 +85,7 @@ public class AFPImageObjectInfo extends AFPDataObjectInfo { | |||
* @return true if this image uses compression | |||
*/ | |||
public boolean hasCompression() { | |||
return compression > -1; | |||
return compression != -1; | |||
} | |||
/** | |||
@@ -123,6 +123,7 @@ public class AFPImageObjectInfo extends AFPDataObjectInfo { | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public String toString() { | |||
return "AFPImageObjectInfo{" + super.toString() | |||
+ ", compression=" + compression |
@@ -24,66 +24,60 @@ package org.apache.fop.afp; | |||
* 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; | |||
} | |||
/** | |||
@@ -125,7 +119,7 @@ public class AFPObjectAreaInfo { | |||
/** | |||
* 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; | |||
@@ -134,7 +128,7 @@ public class AFPObjectAreaInfo { | |||
/** | |||
* 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; | |||
@@ -149,24 +143,15 @@ public class AFPObjectAreaInfo { | |||
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; | |||
} | |||
} |
@@ -53,6 +53,9 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState | |||
/** 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(); | |||
@@ -61,6 +64,9 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState | |||
* format. | |||
*/ | |||
private boolean nativeImagesSupported = false; | |||
private boolean canEmbedJpeg = false; | |||
/** | |||
* true if CMYK images (requires IOCA FS45 suppport on the target platform) | |||
* may be generated | |||
@@ -219,6 +225,24 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState | |||
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 | |||
@@ -259,6 +283,25 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState | |||
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 | |||
* |
@@ -242,8 +242,7 @@ public class AFPResourceManager { | |||
private void includeObject(AFPDataObjectInfo dataObjectInfo, | |||
String objectName) { | |||
IncludeObject includeObject | |||
= dataObjectFactory.createInclude(objectName, dataObjectInfo); | |||
IncludeObject includeObject = dataObjectFactory.createInclude(objectName, dataObjectInfo); | |||
dataStream.getCurrentPage().addObject(includeObject); | |||
} | |||
@@ -21,6 +21,8 @@ package org.apache.fop.afp.fonts; | |||
import java.util.List; | |||
import org.apache.fop.fonts.FontTriplet; | |||
/** | |||
* FontInfo contains meta information on fonts | |||
@@ -28,7 +30,7 @@ import java.util.List; | |||
public class AFPFontInfo { | |||
private AFPFont font; | |||
private List/*<FontTriplet>*/ tripletList; | |||
private List<FontTriplet> tripletList; | |||
/** | |||
* Main constructor | |||
@@ -36,7 +38,7 @@ public class AFPFontInfo { | |||
* @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; | |||
} | |||
@@ -55,7 +57,7 @@ public class AFPFontInfo { | |||
* | |||
* @return List of font triplets | |||
*/ | |||
public List/*<FontTriplet>*/ getFontTriplets() { | |||
public List<FontTriplet> getFontTriplets() { | |||
return tripletList; | |||
} | |||
@@ -53,6 +53,9 @@ public class ImageContent extends AbstractStructuredObject { | |||
*/ | |||
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; | |||
@@ -66,7 +69,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
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; | |||
@@ -147,6 +150,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
* @param color the IDE color model. | |||
* @deprecated use {@link #setIDEStructureParameter(IDEStructureParameter)} instead | |||
*/ | |||
@Deprecated | |||
public void setImageIDEColorModel(byte color) { | |||
needIDEStructureParameter().setColorModel(color); | |||
} | |||
@@ -156,6 +160,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
* @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); | |||
} | |||
@@ -172,6 +177,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
private static final int MAX_DATA_LEN = 65535; | |||
/** {@inheritDoc} */ | |||
@Override | |||
protected void writeContent(OutputStream os) throws IOException { | |||
if (imageSizeParameter != null) { | |||
imageSizeParameter.writeToStream(os); | |||
@@ -206,6 +212,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
protected void writeStart(OutputStream os) throws IOException { | |||
final byte[] startData = new byte[] { | |||
(byte)0x91, // ID | |||
@@ -216,6 +223,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
protected void writeEnd(OutputStream os) throws IOException { | |||
final byte[] endData = new byte[] { | |||
(byte)0x93, // ID | |||
@@ -234,7 +242,7 @@ public class ImageContent extends AbstractStructuredObject { | |||
(byte)0x95, // ID | |||
0x02, // Length | |||
encoding, | |||
0x01, // RECID | |||
(byte)(encoding == COMPID_JPEG ? 0xFE : 0x01), // RECID | |||
}; | |||
return encodingData; | |||
} | |||
@@ -245,17 +253,17 @@ public class ImageContent extends AbstractStructuredObject { | |||
* @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 |
@@ -74,8 +74,8 @@ public abstract class AbstractDataObject extends AbstractNamedAFPObject | |||
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 |
@@ -96,7 +96,7 @@ public class GraphicsObject extends AbstractDataObject { | |||
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); | |||
} |
@@ -107,7 +107,7 @@ public class ObjectContainer extends AbstractDataObject { | |||
ContainerDataDescriptor containerDataDescriptor | |||
= factory.createContainerDataDescriptor( | |||
dataWidth, dataHeight, widthRes, heightRes); | |||
dataWidth, dataHeight, widthRes, heightRes); | |||
getObjectEnvironmentGroup().setDataDescriptor(containerDataDescriptor); | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.apache.fop.afp.modca; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import org.apache.xmlgraphics.util.MimeConstants; | |||
@@ -43,9 +44,8 @@ public final class Registry { | |||
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; | |||
@@ -203,7 +203,7 @@ public final class Registry { | |||
* @return the MOD:CA object type | |||
*/ | |||
public ObjectType getObjectType(String mimeType) { | |||
return (ObjectType)mimeObjectTypeMap.get(mimeType); | |||
return mimeObjectTypeMap.get(mimeType); | |||
} | |||
/** |
@@ -42,18 +42,20 @@ public class ImageHandlerRegistry { | |||
/** 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; | |||
@@ -94,14 +96,14 @@ public class ImageHandlerRegistry { | |||
* @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; | |||
@@ -119,9 +121,7 @@ public class ImageHandlerRegistry { | |||
* @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; | |||
@@ -138,10 +138,8 @@ public class ImageHandlerRegistry { | |||
*/ | |||
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++) { | |||
@@ -149,7 +147,7 @@ public class ImageHandlerRegistry { | |||
} | |||
} | |||
} | |||
return (ImageFlavor[])flavors.toArray(new ImageFlavor[flavors.size()]); | |||
return flavors.toArray(new ImageFlavor[flavors.size()]); | |||
} | |||
/** |
@@ -71,6 +71,16 @@ public interface AFPCustomizable { | |||
*/ | |||
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 | |||
* | |||
@@ -123,4 +133,11 @@ public interface AFPCustomizable { | |||
*/ | |||
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); | |||
} |
@@ -428,6 +428,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler | |||
this.paintingState.setDitheringQuality(quality); | |||
} | |||
/** {@inheritDoc} */ | |||
public void setBitmapEncodingQuality(float quality) { | |||
this.paintingState.setBitmapEncodingQuality(quality); | |||
} | |||
/** {@inheritDoc} */ | |||
public void setShadingMode(AFPShadingMode shadingMode) { | |||
this.shadingMode = shadingMode; | |||
@@ -483,4 +488,9 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler | |||
return pageSegmentMap.get(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public void canEmbedJpeg(boolean canEmbed) { | |||
paintingState.setCanEmbedJpeg(canEmbed); | |||
} | |||
} |
@@ -62,24 +62,17 @@ public abstract class AFPImageHandler implements ImageHandlerBase { | |||
*/ | |||
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; | |||
} | |||
@@ -19,6 +19,12 @@ | |||
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; | |||
@@ -38,7 +44,11 @@ public class AFPImageHandlerRawCCITTFax extends AbstractAFPImageHandlerRawStream | |||
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; | |||
@@ -54,6 +64,15 @@ public class AFPImageHandlerRawCCITTFax extends AbstractAFPImageHandlerRawStream | |||
} | |||
/** {@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(); | |||
} |
@@ -0,0 +1,205 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -19,6 +19,12 @@ | |||
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; | |||
@@ -40,6 +46,9 @@ public class AFPImageHandlerRawStream extends AbstractAFPImageHandlerRawStream { | |||
ImageFlavor.RAW_EPS, | |||
}; | |||
/** logging instance */ | |||
private final Log log = LogFactory.getLog(AFPImageHandlerRawJPEG.class); | |||
/** {@inheritDoc} */ | |||
public int getPriority() { | |||
return 200; | |||
@@ -56,10 +65,22 @@ public class AFPImageHandlerRawStream extends AbstractAFPImageHandlerRawStream { | |||
} | |||
/** {@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) { |
@@ -28,10 +28,10 @@ import java.awt.image.MultiPixelPackedSampleModel; | |||
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; | |||
@@ -40,6 +40,9 @@ import org.apache.xmlgraphics.image.loader.ImageFlavor; | |||
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; | |||
@@ -50,6 +53,7 @@ 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; | |||
@@ -284,7 +288,26 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima | |||
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(); | |||
} | |||
} | |||
@@ -393,6 +416,14 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima | |||
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); | |||
} | |||
} | |||
} |
@@ -23,10 +23,12 @@ import java.io.File; | |||
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; | |||
@@ -80,10 +82,10 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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); | |||
} | |||
@@ -183,10 +185,10 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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)); | |||
@@ -222,10 +224,10 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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) { | |||
@@ -319,7 +321,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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()) { | |||
@@ -396,6 +398,24 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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( | |||
@@ -480,7 +500,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator | |||
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) { |
@@ -154,23 +154,18 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { | |||
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; | |||
} | |||
@@ -103,8 +103,7 @@ public abstract class AbstractAFPImageHandlerRawStream extends AFPImageHandler | |||
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; |
@@ -60,6 +60,10 @@ | |||
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> |
@@ -0,0 +1,76 @@ | |||
/* | |||
* 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()); | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
/* | |||
* 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()); | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -19,18 +19,21 @@ | |||
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 { | |||
} |
@@ -0,0 +1,92 @@ | |||
/* | |||
* 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()); | |||
} | |||
} |
@@ -27,6 +27,8 @@ import org.junit.runners.Suite.SuiteClasses; | |||
* Test suite for FOP's AFP output. | |||
*/ | |||
@RunWith(Suite.class) | |||
@SuiteClasses({ NoOperationTestCase.class }) | |||
@SuiteClasses({ | |||
NoOperationTestCase.class, | |||
AFPRendererConfiguratorTestCase.class }) | |||
public class AFPTestSuite { | |||
} |
@@ -0,0 +1,16 @@ | |||
<?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> |
@@ -0,0 +1,16 @@ | |||
<?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> |
@@ -0,0 +1,13 @@ | |||
<?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> |