Переглянути джерело

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
tags/fop-0_90-alpha1
Jeremias Maerki 18 роки тому
джерело
коміт
20fa5268df
1 змінених файлів з 210 додано та 30 видалено
  1. 210
    30
      src/java/org/apache/fop/svg/PDFGraphics2D.java

+ 210
- 30
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());

Завантаження…
Відмінити
Зберегти