| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| 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. |
| --> |
| <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "http://forrest.apache.org/dtd/document-v20.dtd"> |
| <document> |
| <header> |
| <title>Image Loader Framework</title> |
| </header> |
| <body> |
| <section id="overview"> |
| <title>Overview</title> |
| <p> |
| Apache XML Graphics Commons contains a unified framework for loading and |
| processing images (bitmap and vector). The package name is |
| <code>org.apache.xmlgraphics.image.loader</code>. Key features: |
| </p> |
| <ul> |
| <li> |
| Unified basic API for all supported image types. |
| </li> |
| <li> |
| Image "Preloading": It allows automatic detection on the image |
| content type and can extract the intrinsic size (in pixels and length |
| units) of the image without loading the whole image into memory in |
| most cases. <a href="ext:xmlgraphics.apache.org/fop">Apache FOP</a> |
| uses this as it only needs the size of the image to do the layout. |
| The image is only fully read at the rendering stage. |
| </li> |
| <li> |
| Image conversion facility: Images can be converted into different |
| representations depending on the needs of the consumer. |
| </li> |
| <li> |
| Supported formats: All bitmap formats for which there are codecs |
| for the ImageIO API (like JPEG, PNG, GIF etc.), EPS, EMF. These |
| formats are bundled. Other formats such as SVG and WMF are available |
| through plug-ins hosted elsewhere. |
| </li> |
| <li> |
| Supported in-memory representations: |
| <ul> |
| <li>RenderedImage/BufferedImage</li> |
| <li>raw/undecoded (JPEG, EPS, CCITT group 3/4)</li> |
| <li>Java2D (Images painted through Graphics2D)</li> |
| <li>XML DOM (for SVG, MathML etc.)</li> |
| <li>Additional representations can be added as necessary.</li> |
| </ul> |
| </li> |
| <li> |
| Custom image loaders and converters can be dynamically plugged in. |
| Automatic plug-in detection through the application classpath. |
| </li> |
| <li> |
| An image cache speeds up the processing for images that are requested |
| multiple times. |
| </li> |
| </ul> |
| </section> |
| <section id="tutorial"> |
| <title>Tutorial</title> |
| <section id="manager-setup"> |
| <title>Setting up the manager</title> |
| <p> |
| Before we can start to work with the package we need to set up the |
| <code>ImageManager</code>. It provides convenience methods to load |
| and convert images and holds the image cache. |
| </p> |
| <p> |
| The <code>ImageManager</code> needs an <code>ImageContext</code>. |
| This interface provides the <code>ImageManager</code> with important |
| context and configuration data. Currently this is only the source |
| resolution. The <code>ImageManager</code> and |
| <code>ImageContext</code> are intended to be shared within an |
| application. |
| </p> |
| <source><![CDATA[ |
| import org.apache.xmlgraphics.image.loader.ImageContext; |
| import org.apache.xmlgraphics.image.loader.ImageManager; |
| import org.apache.xmlgraphics.image.loader.impl.DefaultImageContext; |
| |
| [..] |
| |
| ImageManager imageManager = new ImageManager(new DefaultImageContext()); |
| ]]></source> |
| <note> |
| In this example, <code>DefaultImageContext</code> is used. You may |
| need to write your own implementation of <code>ImageContext</code> for |
| your use case. |
| </note> |
| </section> |
| <section id="preloading"> |
| <title>Preloading an image</title> |
| <p> |
| In order to load an image, it needs to be "preloaded" first, i.e. the image content |
| type is detected and the intrinsic size of the image is determined. The result of this |
| process is an <code>ImageInfo</code> instance which contains the URI, MIME type and |
| intrinsic size. In most cases, this is done without loading the whole image (see |
| SPI section below for information on exceptions to this rule). |
| </p> |
| <p> |
| Preloading is normally done through the <code>ImageManager</code>'s |
| <code>getImageInfo()</code> method. For this operation |
| an <code>ImageSessionContext</code> needs to be provided. It is responsible for |
| supplying JAXP <code>Source</code> objects, URI resolution and providing other |
| information needed for the image operations. In simple cases you can simply use |
| <code>DefaultImageSessionContext</code>, but often you will want to write your own |
| implementation of <code>ImageSessionContext</code>. In that case, it's recommended to |
| subclass <code>AbstractImageSessionContext</code> which lets you avoid rewriting a |
| lot of code for providing <code>Source</code> objects. |
| </p> |
| <p> |
| </p> |
| <source><![CDATA[ |
| import org.apache.xmlgraphics.image.loader.ImageInfo; |
| import org.apache.xmlgraphics.image.loader.ImageSessionContext; |
| import org.apache.xmlgraphics.image.loader.impl.DefaultImageSessionContext; |
| |
| [..] |
| ImageSessionContext sessionContext = new DefaultImageSessionContext( |
| imageManager.getImageContext(), null); |
| |
| ImageInfo info = imageManager.getImageInfo(uri, sessionContext); |
| ]]></source> |
| </section> |
| <section id="loading"> |
| <title>Loading an image</title> |
| <p> |
| Once the image is "preloaded", it can be fully loaded in the form/flavor that is |
| needed by the consuming application. The required flavor is indicated through the |
| <code>ImageFlavor</code> class. If you want the image as a bitmap image in memory, |
| you could request an <code>ImageFlavor.RENDERED_IMAGE</code>. Again, the |
| <code>ImageSessionContext</code> will be needed. |
| </p> |
| <source><![CDATA[ |
| import org.apache.xmlgraphics.image.loader.Image; |
| import org.apache.xmlgraphics.image.loader.ImageFlavor; |
| |
| [..] |
| Image img = this.imageManager.getImage( |
| info, ImageFlavor.RENDERED_IMAGE, sessionContext); |
| |
| ImageRendered imageRend = (ImageRendered)img; |
| RenderedImage ri = imageRend.getRenderedImage(); |
| //...and do anything with the RenderedImage |
| ]]></source> |
| <p> |
| In this example above, we simply acquire the image as a RenderedImage instance. |
| If the original image was a vector graphic image (SVG, WMF etc.), it's automatically |
| converted to a bitmap image. Note: The resolution of the created image is controlled |
| by the target resolution returned by the <code>ImageSessionContext</code>. |
| </p> |
| <p> |
| Of course, the framework can only provide images in the formats, it has image loaders |
| or image converters for. An example: It is possible to load EPS images, but they |
| can only be provided in raw form. In order to provide it as a bitmap image, a PostScript |
| interpreter would be needed to interpret the PostScript code. This interpreter would |
| be integrated using an <code>ImageConverter</code> implementation (see SPI section below). |
| If the requested form of the image cannot be provided you will get an |
| <code>ImageException</code> on which you'll have to react as needed. |
| </p> |
| <p> |
| In <a href="ext:xmlgraphics.apache.org/fop">Apache FOP</a>, each renderer supports |
| a different set of image flavors that can be embedded in the target format. For example: |
| The PDF renderer can deal with Java2D image, bitmaps, XML, native JPEG and CCITT images. |
| The PCL renderer, however, can only consume bitmap images. So, if you can accept |
| more than one flavor, the package allows you to specify all of them in an ordered list |
| (the first in the list is the preferred format). The package will then try to return |
| the best representation possible. Here's a code example: |
| </p> |
| <source><![CDATA[ |
| import org.apache.xmlgraphics.image.loader.Image; |
| import org.apache.xmlgraphics.image.loader.ImageFlavor; |
| |
| [..] |
| final ImageFlavor[] flavors = new ImageFlavor[] |
| {ImageFlavor.GRAPHICS2D, |
| ImageFlavor.BUFFERED_IMAGE, |
| ImageFlavor.RENDERED_IMAGE}; |
| |
| Image img = manager.getImage( |
| info, flavors, sessionContext); |
| |
| if (img instanceof ImageGraphics2D) { |
| //handle Java2D/Graphics2D image |
| } else if (img instanceof ImageRendered) { |
| //handle BufferedImage and RenderedImage |
| //(BufferedImage is a subclass of RenderedImage) |
| } else { |
| throw new IllegalStateException("Unexpected flavor"); |
| } |
| ]]></source> |
| <note> |
| While each <code>BufferedImage</code> is also a <code>RenderedImage</code>, |
| it can be more efficient to also specify <code>ImageFlavor.BUFFERED_IMAGE</code> in |
| the flavor array. |
| </note> |
| </section> |
| </section> |
| <section id="tipsntricks"> |
| <title>Tips & Tricks</title> |
| <p> |
| If you are loading bitmap images and you get an error like |
| <code>"Cannot load image (no suitable loader/converter combination available) for |
| myimage.tif (image/tiff)</code>, |
| you maybe be missing the necessary ImageIO codec to decode the image. A number of |
| well-written codecs can be found in |
| <a href="https://jai-imageio.dev.java.net/">JAI Image I/O Tools Project</a>. Just download |
| the distribution and add the JAR to the classpath. ImageIO will automatically pick up |
| the new codecs and they will subsequently be available to the image framework. |
| </p> |
| </section> |
| <section id="spi"> |
| <title>Service Provider Interface (SPI, Plug-ins)</title> |
| <p> |
| The whole image framework is designed to be highly extensible. There are various |
| extension points where new functionality can be added. The three main SPI interfaces are: |
| </p> |
| <ul> |
| <li><code>ImagePreloader</code>: detects the content type and preloads an image</li> |
| <li><code>ImageLoader</code> and <code>ImageLoaderFactory</code>: loads images</li> |
| <li><code>ImageConverter</code>: converts images from one representation into another</li> |
| </ul> |
| <p> |
| If you plan to write an implementation of one of the above interfaces, please also take |
| a look at the existing implementations for reference. |
| </p> |
| <p> |
| Throughout the SPI, you'll find a <code>Map</code> parameter (hints) in the most important |
| methods. That's a way to supply additional information to the implementation by the |
| caller. For example, the source and target resolutions from the image (session) context |
| is stored in the hints. The implementation should not rely on the presence of specialized |
| information and should always have sensible defaults to rely on in this case. |
| </p> |
| <section id="ImagePreloader"> |
| <title>ImagePreloader</title> |
| <p> |
| The first task is identifying whether the implementation supports the given image. |
| If the image is loaded using an ImageInputStream it is important to always reset the |
| stream position to the beginning of the file at the end of the |
| <code>preloadImage()</code> method, because all registered preloaders are check in turn |
| until one implementation signals that it supports the format. In that case, it has to |
| extract only the minimal information from the image necessary to identify the image's |
| intrinsic size. For most formats, this is doable without loading the whole image into |
| memory. |
| </p> |
| <p> |
| However, for some formats (like MathML or WMF), loading the whole image at preloading |
| time is hard to avoid since the image's size can only be determined that way. In such |
| a case, the <code>ImagePreloader</code> implementations shall pass the loaded |
| document to the respective <code>ImageLoader</code> through the custom objects that |
| can be attached to the <code>ImageInfo</code> object. If the preloader loads the whole |
| document, it shall close the given <code>Source</code> object (calling |
| <code>ImageUtil.closeQuietly(Source)</code>). |
| </p> |
| <p> |
| The priority the implementation reports is used to sort all registered implementations. |
| This is to fine-tune the inner workings and to optimize performance since some formats |
| are usually used more frequently than others. |
| </p> |
| <note> |
| Normally, if you implement an <code>ImagePreloader</code> you will also need to implement |
| the respective <code>ImageLoader/ImageLoaderFactory</code>, or vice versa. |
| </note> |
| </section> |
| <section id="ImageLoader"> |
| <title>ImageLoader and ImageLoaderFactory</title> |
| <p> |
| The factory interface has been created to allow checking if some library that an |
| implementation depends on is really in the classpath so it can report back that the |
| <code>ImageLoader</code> is not funtional. The factory also reports what kind of |
| image formats it supports and which image flavors it can return. There can be a |
| complex relationship between the two. It is recommmended, however, to write smaller |
| implementations rather than big, almighty ones. |
| </p> |
| <p> |
| The usage penalty is used when constructing image conversion pipelines. There can be |
| multiple ways to provide an image in one of the supported flavors and this value helps |
| to make the best decision. |
| </p> |
| <p> |
| While the factory basically just provides information and creates new |
| <code>ImageLoader</code> instances, the image loaders are doing the actual leg work |
| of decoding the images. The image flavor returned by the loader must match the |
| flavor that is returned by <code>getTargetFlavor()</code>. |
| </p> |
| </section> |
| <section id="ImageConverter"> |
| <title>ImageConverter</title> |
| <p> |
| The image converter is responsible to transform one image representation into another. |
| Bundled implementations support these conversions: Java2D to bitmap, bitmap to Java2D |
| and RenderedImage to "raw" PNG. Ideas for additional image converters could be: |
| PDF to Java2D, EPS to Java2D or MathML to SVG or Java2D. |
| </p> |
| <p> |
| Each ImageConverter comes with a usage penalty which is used when constructing |
| conversion pipelines so the pipeline with the least penalty value can be chosen. This |
| is necessary as the consuming application my support multiple image flavors and there |
| can be multiple ways to convert an image in one of the requested image flavors. |
| Internally, <a href="http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">Dijkstra's |
| shortest path algorithm</a> is used to find the best path using the penalties as "way |
| lengths". |
| </p> |
| </section> |
| </section> |
| </body> |
| </document> |