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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. //no 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. Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
  99. Rectangle2D bounds = specializedNode.getPrimitiveBounds();
  100. float [] vb = new float[4];
  101. vb[0] = 0; // x
  102. vb[1] = 0; // y
  103. vb[2] = (float) bounds.getWidth(); // width
  104. vb[3] = (float) bounds.getHeight(); // height
  105. // handles the 'preserveAspectRatio', 'overflow' and 'clip'
  106. // and sets the appropriate AffineTransform to the image node
  107. initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds);
  108. return specializedNode;
  109. } catch (Exception e) {
  110. ctx.getUserAgent().displayError(e);
  111. }
  112. return superCreateGraphicsNode(ctx, imageElement, purl);
  113. }
  114. /**
  115. * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode.
  116. * @param ctx the bridge context
  117. * @param imageElement the image element
  118. * @param purl the parsed URL
  119. * @return the newly created graphics node
  120. * @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element)
  121. */
  122. protected GraphicsNode superCreateGraphicsNode
  123. (BridgeContext ctx, Element imageElement, ParsedURL purl) {
  124. return super.createImageGraphicsNode(ctx, imageElement, purl);
  125. }
  126. /**
  127. * Returns an array of supported image flavours
  128. *
  129. * @return an array of supported image flavours
  130. */
  131. protected abstract ImageFlavor[] getSupportedFlavours();
  132. /**
  133. * Creates a loader image node implementation
  134. * @param purl the parsed url
  135. * @param imageElement the image element
  136. * @param ctx the batik bridge context
  137. * @param image the image
  138. *
  139. * @return a loader image node implementation
  140. */
  141. protected LoaderImageNode createLoaderImageNode(
  142. Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) {
  143. return new LoaderImageNode(image, ctx, imageElement, purl);
  144. }
  145. /**
  146. * An image node for natively handled Image instance.
  147. * This holds a natively handled image so that it can be drawn into
  148. * the PDFGraphics2D.
  149. */
  150. public class LoaderImageNode extends AbstractGraphicsNode {
  151. /** image */
  152. protected final Image image;
  153. /** bridge context */
  154. protected final BridgeContext ctx;
  155. /** image element */
  156. protected final Element imageElement;
  157. /** parsed url */
  158. protected final ParsedURL purl;
  159. /** original graphics mode */
  160. protected GraphicsNode origGraphicsNode = null;
  161. /**
  162. * Create a new image node for drawing natively handled images
  163. * into PDF graphics.
  164. * @param image the JPEG image
  165. * @param ctx the bridge context
  166. * @param imageElement the SVG image element
  167. * @param purl the URL to the image
  168. */
  169. public LoaderImageNode(Image image, BridgeContext ctx,
  170. Element imageElement, ParsedURL purl) {
  171. this.image = image;
  172. this.ctx = ctx;
  173. this.imageElement = imageElement;
  174. this.purl = purl;
  175. }
  176. /** {@inheritDoc} */
  177. public Shape getOutline() {
  178. return getPrimitiveBounds();
  179. }
  180. /** {@inheritDoc} */
  181. public void primitivePaint(Graphics2D g2d) {
  182. if (g2d instanceof NativeImageHandler) {
  183. NativeImageHandler nativeImageHandler = (NativeImageHandler) g2d;
  184. float x = 0;
  185. float y = 0;
  186. try {
  187. float width = image.getSize().getWidthPx();
  188. float height = image.getSize().getHeightPx();
  189. nativeImageHandler.addNativeImage(image, x, y, width, height);
  190. } catch (Exception e) {
  191. ctx.getUserAgent().displayError(e);
  192. }
  193. } else {
  194. // Not going directly into PDF so use
  195. // original implementation so filters etc work.
  196. if (origGraphicsNode == null) {
  197. // Haven't constructed base class Graphics Node,
  198. // so do so now.
  199. origGraphicsNode
  200. = superCreateGraphicsNode(ctx, imageElement, purl);
  201. }
  202. origGraphicsNode.primitivePaint(g2d);
  203. }
  204. }
  205. /** {@inheritDoc} */
  206. public Rectangle2D getGeometryBounds() {
  207. return getPrimitiveBounds();
  208. }
  209. /** {@inheritDoc} */
  210. public Rectangle2D getPrimitiveBounds() {
  211. return new Rectangle2D.Double(0, 0,
  212. image.getSize().getWidthPx(),
  213. image.getSize().getHeightPx());
  214. }
  215. /** {@inheritDoc} */
  216. public Rectangle2D getSensitiveBounds() {
  217. //No interactive features, just return primitive bounds
  218. return getPrimitiveBounds();
  219. }
  220. }
  221. /**
  222. * A node that holds a Graphics2D image.
  223. */
  224. public class Graphics2DNode extends AbstractGraphicsNode {
  225. private final ImageGraphics2D image;
  226. /**
  227. * Create a new Graphics2D node.
  228. * @param g2d the Graphics2D image
  229. */
  230. public Graphics2DNode(ImageGraphics2D g2d) {
  231. this.image = g2d;
  232. }
  233. /** {@inheritDoc} */
  234. public Shape getOutline() {
  235. return getPrimitiveBounds();
  236. }
  237. /** {@inheritDoc} */
  238. public void primitivePaint(Graphics2D g2d) {
  239. int width = image.getSize().getWidthPx();
  240. int height = image.getSize().getHeightPx();
  241. Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);
  242. Graphics2DImagePainter painter = image.getGraphics2DImagePainter();
  243. painter.paint(g2d, area);
  244. }
  245. /** {@inheritDoc} */
  246. public Rectangle2D getGeometryBounds() {
  247. return getPrimitiveBounds();
  248. }
  249. /** {@inheritDoc} */
  250. public Rectangle2D getPrimitiveBounds() {
  251. return new Rectangle2D.Double(0, 0,
  252. image.getSize().getWidthPx(),
  253. image.getSize().getHeightPx());
  254. }
  255. /** {@inheritDoc} */
  256. public Rectangle2D getSensitiveBounds() {
  257. //No interactive features, just return primitive bounds
  258. return getPrimitiveBounds();
  259. }
  260. }
  261. }