You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractFOPImageElementBridge.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.svg;
  19. import java.awt.Graphics2D;
  20. import java.awt.Shape;
  21. import java.awt.geom.Rectangle2D;
  22. import org.w3c.dom.Element;
  23. import org.w3c.dom.svg.SVGDocument;
  24. import org.apache.batik.bridge.BridgeContext;
  25. import org.apache.batik.bridge.SVGImageElementBridge;
  26. import org.apache.batik.gvt.AbstractGraphicsNode;
  27. import org.apache.batik.gvt.GraphicsNode;
  28. import org.apache.batik.util.ParsedURL;
  29. import org.apache.xmlgraphics.image.loader.Image;
  30. import org.apache.xmlgraphics.image.loader.ImageException;
  31. import org.apache.xmlgraphics.image.loader.ImageFlavor;
  32. import org.apache.xmlgraphics.image.loader.ImageInfo;
  33. import org.apache.xmlgraphics.image.loader.ImageManager;
  34. import org.apache.xmlgraphics.image.loader.ImageSessionContext;
  35. import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
  36. import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
  37. import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
  38. import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
  39. import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
  40. import org.apache.fop.image.loader.batik.BatikUtil;
  41. /**
  42. * Bridge class for the <image> element when jpeg images.
  43. *
  44. * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  45. */
  46. public abstract class AbstractFOPImageElementBridge extends SVGImageElementBridge {
  47. /**
  48. * Constructs a new bridge for the &lt;image> element.
  49. */
  50. public AbstractFOPImageElementBridge() { }
  51. /**
  52. * Create the raster image node.
  53. * THis checks if it is a jpeg file and creates a jpeg node
  54. * so the jpeg can be inserted directly into the pdf document.
  55. * @param ctx the bridge context
  56. * @param imageElement the svg element for the image
  57. * @param purl the parsed url for the image resource
  58. * @return a new graphics node
  59. */
  60. @Override
  61. protected GraphicsNode createImageGraphicsNode
  62. (BridgeContext ctx, Element imageElement, ParsedURL purl) {
  63. AbstractFOPBridgeContext bridgeCtx = (AbstractFOPBridgeContext)ctx;
  64. ImageManager manager = bridgeCtx.getImageManager();
  65. ImageSessionContext sessionContext = bridgeCtx.getImageSessionContext();
  66. try {
  67. ImageInfo info = manager.getImageInfo(purl.toString(), sessionContext);
  68. ImageFlavor[] supportedFlavors = getSupportedFlavours();
  69. Image image = manager.getImage(info, supportedFlavors, sessionContext);
  70. //TODO color profile overrides aren't handled, yet!
  71. //ICCColorSpaceExt colorspaceOverride = extractColorSpace(e, ctx);
  72. AbstractGraphicsNode specializedNode = null;
  73. if (image instanceof ImageXMLDOM) {
  74. ImageXMLDOM xmlImage = (ImageXMLDOM)image;
  75. if (xmlImage.getDocument() instanceof SVGDocument) {
  76. //Clone DOM because the Batik's CSS Parser attaches to the DOM and is therefore
  77. //not thread-safe.
  78. SVGDocument clonedDoc = (SVGDocument)BatikUtil.cloneSVGDocument(
  79. xmlImage.getDocument());
  80. return createSVGImageNode(ctx, imageElement, clonedDoc);
  81. } else {
  82. //Convert image to Graphics2D
  83. image = manager.convertImage(xmlImage,
  84. new ImageFlavor[] {ImageFlavor.GRAPHICS2D});
  85. }
  86. }
  87. if (image instanceof ImageRawJPEG) {
  88. specializedNode = createLoaderImageNode(image, ctx, imageElement, purl);
  89. } else if (image instanceof ImageRawCCITTFax) {
  90. specializedNode = createLoaderImageNode(image, ctx, imageElement, purl);
  91. } else if (image instanceof ImageGraphics2D) {
  92. ImageGraphics2D g2dImage = (ImageGraphics2D)image;
  93. specializedNode = new Graphics2DNode(g2dImage);
  94. } else {
  95. ctx.getUserAgent().displayError(
  96. new ImageException("Cannot convert an image to a usable format: " + purl));
  97. }
  98. if (specializedNode != null) {
  99. Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
  100. Rectangle2D bounds = specializedNode.getPrimitiveBounds();
  101. float [] vb = new float[4];
  102. vb[0] = 0; // x
  103. vb[1] = 0; // y
  104. vb[2] = (float) bounds.getWidth(); // width
  105. vb[3] = (float) bounds.getHeight(); // height
  106. // handles the 'preserveAspectRatio', 'overflow' and 'clip'
  107. // and sets the appropriate AffineTransform to the image node
  108. initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds);
  109. return specializedNode;
  110. }
  111. } catch (Exception e) {
  112. ctx.getUserAgent().displayError(e);
  113. }
  114. //Fallback
  115. return superCreateGraphicsNode(ctx, imageElement, purl);
  116. }
  117. /**
  118. * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode.
  119. * @param ctx the bridge context
  120. * @param imageElement the image element
  121. * @param purl the parsed URL
  122. * @return the newly created graphics node
  123. * @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element)
  124. */
  125. protected GraphicsNode superCreateGraphicsNode
  126. (BridgeContext ctx, Element imageElement, ParsedURL purl) {
  127. return super.createImageGraphicsNode(ctx, imageElement, purl);
  128. }
  129. /**
  130. * Returns an array of supported image flavours
  131. *
  132. * @return an array of supported image flavours
  133. */
  134. protected abstract ImageFlavor[] getSupportedFlavours();
  135. /**
  136. * Creates a loader image node implementation
  137. * @param purl the parsed url
  138. * @param imageElement the image element
  139. * @param ctx the batik bridge context
  140. * @param image the image
  141. *
  142. * @return a loader image node implementation
  143. */
  144. protected LoaderImageNode createLoaderImageNode(
  145. Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) {
  146. return new LoaderImageNode(image, ctx, imageElement, purl);
  147. }
  148. /**
  149. * An image node for natively handled Image instance.
  150. * This holds a natively handled image so that it can be drawn into
  151. * the PDFGraphics2D.
  152. */
  153. public class LoaderImageNode extends AbstractGraphicsNode {
  154. /** image */
  155. protected final Image image;
  156. /** bridge context */
  157. protected final BridgeContext ctx;
  158. /** image element */
  159. protected final Element imageElement;
  160. /** parsed url */
  161. protected final ParsedURL purl;
  162. /** original graphics mode */
  163. protected GraphicsNode origGraphicsNode = null;
  164. /**
  165. * Create a new image node for drawing natively handled images
  166. * into PDF graphics.
  167. * @param image the JPEG image
  168. * @param ctx the bridge context
  169. * @param imageElement the SVG image element
  170. * @param purl the URL to the image
  171. */
  172. public LoaderImageNode(Image image, BridgeContext ctx,
  173. Element imageElement, ParsedURL purl) {
  174. this.image = image;
  175. this.ctx = ctx;
  176. this.imageElement = imageElement;
  177. this.purl = purl;
  178. }
  179. /** {@inheritDoc} */
  180. public Shape getOutline() {
  181. return getPrimitiveBounds();
  182. }
  183. /** {@inheritDoc} */
  184. public void primitivePaint(Graphics2D g2d) {
  185. if (g2d instanceof NativeImageHandler) {
  186. NativeImageHandler nativeImageHandler = (NativeImageHandler) g2d;
  187. float x = 0;
  188. float y = 0;
  189. try {
  190. float width = image.getSize().getWidthPx();
  191. float height = image.getSize().getHeightPx();
  192. nativeImageHandler.addNativeImage(image, x, y, width, height);
  193. } catch (Exception e) {
  194. ctx.getUserAgent().displayError(e);
  195. }
  196. } else {
  197. // Not going directly into PDF so use
  198. // original implementation so filters etc work.
  199. if (origGraphicsNode == null) {
  200. // Haven't constructed base class Graphics Node,
  201. // so do so now.
  202. origGraphicsNode
  203. = superCreateGraphicsNode(ctx, imageElement, purl);
  204. }
  205. origGraphicsNode.primitivePaint(g2d);
  206. }
  207. }
  208. /** {@inheritDoc} */
  209. public Rectangle2D getGeometryBounds() {
  210. return getPrimitiveBounds();
  211. }
  212. /** {@inheritDoc} */
  213. public Rectangle2D getPrimitiveBounds() {
  214. return new Rectangle2D.Double(0, 0,
  215. image.getSize().getWidthPx(),
  216. image.getSize().getHeightPx());
  217. }
  218. /** {@inheritDoc} */
  219. public Rectangle2D getSensitiveBounds() {
  220. //No interactive features, just return primitive bounds
  221. return getPrimitiveBounds();
  222. }
  223. }
  224. /**
  225. * A node that holds a Graphics2D image.
  226. */
  227. public static class Graphics2DNode extends AbstractGraphicsNode {
  228. private final ImageGraphics2D image;
  229. /**
  230. * Create a new Graphics2D node.
  231. * @param g2d the Graphics2D image
  232. */
  233. public Graphics2DNode(ImageGraphics2D g2d) {
  234. this.image = g2d;
  235. }
  236. /** {@inheritDoc} */
  237. public Shape getOutline() {
  238. return getPrimitiveBounds();
  239. }
  240. /** {@inheritDoc} */
  241. public void primitivePaint(Graphics2D g2d) {
  242. int width = image.getSize().getWidthPx();
  243. int height = image.getSize().getHeightPx();
  244. Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);
  245. Graphics2DImagePainter painter = image.getGraphics2DImagePainter();
  246. painter.paint(g2d, area);
  247. }
  248. /** {@inheritDoc} */
  249. public Rectangle2D getGeometryBounds() {
  250. return getPrimitiveBounds();
  251. }
  252. /** {@inheritDoc} */
  253. public Rectangle2D getPrimitiveBounds() {
  254. return new Rectangle2D.Double(0, 0,
  255. image.getSize().getWidthPx(),
  256. image.getSize().getHeightPx());
  257. }
  258. /** {@inheritDoc} */
  259. public Rectangle2D getSensitiveBounds() {
  260. //No interactive features, just return primitive bounds
  261. return getPrimitiveBounds();
  262. }
  263. }
  264. }