/* * 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.svg; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Rectangle2D; import org.w3c.dom.Element; import org.w3c.dom.svg.SVGDocument; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.SVGImageElementBridge; import org.apache.batik.gvt.AbstractGraphicsNode; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.util.ParsedURL; import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; import org.apache.fop.image.loader.batik.BatikUtil; /** * <p>Bridge class for the <image> element when jpeg images.</p> * * <p>This work was authored by Keiron Liddle (keiron@aftexsw.com).</p> */ public abstract class AbstractFOPImageElementBridge extends SVGImageElementBridge { /** * Constructs a new bridge for the <image> element. */ public AbstractFOPImageElementBridge() { } /** * Create the raster image node. * THis checks if it is a jpeg file and creates a jpeg node * so the jpeg can be inserted directly into the pdf document. * @param ctx the bridge context * @param imageElement the svg element for the image * @param purl the parsed url for the image resource * @return a new graphics node */ @Override protected GraphicsNode createImageGraphicsNode( BridgeContext ctx, Element imageElement, ParsedURL purl) { AbstractFOPBridgeContext bridgeCtx = (AbstractFOPBridgeContext)ctx; ImageManager manager = bridgeCtx.getImageManager(); ImageSessionContext sessionContext = bridgeCtx.getImageSessionContext(); try { ImageInfo info = manager.getImageInfo(purl.toString(), sessionContext); ImageFlavor[] supportedFlavors = getSupportedFlavours(); Image image = manager.getImage(info, supportedFlavors, sessionContext); //TODO color profile overrides aren't handled, yet! //ICCColorSpaceExt colorspaceOverride = extractColorSpace(e, ctx); AbstractGraphicsNode specializedNode = null; if (image instanceof ImageXMLDOM) { ImageXMLDOM xmlImage = (ImageXMLDOM)image; if (xmlImage.getDocument() instanceof SVGDocument) { //Clone DOM because the Batik's CSS Parser attaches to the DOM and is therefore //not thread-safe. SVGDocument clonedDoc = (SVGDocument)BatikUtil.cloneSVGDocument( xmlImage.getDocument()); return createSVGImageNode(ctx, imageElement, clonedDoc); } else { //Convert image to Graphics2D image = manager.convertImage(xmlImage, new ImageFlavor[] {ImageFlavor.GRAPHICS2D}); } } if (image instanceof ImageRawJPEG) { specializedNode = createLoaderImageNode(image, ctx, imageElement, purl); } else if (image instanceof ImageRawCCITTFax) { specializedNode = createLoaderImageNode(image, ctx, imageElement, purl); } else if (image instanceof ImageGraphics2D) { ImageGraphics2D g2dImage = (ImageGraphics2D)image; specializedNode = new Graphics2DNode(g2dImage); } else { ctx.getUserAgent().displayError( new ImageException("Cannot convert an image to a usable format: " + purl)); } if (specializedNode != null) { Rectangle2D imgBounds = getImageBounds(ctx, imageElement); Rectangle2D bounds = specializedNode.getPrimitiveBounds(); float [] vb = new float[4]; vb[0] = 0; // x vb[1] = 0; // y vb[2] = (float) bounds.getWidth(); // width vb[3] = (float) bounds.getHeight(); // height // handles the 'preserveAspectRatio', 'overflow' and 'clip' // and sets the appropriate AffineTransform to the image node initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds); return specializedNode; } } catch (Exception e) { ctx.getUserAgent().displayError(e); } //Fallback return superCreateGraphicsNode(ctx, imageElement, purl); } /** * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode. * @param ctx the bridge context * @param imageElement the image element * @param purl the parsed URL * @return the newly created graphics node * @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element) */ protected GraphicsNode superCreateGraphicsNode( BridgeContext ctx, Element imageElement, ParsedURL purl) { return super.createImageGraphicsNode(ctx, imageElement, purl); } /** * Returns an array of supported image flavours * * @return an array of supported image flavours */ protected abstract ImageFlavor[] getSupportedFlavours(); /** * Creates a loader image node implementation * @param purl the parsed url * @param imageElement the image element * @param ctx the batik bridge context * @param image the image * * @return a loader image node implementation */ protected LoaderImageNode createLoaderImageNode( Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) { return new LoaderImageNode(image, ctx, imageElement, purl); } /** * An image node for natively handled Image instance. * This holds a natively handled image so that it can be drawn into * the PDFGraphics2D. */ public class LoaderImageNode extends AbstractGraphicsNode { /** image */ protected final Image image; /** bridge context */ protected final BridgeContext ctx; /** image element */ protected final Element imageElement; /** parsed url */ protected final ParsedURL purl; /** original graphics mode */ protected GraphicsNode origGraphicsNode; /** * Create a new image node for drawing natively handled images * into PDF graphics. * @param image the JPEG image * @param ctx the bridge context * @param imageElement the SVG image element * @param purl the URL to the image */ public LoaderImageNode(Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) { this.image = image; this.ctx = ctx; this.imageElement = imageElement; this.purl = purl; } /** {@inheritDoc} */ public Shape getOutline() { return getPrimitiveBounds(); } /** {@inheritDoc} */ public void primitivePaint(Graphics2D g2d) { if (g2d instanceof NativeImageHandler) { NativeImageHandler nativeImageHandler = (NativeImageHandler) g2d; float x = 0; float y = 0; try { float width = image.getSize().getWidthPx(); float height = image.getSize().getHeightPx(); nativeImageHandler.addNativeImage(image, x, y, width, height); } catch (Exception e) { ctx.getUserAgent().displayError(e); } } else { // Not going directly into PDF so use // original implementation so filters etc work. if (origGraphicsNode == null) { // Haven't constructed base class Graphics Node, // so do so now. origGraphicsNode = superCreateGraphicsNode(ctx, imageElement, purl); } origGraphicsNode.primitivePaint(g2d); } } /** {@inheritDoc} */ public Rectangle2D getGeometryBounds() { return getPrimitiveBounds(); } /** {@inheritDoc} */ public Rectangle2D getPrimitiveBounds() { return new Rectangle2D.Double(0, 0, image.getSize().getWidthPx(), image.getSize().getHeightPx()); } /** {@inheritDoc} */ public Rectangle2D getSensitiveBounds() { //No interactive features, just return primitive bounds return getPrimitiveBounds(); } } /** * A node that holds a Graphics2D image. */ public static class Graphics2DNode extends AbstractGraphicsNode { private final ImageGraphics2D image; /** * Create a new Graphics2D node. * @param g2d the Graphics2D image */ public Graphics2DNode(ImageGraphics2D g2d) { this.image = g2d; } /** {@inheritDoc} */ public Shape getOutline() { return getPrimitiveBounds(); } /** {@inheritDoc} */ public void primitivePaint(Graphics2D g2d) { int width = image.getSize().getWidthPx(); int height = image.getSize().getHeightPx(); Rectangle2D area = new Rectangle2D.Double(0, 0, width, height); Graphics2DImagePainter painter = image.getGraphics2DImagePainter(); painter.paint(g2d, area); } /** {@inheritDoc} */ public Rectangle2D getGeometryBounds() { return getPrimitiveBounds(); } /** {@inheritDoc} */ public Rectangle2D getPrimitiveBounds() { return new Rectangle2D.Double(0, 0, image.getSize().getWidthPx(), image.getSize().getHeightPx()); } /** {@inheritDoc} */ public Rectangle2D getSensitiveBounds() { //No interactive features, just return primitive bounds return getPrimitiveBounds(); } } }