]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
AFP Output Changes:
authorJeremias Maerki <jeremias@apache.org>
Sat, 12 Jun 2010 08:19:48 +0000 (08:19 +0000)
committerJeremias Maerki <jeremias@apache.org>
Sat, 12 Jun 2010 08:19:48 +0000 (08:19 +0000)
- Fixed positioning of Java2D-based images (when GOCA is enabled). GraphicsDataDescriptor had a bit order bug. The Graphics2D image handler didn't save state and reposition the image origin.
- Switched bitmap image handling in AFPGraphics2D to (re-)use AFPImageHandlerRenderedImage so it can profit from it's advanced image conversion functionality. This also avoids some bugs with certain image formats.
- Added enhanced dithering functionality for images that need to be converted to bi-level images.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@953952 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
src/documentation/content/xdocs/trunk/output.xml
src/java/org/apache/fop/afp/AFPGraphics2D.java
src/java/org/apache/fop/afp/AFPPaintingState.java
src/java/org/apache/fop/afp/modca/GraphicsDataDescriptor.java
src/java/org/apache/fop/render/afp/AFPCustomizable.java
src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
src/java/org/apache/fop/render/afp/AFPRenderer.java
src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
src/java/org/apache/fop/util/bitmap/BitmapImageUtil.java
status.xml

index 54ed357a010c3ebd499c71ff3672cdcfdc9e8311..7a91992ca221d0d602c5974a0a177dd3ef3fdb04 100644 (file)
@@ -531,9 +531,10 @@ out = proc.getOutputStream();]]></source>
         latest features. We're trying to make AFP output work in as many environments as possible.
         However, to make AFP output work on older environments it is recommended to set to
         configuration to 1 bit per pixel (see below on how to do this). In this case, all images
-        are converted to bi-level images using IOCA function set 10 (FS10). If a higher number of
-        bits per pixel is configured, FOP has to switch to at least FS11 which may not work
-        everywhere.
+        are converted to bi-level images using IOCA function set 10 (FS10) and are enclosed in
+        page-segments since some implementation cannot deal with IOCA objects directly.
+        If a higher number of bits per pixel is configured, FOP has to switch to at least FS11
+        which may not work everywhere.
       </p>
     </section>
     <section id="afp-configuration">
@@ -724,8 +725,20 @@ Note that the value of the encoding attribute in the example is the double-byte
         colors. This will only have an effect if the color mode is set to "color". Example:
       </p>
       <source><![CDATA[
-      <images mode="color" cmyk="true"/>
-]]></source>
+      <images mode="color" cmyk="true"/>]]></source>
+        <p>
+          When the color mode is set to 1 bit (bi-level), the "dithering-quality" attribute can
+          be used to select the level of quality to use when converting images to bi-level images.
+          Valid values for this attribute are floating point numbers from 0.0 (fastest) to
+          1.0 (best), or special values: "minimum" (=0.0), "maximum" (1.0),
+          "medium" (0.5, the default). For the higher settings to work as expected, JAI needs to
+          be present in the classpath. If JAI is present, 0.0 results in a minimal darkness-level
+          switching between white and black. 0.5 does bayer-based dithering and 1.0 will use
+          error-diffusion dithering. The higher the value, the higher the quality and the slower
+          the processing of the images.
+        </p>
+        <source><![CDATA[
+      <images mode="b+w" bits-per-pixel="1" dithering-quality="maximum"/>]]></source>
       </section>
       <section id="afp-shading-config">
         <title>Shading</title>
index b8d7158cffb3a778389efe63645fc4645582ef52..fa9c0d7bf3d053ebe67acf36a769e8b308aaf7d0 100644 (file)
@@ -45,7 +45,6 @@ import java.awt.image.RenderedImage;
 import java.awt.image.renderable.RenderableImage;
 import java.io.IOException;
 
-import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -56,8 +55,6 @@ import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
 import org.apache.xmlgraphics.java2d.GraphicContext;
 import org.apache.xmlgraphics.java2d.StrokingTextHandler;
 import org.apache.xmlgraphics.java2d.TextHandler;
-import org.apache.xmlgraphics.ps.ImageEncodingHelper;
-import org.apache.xmlgraphics.util.MimeConstants;
 import org.apache.xmlgraphics.util.UnitConv;
 
 import org.apache.fop.afp.goca.GraphicsSetLineType;
@@ -65,6 +62,8 @@ import org.apache.fop.afp.modca.GraphicsObject;
 import org.apache.fop.afp.svg.AFPGraphicsConfiguration;
 import org.apache.fop.afp.util.CubicBezierApproximator;
 import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.afp.AFPImageHandlerRenderedImage;
+import org.apache.fop.render.afp.AFPRenderingContext;
 import org.apache.fop.svg.NativeImageHandler;
 
 /**
@@ -559,75 +558,6 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
                                  BufferedImage.TYPE_INT_ARGB);
     }
 
-    private AFPImageObjectInfo createImageObjectInfo(
-            RenderedImage img, int x, int y, int width, int height) throws IOException {
-        ImageInfo imageInfo = new ImageInfo(null, "image/unknown");
-        ImageSize size = new ImageSize(img.getWidth(), img.getHeight(), 72);
-        imageInfo.setSize(size);
-
-        ImageRendered imageRendered = new ImageRendered(imageInfo, img, null);
-        RenderedImage renderedImage = imageRendered.getRenderedImage();
-
-        // create image object info
-        AFPImageObjectInfo imageObjectInfo = new AFPImageObjectInfo();
-
-        imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS45);
-
-        int bitsPerPixel = paintingState.getBitsPerPixel();
-        imageObjectInfo.setBitsPerPixel(bitsPerPixel);
-
-        imageObjectInfo.setResourceInfo(resourceInfo);
-
-        int dataHeight = renderedImage.getHeight();
-        imageObjectInfo.setDataHeight(dataHeight);
-
-        int dataWidth = renderedImage.getWidth();
-        imageObjectInfo.setDataWidth(dataWidth);
-
-        int resolution = paintingState.getResolution();
-        imageObjectInfo.setDataWidthRes(resolution);
-        imageObjectInfo.setDataHeightRes(resolution);
-
-        boolean colorImages = paintingState.isColorImages();
-        imageObjectInfo.setColor(colorImages);
-
-        ByteArrayOutputStream boas = new ByteArrayOutputStream();
-        ImageEncodingHelper.encodeRenderedImageAsRGB(renderedImage, boas);
-        byte[] imageData = boas.toByteArray();
-
-        // convert to grayscale
-        if (!colorImages) {
-            boas.reset();
-            imageObjectInfo.setBitsPerPixel(bitsPerPixel);
-            ImageEncodingHelper.encodeRGBAsGrayScale(
-                  imageData, dataWidth, dataHeight, bitsPerPixel, boas);
-            imageData = boas.toByteArray();
-            if (bitsPerPixel == 1) {
-                //FS10 should generate a page seqment to avoid problems
-                imageObjectInfo.setCreatePageSegment(true);
-            }
-        }
-        imageObjectInfo.setData(imageData);
-
-        if (imageInfo != null) {
-            imageObjectInfo.setUri(imageInfo.getOriginalURI());
-        }
-
-        // create object area info
-        AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
-        objectAreaInfo.setX(x);
-        objectAreaInfo.setY(y);
-        objectAreaInfo.setWidth(width);
-        objectAreaInfo.setHeight(height);
-
-        objectAreaInfo.setWidthRes(resolution);
-        objectAreaInfo.setHeightRes(resolution);
-
-        imageObjectInfo.setObjectAreaInfo(objectAreaInfo);
-
-        return imageObjectInfo;
-    }
-
     /**
      * Draws an AWT image into a BufferedImage using an AWT Graphics2D implementation
      *
@@ -667,7 +597,6 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
     /** {@inheritDoc} */
     public boolean drawImage(Image img, int x, int y, int width, int height,
             ImageObserver observer) {
-
         // draw with AWT Graphics2D
         Dimension imageSize = new Dimension(width, height);
         BufferedImage bufferedImage = buildBufferedImage(imageSize);
@@ -684,20 +613,33 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
         int imgWidth = img.getWidth();
         int imgHeight = img.getHeight();
 
-        AffineTransform at = paintingState.getData().getTransform();
         AffineTransform gat = gc.getTransform();
         int graphicsObjectHeight
             = graphicsObj.getObjectEnvironmentGroup().getObjectAreaDescriptor().getHeight();
-        int x = (int)Math.round(at.getTranslateX() + gat.getTranslateX());
-        int y = (int)Math.round(at.getTranslateY() - (gat.getTranslateY() - graphicsObjectHeight));
-        int width = (int)Math.round(imgWidth * gat.getScaleX());
-        int height = (int)Math.round(imgHeight * -gat.getScaleY());
+
+        double toMillipointFactor = UnitConv.IN2PT * 1000 / (double)paintingState.getResolution();
+        double x = gat.getTranslateX();
+        double y = -(gat.getTranslateY() - graphicsObjectHeight);
+        x = toMillipointFactor * x;
+        y = toMillipointFactor * y;
+        double w = toMillipointFactor * imgWidth * gat.getScaleX();
+        double h = toMillipointFactor * imgHeight * -gat.getScaleY();
+
+        AFPImageHandlerRenderedImage handler = new AFPImageHandlerRenderedImage();
+        ImageInfo imageInfo = new ImageInfo(null, null);
+        imageInfo.setSize(new ImageSize(
+                img.getWidth(), img.getHeight(), paintingState.getResolution()));
+        imageInfo.getSize().calcSizeFromPixels();
+        ImageRendered red = new ImageRendered(imageInfo, img, null);
+        Rectangle targetPos = new Rectangle(
+                (int)Math.round(x),
+                (int)Math.round(y),
+                (int)Math.round(w),
+                (int)Math.round(h));
+        AFPRenderingContext context = new AFPRenderingContext(null,
+                resourceManager, paintingState, fontInfo, null);
         try {
-            // get image object info
-            AFPImageObjectInfo imageObjectInfo
-                = createImageObjectInfo(img, x, y, width, height);
-            // create image resource
-            resourceManager.createObject(imageObjectInfo);
+            handler.handleImage(context, red, targetPos);
         } catch (IOException ioe) {
             handleIOException(ioe);
         }
index a198741837202940f21da00872d11f5a9b776ce6..4e314e5d9d8bfe4b7a6928b0fb579f8bea13e440 100644 (file)
@@ -51,6 +51,9 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState
     /** color image support */
     private boolean colorImages = false;
 
+    /** dithering quality setting (0.0f..1.0f) */
+    private float ditheringQuality;
+
     /** color image handler */
     private ColorConverter colorConverter = GrayScaleColorConverter.getInstance();
 
@@ -233,6 +236,25 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState
         return this.cmykImagesSupported;
     }
 
+    /**
+     * Gets the dithering quality setting to use when converting images to monochrome images.
+     * @return the dithering quality (a value between 0.0f and 1.0f)
+     */
+    public float getDitheringQuality() {
+        return this.ditheringQuality;
+    }
+
+    /**
+     * Sets the dithering quality setting to use when converting images to monochrome images.
+     * @param quality Defines the desired quality level for the conversion.
+     *                  Valid values: a value between 0.0f (fastest) and 1.0f (best)
+     */
+    public void setDitheringQuality(float quality) {
+        quality = Math.max(quality, 0.0f);
+        quality = Math.min(quality, 1.0f);
+        this.ditheringQuality = quality;
+    }
+
     /**
      * Sets the output/device resolution
      *
index 5495e2e9ce13fe5db64bd27d043daa392e92df98..5fa3b70a9bfc45ce96872c92e237b00bfdaf81c4 100644 (file)
@@ -104,8 +104,8 @@ public class GraphicsDataDescriptor extends AbstractDescriptor {
         return data;
     }
 
-    private static final int ABS = 2;
-    private static final int IMGRES = 8;
+    private static final int ABS = 64;
+    private static final int IMGRES = 16;
 
     /**
      * Returns the window specification data
index 5f3fe6823077037319b9750d932de31cda22c639..93aaa8b1a3d2890525609018f064f249f6d9c4ab 100644 (file)
@@ -64,6 +64,13 @@ public interface AFPCustomizable {
      */
     void setShadingMode(AFPShadingMode shadingMode);
 
+    /**
+     * Sets the dithering quality setting to use when converting images to monochrome images.
+     * @param quality Defines the desired quality level for the conversion.
+     *                  Valid values: a value between 0.0f (fastest) and 1.0f (best)
+     */
+    void setDitheringQuality(float quality);
+
     /**
      * Sets the output/device resolution
      *
index 3fec25d8de412f759a17fd0a6be876175b869109..21d4faf56ec45c1b01f3639041b5d4f18454f6e0 100644 (file)
@@ -46,8 +46,8 @@ import org.apache.fop.fonts.FontManager;
 import org.apache.fop.render.afp.extensions.AFPElementMapping;
 import org.apache.fop.render.afp.extensions.AFPIncludeFormMap;
 import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap;
-import org.apache.fop.render.afp.extensions.AFPPageSetup;
 import org.apache.fop.render.afp.extensions.AFPPageOverlay;
+import org.apache.fop.render.afp.extensions.AFPPageSetup;
 import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
 import org.apache.fop.render.intermediate.IFDocumentHandler;
 import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
@@ -361,6 +361,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
         paintingState.setCMYKImagesSupported(value);
     }
 
+    /** {@inheritDoc} */
+    public void setDitheringQuality(float quality) {
+        this.paintingState.setDitheringQuality(quality);
+    }
+
     /** {@inheritDoc} */
     public void setShadingMode(AFPShadingMode shadingMode) {
         this.shadingMode = shadingMode;
index 32a95b44512413e22183822d3061e1e80950a12e..aaaecf2eabcf3afb747b72e531b79a352fbca427 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.render.afp;
 
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.io.IOException;
 
 import org.apache.xmlgraphics.image.loader.Image;
@@ -136,6 +137,12 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH
 
         setDefaultResourceLevel(graphicsObjectInfo, afpContext.getResourceManager());
 
+        AFPPaintingState paintingState = afpContext.getPaintingState();
+        paintingState.save(); // save
+        AffineTransform placement = new AffineTransform();
+        placement.translate(pos.x, pos.y);
+        paintingState.concatenate(placement);
+
         // Image content
         ImageGraphics2D imageG2D = (ImageGraphics2D)image;
         boolean textAsShapes = false; //TODO Make configurable
@@ -152,6 +159,8 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH
 
         // Create image
         afpContext.getResourceManager().createObject(graphicsObjectInfo);
+
+        paintingState.restore(); // resume
     }
 
     /** {@inheritDoc} */
index 330f78d638223254a7bcaa117761da5f46429659..cb7b23da22fc89cbd8020ac9e6ac1000f2558f2f 100644 (file)
@@ -101,6 +101,7 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
                 maxPixelSize *= 3; //RGB is maximum
             }
         }
+        float ditheringQuality = paintingState.getDitheringQuality();
         RenderedImage renderedImage = imageRendered.getRenderedImage();
 
         ImageInfo imageInfo = imageRendered.getInfo();
@@ -130,9 +131,13 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
                     log.debug("Resample from " + intrinsicSize.getDimensionPx()
                             + " to " + resampledDim);
                 }
-                renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage, resampledDim);
+                renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage,
+                        resampledDim, ditheringQuality);
                 effIntrinsicSize = new ImageSize(
                         resampledDim.width, resampledDim.height, resolution);
+            } else if (ditheringQuality >= 0.5f) {
+                renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage,
+                        intrinsicSize.getDimensionPx(), ditheringQuality);
             }
         }
 
@@ -157,7 +162,6 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
         if (cm.hasAlpha()) {
             pixelSize -= 8;
         }
-        //TODO Add support for CMYK images
 
         byte[] imageData = null;
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
index d88deadfe8d62dc8d2e4b160ae7fcc3ac085d4a3..1f373023c7ce7b6e3e28e8915f12c201128c3965 100644 (file)
@@ -838,6 +838,11 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
         paintingState.setCMYKImagesSupported(value);
     }
 
+    /** {@inheritDoc} */
+    public void setDitheringQuality(float quality) {
+        this.paintingState.setDitheringQuality(quality);
+    }
+
     /** {@inheritDoc} */
     public void setShadingMode(AFPShadingMode shadingMode) {
         this.shadingMode = shadingMode;
index 23f413813572ae5eabdedcb8d6b471474adb973a..1720667dfb8a49b33985d69ac994b4cc4271aca7 100644 (file)
@@ -390,6 +390,21 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
             customizable.setBitsPerPixel(bitsPerPixel);
         }
 
+        String dithering = imagesCfg.getAttribute("dithering-quality", "medium");
+        float dq = 0.5f;
+        if (dithering.startsWith("min")) {
+            dq = 0.0f;
+        } else if (dithering.startsWith("max")) {
+            dq = 1.0f;
+        } else {
+            try {
+                dq = Float.parseFloat(dithering);
+            } catch (NumberFormatException nfe) {
+                //ignore and leave the default above
+            }
+        }
+        customizable.setDitheringQuality(dq);
+
         // native image support
         boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
         customizable.setNativeImagesSupported(nativeImageSupport);
index cb46395ca82998c3c191f6187b0c205c6846353c..c08076316c144f93615a2085f4e1d28b056f1159 100644 (file)
@@ -135,6 +135,53 @@ public class BitmapImageUtil {
      */
     public static final BufferedImage convertToMonochrome(RenderedImage img,
             Dimension targetDimension) {
+        return toBufferedImage(convertToMonochrome(img, targetDimension, 0.0f));
+    }
+
+    /**
+     * Converts an image to a monochrome 1-bit image. Optionally, the image can be scaled.
+     * @param img the image to be converted
+     * @param targetDimension the new target dimensions or null if no scaling is necessary
+     * @param quality Defines the desired quality level for the conversion.
+     *                  Valid values: a value between 0.0f (fastest) and 1.0f (best)
+     * @return the monochrome image
+     */
+    public static final RenderedImage convertToMonochrome(RenderedImage img,
+            Dimension targetDimension, float quality) {
+        if (!isMonochromeImage(img)) {
+            if (quality >= 0.5f) {
+                BufferedImage bi;
+                Dimension orgDim = new Dimension(img.getWidth(), img.getHeight());
+                if (targetDimension != null && !orgDim.equals(targetDimension)) {
+                    //Scale only before dithering
+                    ColorModel cm = img.getColorModel();
+                    BufferedImage tgt = new BufferedImage(cm,
+                            cm.createCompatibleWritableRaster(
+                                    targetDimension.width, targetDimension.height),
+                            cm.isAlphaPremultiplied(), null);
+                    transferImage(img, tgt);
+                    bi = tgt;
+                } else {
+                    bi = toBufferedImage(img);
+                }
+                //Now convert to monochrome (dithering if available)
+                MonochromeBitmapConverter converter = createDefaultMonochromeBitmapConverter();
+                if (quality >= 0.8f) {
+                    //Activates error diffusion if JAI is available
+                    converter.setHint("quality", Boolean.TRUE.toString());
+                    //Need to convert to grayscale first since otherwise, there may be encoding
+                    //problems later with the images JAI can generate.
+                    bi = convertToGrayscale(bi, targetDimension);
+                }
+                try {
+                    return converter.convertToMonochrome(bi);
+                } catch (Exception e) {
+                    //Provide a fallback if exotic formats are encountered
+                    bi = convertToGrayscale(bi, targetDimension);
+                    return converter.convertToMonochrome(bi);
+                }
+            }
+        }
         return convertAndScaleImage(img, targetDimension, BufferedImage.TYPE_BYTE_BINARY);
     }
 
index f933e8a64fdacd7d70773c3a1020dc7d73e5209c..353b8059b2e443d2e49b5cf58eb040455cc88eda 100644 (file)
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="JM" type="fix">
+        AFP Output: Fixed positioning of Java2D-based images (when GOCA is enabled).
+      </action>
+      <action context="Renderers" dev="JM" type="add">
+        AFP Output: Added enhanced dithering functionality for images that are converted to
+        bi-level images.
+      </action>
       <action context="Renderers" dev="JM" type="fix">
         AFP Output: Fix for bitmap images inside an SVG or G2D graphic (printer errors) and
         positioning fix for bitmaps from G2D graphics.