<?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 &amp; 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>
    <section id="customization">
      <title>Customization</title>
      <section id="source-reuse">
        <title>Disabling Source Re-use</title>
        <p>
          By default, the Source object being used during the pre-loading stage is re-used when
          the image is fully loaded later (assuming an ImageSessionContext is used that descends
          from AbstractImageSessionContext). That means that a stream is only opened once and
          the image loading framework tries to re-wind the stream when it has to re-read portions
          of the stream when loading the complete image.
        </p>
        <p>
          In some situations, this behavior may be undesired. Therefore, it can be disabled
          through a system property 
          (<code>org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.no-source-reuse</code>).
          Set it to "true" and that feature will be disabled.
        </p>
      </section>
      <section id="plugin-penalties">
        <title>Adjusting plug-in penalties</title>
        <p>
          Every image loader plug-in has a hard-coded usage penalty that influences which solution
          is chosen if there are multiple possibilities to load an image. Sometimes, though, these
          penalties need to be tweaked. The <code>ImageImplRegistry</code> (reachable through
          <code>ImageManager.getRegistry()</code>) supports storing additional penalty values.
          An example:
        </p>
        <source><![CDATA[ImageImplRegistry registry = imageManager.getRegistry();
registry.setAdditionalPenalty(
    "org.apache.xmlgraphics.image.loader.impl.ImageLoaderRawCCITTFax",
    Penalty.toPenalty(10000));]]></source>
        <p>
          This increases the penalty for the raw CCITT loader significantly so it will only be
          used if no other solutions exist. You can also set <code>Penalty.INFINITE_PENALTY</code>
          to disable the plug-in altogether. Negative penalties are possible to promote a plug-in
          but a negative penalty sum will be treated as zero penalty in most cases.
        </p>
      </section>
    </section>
  </body>
</document>
