Просмотр исходного кода

AFP Output Changes:

- 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
pull/37/head
Jeremias Maerki 14 лет назад
Родитель
Сommit
ea5628ad60

+ 18
- 5
src/documentation/content/xdocs/trunk/output.xml Просмотреть файл

latest features. We're trying to make AFP output work in as many environments as possible. 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 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 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> </p>
</section> </section>
<section id="afp-configuration"> <section id="afp-configuration">
colors. This will only have an effect if the color mode is set to "color". Example: colors. This will only have an effect if the color mode is set to "color". Example:
</p> </p>
<source><![CDATA[ <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>
<section id="afp-shading-config"> <section id="afp-shading-config">
<title>Shading</title> <title>Shading</title>

+ 25
- 83
src/java/org/apache/fop/afp/AFPGraphics2D.java Просмотреть файл

import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImage;
import java.io.IOException; import java.io.IOException;


import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;


import org.apache.xmlgraphics.java2d.GraphicContext; import org.apache.xmlgraphics.java2d.GraphicContext;
import org.apache.xmlgraphics.java2d.StrokingTextHandler; import org.apache.xmlgraphics.java2d.StrokingTextHandler;
import org.apache.xmlgraphics.java2d.TextHandler; 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.xmlgraphics.util.UnitConv;


import org.apache.fop.afp.goca.GraphicsSetLineType; import org.apache.fop.afp.goca.GraphicsSetLineType;
import org.apache.fop.afp.svg.AFPGraphicsConfiguration; import org.apache.fop.afp.svg.AFPGraphicsConfiguration;
import org.apache.fop.afp.util.CubicBezierApproximator; import org.apache.fop.afp.util.CubicBezierApproximator;
import org.apache.fop.fonts.FontInfo; 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; import org.apache.fop.svg.NativeImageHandler;


/** /**
BufferedImage.TYPE_INT_ARGB); 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 * Draws an AWT image into a BufferedImage using an AWT Graphics2D implementation
* *
/** {@inheritDoc} */ /** {@inheritDoc} */
public boolean drawImage(Image img, int x, int y, int width, int height, public boolean drawImage(Image img, int x, int y, int width, int height,
ImageObserver observer) { ImageObserver observer) {

// draw with AWT Graphics2D // draw with AWT Graphics2D
Dimension imageSize = new Dimension(width, height); Dimension imageSize = new Dimension(width, height);
BufferedImage bufferedImage = buildBufferedImage(imageSize); BufferedImage bufferedImage = buildBufferedImage(imageSize);
int imgWidth = img.getWidth(); int imgWidth = img.getWidth();
int imgHeight = img.getHeight(); int imgHeight = img.getHeight();


AffineTransform at = paintingState.getData().getTransform();
AffineTransform gat = gc.getTransform(); AffineTransform gat = gc.getTransform();
int graphicsObjectHeight int graphicsObjectHeight
= graphicsObj.getObjectEnvironmentGroup().getObjectAreaDescriptor().getHeight(); = 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 { 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) { } catch (IOException ioe) {
handleIOException(ioe); handleIOException(ioe);
} }

+ 22
- 0
src/java/org/apache/fop/afp/AFPPaintingState.java Просмотреть файл

/** color image support */ /** color image support */
private boolean colorImages = false; private boolean colorImages = false;


/** dithering quality setting (0.0f..1.0f) */
private float ditheringQuality;

/** color image handler */ /** color image handler */
private ColorConverter colorConverter = GrayScaleColorConverter.getInstance(); private ColorConverter colorConverter = GrayScaleColorConverter.getInstance();


return this.cmykImagesSupported; 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 * Sets the output/device resolution
* *

+ 2
- 2
src/java/org/apache/fop/afp/modca/GraphicsDataDescriptor.java Просмотреть файл

return data; 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 * Returns the window specification data

+ 7
- 0
src/java/org/apache/fop/render/afp/AFPCustomizable.java Просмотреть файл

*/ */
void setShadingMode(AFPShadingMode shadingMode); 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 * Sets the output/device resolution
* *

+ 6
- 1
src/java/org/apache/fop/render/afp/AFPDocumentHandler.java Просмотреть файл

import org.apache.fop.render.afp.extensions.AFPElementMapping; import org.apache.fop.render.afp.extensions.AFPElementMapping;
import org.apache.fop.render.afp.extensions.AFPIncludeFormMap; import org.apache.fop.render.afp.extensions.AFPIncludeFormMap;
import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap; 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.AFPPageOverlay;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
paintingState.setCMYKImagesSupported(value); paintingState.setCMYKImagesSupported(value);
} }


/** {@inheritDoc} */
public void setDitheringQuality(float quality) {
this.paintingState.setDitheringQuality(quality);
}

/** {@inheritDoc} */ /** {@inheritDoc} */
public void setShadingMode(AFPShadingMode shadingMode) { public void setShadingMode(AFPShadingMode shadingMode) {
this.shadingMode = shadingMode; this.shadingMode = shadingMode;

+ 9
- 0
src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java Просмотреть файл

package org.apache.fop.render.afp; package org.apache.fop.render.afp;


import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.io.IOException; import java.io.IOException;


import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.Image;


setDefaultResourceLevel(graphicsObjectInfo, afpContext.getResourceManager()); 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 // Image content
ImageGraphics2D imageG2D = (ImageGraphics2D)image; ImageGraphics2D imageG2D = (ImageGraphics2D)image;
boolean textAsShapes = false; //TODO Make configurable boolean textAsShapes = false; //TODO Make configurable


// Create image // Create image
afpContext.getResourceManager().createObject(graphicsObjectInfo); afpContext.getResourceManager().createObject(graphicsObjectInfo);

paintingState.restore(); // resume
} }


/** {@inheritDoc} */ /** {@inheritDoc} */

+ 6
- 2
src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java Просмотреть файл

maxPixelSize *= 3; //RGB is maximum maxPixelSize *= 3; //RGB is maximum
} }
} }
float ditheringQuality = paintingState.getDitheringQuality();
RenderedImage renderedImage = imageRendered.getRenderedImage(); RenderedImage renderedImage = imageRendered.getRenderedImage();


ImageInfo imageInfo = imageRendered.getInfo(); ImageInfo imageInfo = imageRendered.getInfo();
log.debug("Resample from " + intrinsicSize.getDimensionPx() log.debug("Resample from " + intrinsicSize.getDimensionPx()
+ " to " + resampledDim); + " to " + resampledDim);
} }
renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage, resampledDim);
renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage,
resampledDim, ditheringQuality);
effIntrinsicSize = new ImageSize( effIntrinsicSize = new ImageSize(
resampledDim.width, resampledDim.height, resolution); resampledDim.width, resampledDim.height, resolution);
} else if (ditheringQuality >= 0.5f) {
renderedImage = BitmapImageUtil.convertToMonochrome(renderedImage,
intrinsicSize.getDimensionPx(), ditheringQuality);
} }
} }


if (cm.hasAlpha()) { if (cm.hasAlpha()) {
pixelSize -= 8; pixelSize -= 8;
} }
//TODO Add support for CMYK images


byte[] imageData = null; byte[] imageData = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();

+ 5
- 0
src/java/org/apache/fop/render/afp/AFPRenderer.java Просмотреть файл

paintingState.setCMYKImagesSupported(value); paintingState.setCMYKImagesSupported(value);
} }


/** {@inheritDoc} */
public void setDitheringQuality(float quality) {
this.paintingState.setDitheringQuality(quality);
}

/** {@inheritDoc} */ /** {@inheritDoc} */
public void setShadingMode(AFPShadingMode shadingMode) { public void setShadingMode(AFPShadingMode shadingMode) {
this.shadingMode = shadingMode; this.shadingMode = shadingMode;

+ 15
- 0
src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java Просмотреть файл

customizable.setBitsPerPixel(bitsPerPixel); 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 // native image support
boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false); boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
customizable.setNativeImagesSupported(nativeImageSupport); customizable.setNativeImagesSupported(nativeImageSupport);

+ 47
- 0
src/java/org/apache/fop/util/bitmap/BitmapImageUtil.java Просмотреть файл

*/ */
public static final BufferedImage convertToMonochrome(RenderedImage img, public static final BufferedImage convertToMonochrome(RenderedImage img,
Dimension targetDimension) { 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); return convertAndScaleImage(img, targetDimension, BufferedImage.TYPE_BYTE_BINARY);
} }



+ 7
- 0
status.xml Просмотреть файл

documents. Example: the fix of marks layering will be such a case when it's done. documents. Example: the fix of marks layering will be such a case when it's done.
--> -->
<release version="FOP Trunk" date="TBD"> <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"> <action context="Renderers" dev="JM" type="fix">
AFP Output: Fix for bitmap images inside an SVG or G2D graphic (printer errors) and AFP Output: Fix for bitmap images inside an SVG or G2D graphic (printer errors) and
positioning fix for bitmaps from G2D graphics. positioning fix for bitmaps from G2D graphics.

Загрузка…
Отмена
Сохранить