--- src/java/org/apache/fop/svg/PDFGraphics2D.java (revision 330229) +++ src/java/org/apache/fop/svg/PDFGraphics2D.java (working copy) @@ -45,6 +45,7 @@ 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,17 +62,23 @@ import java.awt.Shape; import java.awt.Stroke; import java.awt.Paint; +import java.awt.PaintContext; import java.awt.Rectangle; +import java.awt.Transparency; 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; @@ -129,7 +136,7 @@ * 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 +383,11 @@ 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 +715,18 @@ 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 +848,28 @@ * @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 repeate 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; @@ -885,6 +911,8 @@ 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 +928,22 @@ 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()); @@ -941,6 +982,8 @@ 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 +1003,16 @@ 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(); @@ -1061,8 +1107,124 @@ // 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>>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>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; + } + /** * Apply the stroke to the PDF. * This takes the java stroke and outputs the appropriate settings @@ -1512,7 +1674,16 @@ 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());