aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2005-11-09 14:42:01 +0000
committerJeremias Maerki <jeremias@apache.org>2005-11-09 14:42:01 +0000
commit20fa5268df0017236762f1bcf2ebbcad1e623ac5 (patch)
tree6410401aef59457c8e70e6985fb8b41a6637f118 /src
parentd9aaf8603b6f29c1ad5e273d801ce8202d5b5134 (diff)
downloadxmlgraphics-fop-20fa5268df0017236762f1bcf2ebbcad1e623ac5.tar.gz
xmlgraphics-fop-20fa5268df0017236762f1bcf2ebbcad1e623ac5.zip
Bugzilla #37236:
Improvements for gradients in PDF Transcoder. Submitted by: Thomas Deweese <deweese.at.apache.org> git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@332046 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java240
1 files changed, 210 insertions, 30 deletions
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index b3a49da37..abcc627c5 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -45,6 +45,7 @@ import org.apache.batik.ext.awt.g2d.AbstractGraphics2D;
import org.apache.batik.ext.awt.g2d.GraphicContext;
import org.apache.batik.ext.awt.RadialGradientPaint;
import org.apache.batik.ext.awt.LinearGradientPaint;
+import org.apache.batik.ext.awt.MultipleGradientPaint;
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
import org.apache.batik.gvt.PatternPaint;
import org.apache.batik.gvt.GraphicsNode;
@@ -61,22 +62,26 @@ import java.awt.Image;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Paint;
+import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.BasicStroke;
import java.awt.AlphaComposite;
import java.awt.geom.AffineTransform;
+import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DirectColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
import java.awt.image.renderable.RenderableImage;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
-import java.awt.color.ColorSpace;
import java.io.StringWriter;
import java.io.IOException;
import java.io.OutputStream;
@@ -129,7 +134,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* The count of JPEG images added to document so they recieve
* unique keys.
*/
- protected int jpegCount = 0;
+ protected int[] jpegCount = {0};
/**
* The current font information.
@@ -376,12 +381,11 @@ public class PDFGraphics2D extends AbstractGraphics2D {
public void addJpegImage(JpegImage jpeg, float x, float y,
float width, float height) {
preparePainting();
- String key = "__AddJPEG_" + jpegCount;
- jpegCount++;
+ String key = "__AddJPEG_" + jpegCount[0];
+ jpegCount[0]++;
FopPDFImage fopimage = new FopPDFImage(jpeg, key);
int xObjectNum = this.pdfDoc.addImage(resourceContext,
fopimage).getXNumber();
-
AffineTransform at = getTransform();
double[] matrix = new double[6];
at.getMatrix(matrix);
@@ -709,7 +713,18 @@ public class PDFGraphics2D extends AbstractGraphics2D {
Paint paint = getPaint();
if (graphicsState.setPaint(paint)) {
- applyPaint(paint, false);
+ if (!applyPaint(paint, false)) {
+ // Stroke the shape and use it to 'clip'
+ // the paint contents.
+ Shape ss = getStroke().createStrokedShape(s);
+ applyUnknownPaint(paint, ss);
+
+ if (newClip || newTransform) {
+ currentStream.write("Q\n");
+ graphicsState.pop();
+ }
+ return;
+ }
}
applyStroke(getStroke());
@@ -831,19 +846,29 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* @param paint the paint to convert to PDF
* @param fill true if the paint should be set for filling
*/
- protected void applyPaint(Paint paint, boolean fill) {
+ protected boolean applyPaint(Paint paint, boolean fill) {
preparePainting();
+ if (paint instanceof Color) {
+ return true;
+ }
if (paint instanceof LinearGradientPaint) {
LinearGradientPaint gp = (LinearGradientPaint)paint;
+
+ // This code currently doesn't support 'repeat'.
+ // For linear gradients it is possible to construct
+ // a 'tile' that is repeated with a PDF pattern, but
+ // it would be very tricky as you would have to rotate
+ // the coordinate system so the repeat was axially
+ // aligned. At this point I'm just going to rasterize it.
+ MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
+ if (cycle != MultipleGradientPaint.NO_CYCLE) {
+ return false;
+ }
+
Color[] cols = gp.getColors();
float[] fractions = gp.getFractions();
- //MultipleGradientPaint.CycleMethodEnum cycenum = gp.getCycleMethod();
- //boolean cyclic = (cycenum == MultipleGradientPaint.REPEAT);
- // This code currently doesn't support 'repeat' as PDF has
- // no way to support this (we need to rasterize).
-
// Build proper transform from gradient space to page space
// ('Patterns' don't get userspace transform).
AffineTransform transform;
@@ -854,8 +879,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
List theMatrix = new java.util.ArrayList();
double [] mat = new double[6];
transform.getMatrix(mat);
- for (int idx=0; idx<mat.length; idx++)
+ for (int idx = 0; idx < mat.length; idx++) {
theMatrix.add(new Double(mat[idx]));
+ }
Point2D p1 = gp.getStartPoint();
Point2D p2 = gp.getEndPoint();
@@ -885,6 +911,10 @@ public class PDFGraphics2D extends AbstractGraphics2D {
for (int count = 0; count < cols.length; count++) {
Color c1 = cols[count];
+ if (c1.getAlpha() != 255) {
+ return false; // PDF can't do alpha
+ }
+
PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
c1.getBlue());
someColors.add(color1);
@@ -900,9 +930,23 @@ public class PDFGraphics2D extends AbstractGraphics2D {
someColors, theBounds, theCoords, theMatrix);
currentStream.write(myPat.getColorSpaceOut(fill));
- } else if (paint instanceof RadialGradientPaint) {
+ return true;
+ }
+ if (paint instanceof RadialGradientPaint) {
RadialGradientPaint rgp = (RadialGradientPaint)paint;
+ // There is essentially no way to support repeate
+ // in PDF for radial gradients (the one option would
+ // be to 'grow' the outer circle until it fully covered
+ // the bounds and then grow the stops accordingly, the
+ // problem is that this may require an extremely large
+ // number of stops for cases where the focus is near
+ // the edge of the outer circle). so we rasterize.
+ MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
+ if (cycle != MultipleGradientPaint.NO_CYCLE) {
+ return false;
+ }
+
AffineTransform transform;
transform = new AffineTransform(graphicsState.getTransform());
transform.concatenate(getTransform());
@@ -911,27 +955,28 @@ public class PDFGraphics2D extends AbstractGraphics2D {
List theMatrix = new java.util.ArrayList();
double [] mat = new double[6];
transform.getMatrix(mat);
- for (int idx=0; idx<mat.length; idx++)
+ for (int idx = 0; idx < mat.length; idx++) {
theMatrix.add(new Double(mat[idx]));
+ }
double ar = rgp.getRadius();
Point2D ac = rgp.getCenterPoint();
Point2D af = rgp.getFocusPoint();
List theCoords = new java.util.ArrayList();
- double dx = af.getX()-ac.getX();
- double dy = af.getY()-ac.getY();
- double d = Math.sqrt(dx*dx+dy*dy);
+ double dx = af.getX() - ac.getX();
+ double dy = af.getY() - ac.getY();
+ double d = Math.sqrt(dx * dx + dy * dy);
if (d > ar) {
// the center point af must be within the circle with
// radius ar centered at ac so limit it to that.
- double scale = (ar*.9999)/d;
- dx = dx*scale;
- dy = dy*scale;
+ double scale = (ar * .9999) / d;
+ dx = dx * scale;
+ dy = dy * scale;
}
- theCoords.add(new Double(ac.getX()+dx)); // Fx
- theCoords.add(new Double(ac.getY()+dy)); // Fy
+ theCoords.add(new Double(ac.getX() + dx)); // Fx
+ theCoords.add(new Double(ac.getY() + dy)); // Fy
theCoords.add(new Double(0));
theCoords.add(new Double(ac.getX()));
theCoords.add(new Double(ac.getY()));
@@ -941,6 +986,10 @@ public class PDFGraphics2D extends AbstractGraphics2D {
List someColors = new java.util.ArrayList();
for (int count = 0; count < cols.length; count++) {
Color cc = cols[count];
+ if (cc.getAlpha() != 255) {
+ return false; // PDF can't do alpha
+ }
+
someColors.add(new PDFColor(cc.getRed(), cc.getGreen(),
cc.getBlue()));
}
@@ -960,13 +1009,16 @@ public class PDFGraphics2D extends AbstractGraphics2D {
currentStream.write(myPat.getColorSpaceOut(fill));
- } else if (paint instanceof PatternPaint) {
+ return true;
+ }
+ if (paint instanceof PatternPaint) {
PatternPaint pp = (PatternPaint)paint;
- createPattern(pp, fill);
+ return createPattern(pp, fill);
}
+ return false; // unknown paint
}
- private void createPattern(PatternPaint pp, boolean fill) {
+ private boolean createPattern(PatternPaint pp, boolean fill) {
preparePainting();
FontInfo fontInfo = new FontInfo();
@@ -1021,8 +1073,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
List bbox = new java.util.ArrayList();
bbox.add(new Double(rect.getX()));
- bbox.add(new Double(rect.getHeight()+rect.getY()));
- bbox.add(new Double(rect.getWidth() +rect.getX()));
+ bbox.add(new Double(rect.getHeight() + rect.getY()));
+ bbox.add(new Double(rect.getWidth() + rect.getX()));
bbox.add(new Double(rect.getY()));
AffineTransform transform;
@@ -1033,8 +1085,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
List theMatrix = new java.util.ArrayList();
double [] mat = new double[6];
transform.getMatrix(mat);
- for (int idx=0; idx<mat.length; idx++)
+ for (int idx = 0; idx < mat.length; idx++) {
theMatrix.add(new Double(mat[idx]));
+ }
/** @todo see if pdfDoc and res can be linked here,
(currently res <> PDFDocument's resources) so addFonts()
@@ -1061,6 +1114,124 @@ public class PDFGraphics2D extends AbstractGraphics2D {
// ignore exception, will be thrown again later
}
}
+ return true;
+ }
+
+ protected boolean applyUnknownPaint(Paint paint, Shape shape) {
+ preparePainting();
+
+ Shape clip = getClip();
+ Rectangle2D usrClipBounds, usrBounds;
+ usrBounds = shape.getBounds2D();
+ usrClipBounds = clip.getBounds2D();
+ if (!usrClipBounds.intersects(usrBounds)) {
+ return true;
+ }
+ Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
+ double usrX = usrBounds.getX();
+ double usrY = usrBounds.getY();
+ double usrW = usrBounds.getWidth();
+ double usrH = usrBounds.getHeight();
+
+ Rectangle devShapeBounds, devClipBounds, devBounds;
+ AffineTransform at = getTransform();
+ devShapeBounds = at.createTransformedShape(shape).getBounds();
+ devClipBounds = at.createTransformedShape(clip).getBounds();
+ if (!devClipBounds.intersects(devShapeBounds)) {
+ return true;
+ }
+ devBounds = devShapeBounds.intersection(devClipBounds);
+ int devX = devBounds.x;
+ int devY = devBounds.y;
+ int devW = devBounds.width;
+ int devH = devBounds.height;
+
+ ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ ColorModel rgbCM = new DirectColorModel
+ (rgbCS, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
+ false, DataBuffer.TYPE_BYTE);
+
+ PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
+ at, getRenderingHints());
+ PDFXObject imageInfo = pdfDoc.getImage
+ ("TempImage:" + pctx.toString());
+ if (imageInfo != null) {
+ resourceContext.getPDFResources().addXObject(imageInfo);
+ } else {
+ Raster r = pctx.getRaster(devX, devY, devW, devH);
+ WritableRaster wr = (WritableRaster)r;
+ wr = wr.createWritableTranslatedChild(0, 0);
+
+ ColorModel pcm = pctx.getColorModel();
+ BufferedImage bi = new BufferedImage
+ (pcm, wr, pcm.isAlphaPremultiplied(), null);
+ final byte[] rgb = new byte[devW * devH * 3];
+ final int[] line = new int[devW];
+ final byte[] mask;
+ int x, y, val, rgbIdx = 0;
+
+ if (pcm.hasAlpha()) {
+ mask = new byte[devW * devH];
+ int maskIdx = 0;
+ for (y = 0; y < devH; y++) {
+ bi.getRGB(0, y, devW, 1, line, 0, devW);
+ for (x = 0; x < devW; x++) {
+ val = line[x];
+ mask[maskIdx++] = (byte)(val >>> 24);
+ rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
+ rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
+ rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
+ }
+ }
+ } else {
+ mask = null;
+ for (y = 0; y < devH; y++) {
+ bi.getRGB(0, y, devW, 1, line, 0, devW);
+ for (x = 0; x < devW; x++) {
+ val = line[x];
+ rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
+ rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
+ rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
+ }
+ }
+ }
+
+ String maskRef = null;
+ if (mask != null) {
+ BitmapImage fopimg = new BitmapImage
+ ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
+ fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
+ PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
+ maskRef = xobj.referencePDF();
+
+ if (outputStream != null) {
+ try {
+ this.pdfDoc.output(outputStream);
+ } catch (IOException ioe) {
+ // ignore exception, will be thrown again later
+ }
+ }
+ }
+ BitmapImage fopimg;
+ fopimg = new BitmapImage("TempImage:" + pctx.toString(),
+ devW, devH, rgb, maskRef);
+ fopimg.setTransparent(new PDFColor(255, 255, 255));
+ imageInfo = pdfDoc.addImage(resourceContext, fopimg);
+ if (outputStream != null) {
+ try {
+ this.pdfDoc.output(outputStream);
+ } catch (IOException ioe) {
+ // ignore exception, will be thrown again later
+ }
+ }
+ }
+
+ currentStream.write("q\n");
+ writeClip(shape);
+ currentStream.write("" + usrW + " 0 0 " + (-usrH) + " " + usrX
+ + " " + (usrY + usrH) + " cm\n" + "/Im"
+ + imageInfo.getXNumber() + " Do\nQ\n");
+ return true;
}
/**
@@ -1512,7 +1683,16 @@ public class PDFGraphics2D extends AbstractGraphics2D {
Paint paint = getPaint();
if (graphicsState.setPaint(paint)) {
- applyPaint(paint, true);
+ if (!applyPaint(paint, true)) {
+ // Use the shape to 'clip' the paint contents.
+ applyUnknownPaint(paint, s);
+
+ if (newClip || newTransform) {
+ currentStream.write("Q\n");
+ graphicsState.pop();
+ }
+ return;
+ }
}
//PathIterator iter = s.getPathIterator(getTransform());