org.apache.fop.render.pdf.pdfbox.PSPDFGraphics2D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fop-pdf-images Show documentation
Show all versions of fop-pdf-images Show documentation
Apache FOP PDF Images plug-in extends FOP in order to add support for using PDF images in fo:external-graphic elements when generating PDF files.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.render.pdf.pdfbox;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.ImageObserver;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.pdmodel.common.function.PDFunction;
import org.apache.pdfbox.pdmodel.common.function.PDFunctionType0;
import org.apache.pdfbox.pdmodel.common.function.PDFunctionType2;
import org.apache.pdfbox.pdmodel.common.function.PDFunctionType3;
import org.apache.pdfbox.pdmodel.graphics.shading.AxialShadingContext;
import org.apache.pdfbox.pdmodel.graphics.shading.AxialShadingPaint;
import org.apache.pdfbox.pdmodel.graphics.shading.RadialShadingContext;
import org.apache.pdfbox.pdmodel.graphics.shading.RadialShadingPaint;
import org.apache.pdfbox.util.Matrix;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.io.TempResourceURIGenerator;
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSResource;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.render.gradient.Function;
import org.apache.fop.render.gradient.GradientMaker;
import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
import org.apache.fop.render.gradient.Pattern;
import org.apache.fop.render.gradient.Shading;
import org.apache.fop.render.ps.Gradient;
import org.apache.fop.render.ps.PSDocumentHandler;
import org.apache.fop.render.ps.PSImageUtils;
public class PSPDFGraphics2D extends PSGraphics2D {
private boolean clearRect;
public PSPDFGraphics2D(boolean textAsShapes) {
super(textAsShapes);
}
public PSPDFGraphics2D(PSGraphics2D g) {
super(g);
}
public PSPDFGraphics2D(boolean textAsShapes, PSGenerator gen) {
super(textAsShapes, gen);
}
public void clearRect(int x, int y, int width, int height) {
if (clearRect) {
super.clearRect(x, y, width, height);
}
clearRect = true;
}
private final GradientMaker.DoubleFormatter doubleFormatter = new DoubleFormatter() {
public String formatDouble(double d) {
return getPSGenerator().formatDouble(d);
}
};
protected void applyPaint(Paint paint, boolean fill) {
preparePainting();
if (paint instanceof AxialShadingPaint || paint instanceof RadialShadingPaint) {
PaintContext paintContext = paint.createContext(null, new Rectangle(), null, new AffineTransform(),
getRenderingHints());
int deviceColorSpace = PDFDeviceColorSpace.DEVICE_RGB;
if (paint instanceof AxialShadingPaint) {
try {
AxialShadingContext asc = (AxialShadingContext) paintContext;
float[] fCoords = asc.getCoords();
transformCoords(fCoords, paint, true);
PDFunction function = asc.getFunction();
Function targetFT = getFunction(function);
if (targetFT != null) {
if (targetFT.getFunctions().size() == 5
&& targetFT.getFunctions().get(0).getFunctionType() == 0) {
return;
}
List dCoords = floatArrayToDoubleList(fCoords);
PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(deviceColorSpace);
Shading shading = new Shading(2, colSpace, dCoords, targetFT);
Pattern pattern = new Pattern(2, shading, null);
gen.write(Gradient.outputPattern(pattern, doubleFormatter));
}
} catch (IOException ioe) {
handleIOException(ioe);
}
} else if (paint instanceof RadialShadingPaint) {
try {
RadialShadingContext rsc = (RadialShadingContext) paintContext;
float[] fCoords = rsc.getCoords();
transformCoords(fCoords, paint, false);
PDFunction function = rsc.getFunction();
Function targetFT3 = getFunction(function);
List dCoords = floatArrayToDoubleList(fCoords);
PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(deviceColorSpace);
Shading shading = new Shading(3, colSpace, dCoords, targetFT3);
Pattern pattern = new Pattern(2, shading, null);
gen.write(Gradient.outputPattern(pattern, doubleFormatter));
} catch (IOException ioe) {
handleIOException(ioe);
}
}
}
}
private void transformCoords(float[] coords, Paint paint, boolean axialShading) {
try {
Field f = paint.getClass().getDeclaredField("matrix");
f.setAccessible(true);
Matrix ctm = (Matrix) f.get(paint);
AffineTransform at = ctm.createAffineTransform();
if (axialShading) {
at.transform(coords, 0, coords, 0, 2);
} else {
at.transform(coords, 0, coords, 0, 1);
at.transform(coords, 3, coords, 3, 1);
coords[2] *= ctm.getScalingFactorX();
coords[5] *= ctm.getScalingFactorX();
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static Function getFunction(PDFunction f) throws IOException {
if (f instanceof PDFunctionType3) {
PDFunctionType3 sourceFT3 = (PDFunctionType3) f;
float[] bounds = sourceFT3.getBounds().toFloatArray();
COSArray sourceFunctions = sourceFT3.getFunctions();
List targetFunctions = new ArrayList();
for (int j = 0; j < sourceFunctions.size(); j++) {
targetFunctions.add(getFunction(PDFunction.create(sourceFunctions.get(j))));
}
return new Function(null, null, targetFunctions, toList(bounds), null);
} else if (f instanceof PDFunctionType2) {
PDFunctionType2 sourceFT2 = (PDFunctionType2) f;
double interpolation = (double)sourceFT2.getN();
float[] c0 = sourceFT2.getC0().toFloatArray();
float[] c1 = sourceFT2.getC1().toFloatArray();
return new Function(null, null, c0, c1, interpolation);
} else if (f instanceof PDFunctionType0) {
COSDictionary s = f.getCOSObject();
assert s instanceof COSStream;
COSStream stream = (COSStream) s;
COSArray encode = (COSArray) s.getDictionaryObject(COSName.ENCODE);
COSArray domain = (COSArray) s.getDictionaryObject(COSName.DOMAIN);
COSArray range = (COSArray) s.getDictionaryObject(COSName.RANGE);
int bits = ((COSInteger)s.getDictionaryObject(COSName.BITS_PER_SAMPLE)).intValue();
COSArray size = (COSArray) s.getDictionaryObject(COSName.SIZE);
byte[] x = IOUtils.toByteArray(stream.getUnfilteredStream());
for (byte y : x) {
if (y != 0) {
return new Function(floatArrayToDoubleList(domain.toFloatArray()),
floatArrayToDoubleList(range.toFloatArray()),
floatArrayToDoubleList(encode.toFloatArray()),
x,
bits,
toList(size)
);
}
}
return null;
}
throw new IOException("Unsupported " + f.toString());
}
private static List toList(float[] array) {
List list = new ArrayList(array.length);
for (float f : array) {
list.add(f);
}
return list;
}
private static List toList(COSArray array) {
List list = new ArrayList();
for (COSBase i : array) {
list.add(((COSInteger)i).intValue());
}
return list;
}
private static List floatArrayToDoubleList(float[] floatArray) {
List doubleList = new ArrayList();
for (float f : floatArray) {
doubleList.add((double) f);
}
return doubleList;
}
@Override
public boolean drawImage(Image img, int x1, int y1, ImageObserver observer) {
PSGenerator tmp = gen;
if (gen instanceof PSDocumentHandler.FOPPSGenerator) {
PSDocumentHandler.FOPPSGenerator fopGen = (PSDocumentHandler.FOPPSGenerator)tmp;
PSDocumentHandler handler = fopGen.getHandler();
if (handler.getPSUtil().isOptimizeResources()) {
try {
final int width = img.getWidth(observer);
final int height = img.getHeight(observer);
if (width == -1 || height == -1) {
return false;
}
BufferedImage buf = getImage(width, height, img, observer);
if (buf == null) {
return false;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataBufferInt db = (DataBufferInt) buf.getRaster().getDataBuffer();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(width);
dos.writeInt(height);
for (int i : db.getData()) {
dos.writeInt(i);
}
String format = DataBufferInt.class.getName();
int hash = Arrays.hashCode(bos.toByteArray());
URI uri = fopGen.getImages().get(hash);
if (uri == null) {
uri = new TempResourceURIGenerator("img" + hash + "." + format).generate();
fopGen.getImages().put(hash, uri);
BufferedOutputStream outputStream = fopGen.getTempStream(uri);
outputStream.write(bos.toByteArray());
outputStream.close();
}
PSResource form = handler.getFormForImage(uri.toASCIIString());
ImageInfo info = new ImageInfo(uri.toASCIIString(), "image/" + format);
ImageSize size = new ImageSize(width, height, handler.getUserAgent().getTargetResolution());
size.calcSizeFromPixels();
info.setSize(size);
float res = handler.getUserAgent().getSourceResolution() / 72;
Rectangle rect =
new Rectangle(0, 0, (int)(size.getWidthMpt() * res), (int)(size.getHeightMpt() * res));
gen.saveGraphicsState();
gen.concatMatrix(getTransform());
writeClip(getClip());
PSImageUtils.drawForm(form, info, rect, gen);
gen.restoreGraphicsState();
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
}
return super.drawImage(img, x1, y1, observer);
}
private BufferedImage getImage(int width, int height, Image img, ImageObserver observer) {
Dimension size = new Dimension(width, height);
BufferedImage buf = buildBufferedImage(size);
Graphics2D g = buf.createGraphics();
g.setComposite(AlphaComposite.SrcOver);
g.setBackground(new Color(1, 1, 1, 0));
g.fillRect(0, 0, width, height);
g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
if (!g.drawImage(img, 0, 0, observer)) {
return null;
}
g.dispose();
return buf;
}
}