/*
 * 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: AbstractFOPImageElementBridge.java 1805173 2017-08-16 10:50:04Z ssteiner $ */

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;

Bridge class for the <image> element when jpeg images. This work was originally authored by Keiron Liddle
/** * Bridge class for the &lt;image&gt; element when jpeg images. * * This work was originally authored by <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> */
public abstract class AbstractFOPImageElementBridge extends SVGImageElementBridge {
Constructs a new bridge for the <image> element.
/** * Constructs a new bridge for the &lt;image&gt; 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.
Params:
  • ctx – the bridge context
  • imageElement – the svg element for the image
  • purl – the parsed url for the image resource
Returns:a new graphics node
/** * 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.
Params:
  • ctx – the bridge context
  • imageElement – the image element
  • purl – the parsed URL
See Also:
Returns:the newly created graphics node
/** * 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
Returns:an array of supported image flavours
/** * Returns an array of supported image flavours * * @return an array of supported image flavours */
protected abstract ImageFlavor[] getSupportedFlavours();
Creates a loader image node implementation
Params:
  • purl – the parsed url
  • imageElement – the image element
  • ctx – the batik bridge context
  • image – the image
Returns:a loader image node implementation
/** * 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.
/** * 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
/** image */
protected final Image image;
bridge context
/** bridge context */
protected final BridgeContext ctx;
image element
/** image element */
protected final Element imageElement;
parsed url
/** parsed url */
protected final ParsedURL purl;
original graphics mode
/** original graphics mode */
protected GraphicsNode origGraphicsNode;
Create a new image node for drawing natively handled images into PDF graphics.
Params:
  • image – the JPEG image
  • ctx – the bridge context
  • imageElement – the SVG image element
  • purl – the URL to the image
/** * 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}
/** {@inheritDoc} */
public Shape getOutline() { return getPrimitiveBounds(); }
{@inheritDoc}
/** {@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}
/** {@inheritDoc} */
public Rectangle2D getGeometryBounds() { return getPrimitiveBounds(); }
{@inheritDoc}
/** {@inheritDoc} */
public Rectangle2D getPrimitiveBounds() { return new Rectangle2D.Double(0, 0, image.getSize().getWidthPx(), image.getSize().getHeightPx()); }
{@inheritDoc}
/** {@inheritDoc} */
public Rectangle2D getSensitiveBounds() { //No interactive features, just return primitive bounds return getPrimitiveBounds(); } }
A node that holds a Graphics2D image.
/** * A node that holds a Graphics2D image. */
public static class Graphics2DNode extends AbstractGraphicsNode { private final ImageGraphics2D image;
Create a new Graphics2D node.
Params:
  • g2d – the Graphics2D image
/** * Create a new Graphics2D node. * @param g2d the Graphics2D image */
public Graphics2DNode(ImageGraphics2D g2d) { this.image = g2d; }
{@inheritDoc}
/** {@inheritDoc} */
public Shape getOutline() { return getPrimitiveBounds(); }
{@inheritDoc}
/** {@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}
/** {@inheritDoc} */
public Rectangle2D getGeometryBounds() { return getPrimitiveBounds(); }
{@inheritDoc}
/** {@inheritDoc} */
public Rectangle2D getPrimitiveBounds() { return new Rectangle2D.Double(0, 0, image.getSize().getWidthPx(), image.getSize().getHeightPx()); }
{@inheritDoc}
/** {@inheritDoc} */
public Rectangle2D getSensitiveBounds() { //No interactive features, just return primitive bounds return getPrimitiveBounds(); } } }