| /* |
| * 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.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.color.PDColorSpace; |
| import org.apache.pdfbox.pdmodel.graphics.pattern.TilingPaint; |
| 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.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 { |
| |
| public PSPDFGraphics2D(boolean textAsShapes) { |
| super(textAsShapes); |
| } |
| |
| public PSPDFGraphics2D(PSGraphics2D g) { |
| super(g); |
| } |
| |
| public PSPDFGraphics2D(boolean textAsShapes, PSGenerator gen) { |
| super(textAsShapes, gen); |
| } |
| |
| 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, null, null, new AffineTransform(), |
| getRenderingHints()); |
| PDColorSpace pdcs; |
| int deviceColorSpace = PDFDeviceColorSpace.DEVICE_RGB; |
| if (paint instanceof AxialShadingPaint) { |
| try { |
| AxialShadingContext asc = (AxialShadingContext) paintContext; |
| float[] fCoords = asc.getCoords(); |
| PDFunction function = asc.getFunction(); |
| Function targetFT = getFunction(function); |
| if (targetFT != null) { |
| if (targetFT.getFunctions().size() == 5 |
| && targetFT.getFunctions().get(0).getFunctionType() == 0) { |
| return; |
| } |
| List<Double> 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(); |
| PDFunction function = rsc.getFunction(); |
| Function targetFT3 = getFunction(function); |
| List<Double> 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); |
| } |
| } |
| } |
| if (paint instanceof TilingPaint) { |
| super.applyPaint(paint, fill); |
| } |
| } |
| |
| 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<Function> targetFunctions = new ArrayList<Function>(); |
| 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.getDictionary(); |
| 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<Float> toList(float[] array) { |
| List<Float> list = new ArrayList<Float>(array.length); |
| for (float f : array) { |
| list.add(f); |
| } |
| return list; |
| } |
| |
| private static List<Integer> toList(COSArray array) { |
| List<Integer> list = new ArrayList<Integer>(); |
| for (COSBase i : array) { |
| list.add(((COSInteger)i).intValue()); |
| } |
| return list; |
| } |
| |
| private static List<Double> floatArrayToDoubleList(float[] floatArray) { |
| List<Double> doubleList = new ArrayList<Double>(); |
| 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; |
| } |
| } |