You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-dev@xmlgraphics.apache.org by Eric SCHAEFFER <es...@posterconseil.com> on 2000/02/03 10:08:03 UTC

JIMI files (Re: We should do something)

----- Original Message -----
From: <Gi...@pwr.ch>
To: <fo...@xml.apache.org>
Sent: Tuesday, February 01, 2000 9:18 AM
Subject: RE: We should do something


> This is a multi-part message in MIME format.
>
> ------=_NextPart_000_0000_01BF6C95.4A880BA0
> Content-Type: text/plain;
> charset="iso-8859-1"
> Content-Transfer-Encoding: 7bit
>
> Hi Eric
>
> > My Jimi implementation is ready (but I'd like people to test it).
> > If we do it this way, we could then change for JAI just changing the
> > properties file.
>
> Send it over. I like to test them. I've tested the JAI implementation. But
> the quality of the images when printed doesn't fullfill my expectation.
>
> Giacomo
>

I'm going to change the FopImageFactory class to use a configuration file.
The PDFRenderer.renderImageArea function needs to be changed (I don't use
XObject, just insert the image data inline, encoded in Hexa and
Deflat/ZLib)...


Eric.

=============================================
FopImage
=============================================

package org.apache.fop.image;

public interface FopImage {
  public int getPixelWidth();
  public int getPixelHeight();
  public int getWidth();
  public int getHeight();
  public void setDimensions(int w, int h);
  public int getX();
  public int getY();
  public String getHRef();
// be carreful : image is compressed with zlib
  public byte[] getImageMap();
  public int getImageSize();
  public boolean isColored();
  public int getBitsPerPixel();
}

=============================================
FopImageFactory
=============================================

package org.apache.fop.image;

import java.io.IOException;
import java.net.*;

public class FopImageFactory {

 // Image file formats supported
 public final static int IMAGE_GIF = 1;
 public final static int IMAGE_JPEG = 2;
 public final static int IMAGE_PNG = 3;
 public final static int IMAGE_TGA = 4;
 public final static int IMAGE_DIB = 5;
 public final static int IMAGE_DDB = 6;
 public final static int IMAGE_BMP = 7;
 public final static int IMAGE_PICT = 8;
 public final static int IMAGE_PSD = 9;
 public final static int IMAGE_CMU_RASTER = 10;
 public final static int IMAGE_TIFF = 11;
 public final static int IMAGE_XBM = 12;
 public final static int IMAGE_XPM = 13;
 public final static int IMAGE_ICO = 14;
 public final static int IMAGE_CUR = 15;
 public final static int IMAGE_PCX = 16;

 public static FopImage Make(String ref,int x,int y, int width, int height)
{
  URL absoluteURL = null;
  try {
   absoluteURL = new URL(ref);
  }
  catch (MalformedURLException e) {
   // maybe relative
/*   URL context_url = null;
   try {
    context_url = new URL(); // how to get the context URL ?
    try {
     absoluteURL = new URL(context_url, ref);
    }
    catch (MalformedURLException e_abs) {
     // not found
     System.err.println("Invalid Image URL : " + e_abs.getMessage() + "(base
URL " + context_url.toString() + ")");
     return null;
    }
   }
   catch (MalformedURLException e_context) {
    // pb context url
    System.err.println("Invalid Image URL - error on relative URL : " +
e_context.getMessage());
    return null;
   }
*/
   System.err.println("Invalid Image URL : " + e.getMessage());
   return null;
  }

  int imageType = -1;

  // try to get image type by 'Content-Type'
  try {
   URLConnection url_conn = absoluteURL.openConnection();
   String contentType = url_conn.getContentType();
   if ( contentType != null ) {
    if ( ( contentType.equalsIgnoreCase("image/gif") ) ||
      ( contentType.equalsIgnoreCase("image/x-gif") ) ) {
     imageType = IMAGE_GIF;
    } else if ( ( contentType.equalsIgnoreCase("image/jpeg") ) ||
        ( contentType.equalsIgnoreCase("image/x-jpeg") ) ) {
     imageType = IMAGE_JPEG;
    } else if ( ( contentType.equalsIgnoreCase("image/png") ) ||
        ( contentType.equalsIgnoreCase("image/x-png") ) ) {
     imageType = IMAGE_PNG;
    } else if ( ( contentType.equalsIgnoreCase("image/tga") ) ||
        ( contentType.equalsIgnoreCase("image/x-tga") ) ) {
     imageType = IMAGE_TGA;
    } else if ( contentType.equalsIgnoreCase("image/dib") ) {
     imageType = IMAGE_DIB;
    } else if ( ( contentType.equalsIgnoreCase("image/ddb") ) ||
        ( contentType.equalsIgnoreCase("image/x-ddb") ) ) {
     imageType = IMAGE_DDB;
    } else if ( ( contentType.equalsIgnoreCase("image/bmp") ) ||
        ( contentType.equalsIgnoreCase("image/x-bmp") ) ||
        ( contentType.equalsIgnoreCase("image/x-ms-bmp") ) ) {
     imageType = IMAGE_BMP;
    } else if ( ( contentType.equalsIgnoreCase("image/pict") ) ||
        ( contentType.equalsIgnoreCase("image/x-pict") ) ) {
     imageType = IMAGE_PICT;
    } else if ( ( contentType.equalsIgnoreCase("image/psd") ) ||
        ( contentType.equalsIgnoreCase("image/x-psd") ) ) {
     imageType = IMAGE_PSD;
    } else if ( ( contentType.equalsIgnoreCase("image/cmu-raster") ) ||
        ( contentType.equalsIgnoreCase("image/x-cmu-raster") ) ) {
     imageType = IMAGE_CMU_RASTER;
    } else if ( ( contentType.equalsIgnoreCase("image/tiff") ) ||
        ( contentType.equalsIgnoreCase("image/x-tiff") ) ) {
     imageType = IMAGE_TIFF;
    } else if ( ( contentType.equalsIgnoreCase("image/xbm") ) ||
        ( contentType.equalsIgnoreCase("image/x-xbm") ) ||
        ( contentType.equalsIgnoreCase("image/x-xbitmap") ) ) {
     imageType = IMAGE_XBM;
    } else if ( ( contentType.equalsIgnoreCase("image/xpm") ) ||
        ( contentType.equalsIgnoreCase("image/x-xpm") ) ||
        ( contentType.equalsIgnoreCase("image/x-xbitmap") ) ) {
     imageType = IMAGE_XPM;
    } else if ( ( contentType.equalsIgnoreCase("image/ico") ) ||
        ( contentType.equalsIgnoreCase("image/x-ico") ) ) {
     imageType = IMAGE_ICO;
    } else if ( ( contentType.equalsIgnoreCase("image/cur") ) ||
        ( contentType.equalsIgnoreCase("image/x-cur") ) ) {
     imageType = IMAGE_CUR;
    } else if ( ( contentType.equalsIgnoreCase("image/pcx") ) ||
        ( contentType.equalsIgnoreCase("image/x-pcx") ) ) {
     imageType = IMAGE_PCX;
    }
   }
  }
  catch (IOException e) {
   System.err.println("Error while recovering Image Type : " +
e.getMessage());
   return null;
  }

  // try get image type by extension if the first method failed
  if ( imageType < 0 ) {
   String stringURL = absoluteURL.toString();
   int extensionStart = stringURL.lastIndexOf(".");
   String extensionURL = "";
   if ( ( extensionStart > 0 ) && ( extensionStart < stringURL.length() -
1 ) ) {
    extensionURL = stringURL.substring(extensionStart + 1,
stringURL.length());
   }

   if (  extensionURL.equalsIgnoreCase("gif") ) {
    imageType = IMAGE_GIF;
   } else if ( ( extensionURL.equalsIgnoreCase("jpeg") ) ||
       ( extensionURL.equalsIgnoreCase("jpg") ) ||
       ( extensionURL.equalsIgnoreCase("jpe") ) ) {
    imageType = IMAGE_JPEG;
   } else if ( extensionURL.equalsIgnoreCase("png") ) {
    imageType = IMAGE_PNG;
   } else if ( extensionURL.equalsIgnoreCase("tga") ) {
    imageType = IMAGE_TGA;
   } else if ( extensionURL.equalsIgnoreCase("dib") ) {
    imageType = IMAGE_DIB;
   } else if ( extensionURL.equalsIgnoreCase("ddb") ) {
    imageType = IMAGE_DDB;
   } else if ( extensionURL.equalsIgnoreCase("bmp") ) {
    imageType = IMAGE_BMP;
   } else if ( extensionURL.equalsIgnoreCase("pict") ) {
   // ?
    imageType = IMAGE_PICT;
   } else if ( extensionURL.equalsIgnoreCase("psd") ) {
    imageType = IMAGE_PSD;
   } else if ( extensionURL.equalsIgnoreCase("ras") ) {
   // ?
    imageType = IMAGE_CMU_RASTER;
   } else if ( ( extensionURL.equalsIgnoreCase("tiff") ) ||
       ( extensionURL.equalsIgnoreCase("tif") ) ) {
    imageType = IMAGE_TIFF;
   } else if ( extensionURL.equalsIgnoreCase("xbm") ) {
    imageType = IMAGE_XBM;
   } else if ( extensionURL.equalsIgnoreCase("xpm") ) {
    imageType = IMAGE_XPM;
   } else if ( extensionURL.equalsIgnoreCase("ico") ) {
    imageType = IMAGE_ICO;
   } else if ( extensionURL.equalsIgnoreCase("cur") ) {
    imageType = IMAGE_CUR;
   } else if ( extensionURL.equalsIgnoreCase("pcx") ) {
    imageType = IMAGE_PCX;
   }
  }

  // if image type not defined
  if ( imageType < 0 ) {
   System.err.println("Image format not supported (" +
absoluteURL.toString() + ")");
   return null;
  }

  // Load the right class
  FopImage image = null;
  if (  imageType == IMAGE_GIF ||
    imageType == IMAGE_JPEG ) {
   try {
    image = new GifJpegImage(absoluteURL, x, y, width, height);
   } catch (InstantiationException e) {
    System.err.println(e.getMessage());
    return null;
   }
   return image;
  } else if (  imageType == IMAGE_PNG ||
    imageType == IMAGE_TGA ||
    imageType == IMAGE_DIB ||
    imageType == IMAGE_DDB ||
    imageType == IMAGE_BMP ||
    imageType == IMAGE_PICT ||
    imageType == IMAGE_PSD ||
    imageType == IMAGE_CMU_RASTER ||
    imageType == IMAGE_TIFF ||
    imageType == IMAGE_XBM ||
    imageType == IMAGE_XPM ||
    imageType == IMAGE_ICO ||
    imageType == IMAGE_CUR ||
    imageType == IMAGE_PCX ) {
   try {
    image = new JimiImage(absoluteURL, x, y, width, height);
   } catch (InstantiationException e) {
    System.err.println(e.getMessage());
    return null;
   }
   return image;
  }
/* else if ( ... */

  // no Image Producer
   System.err.println("Image format not supported (" +
absoluteURL.toString() + ")");
  return null;

 }
}

=============================================
GifJpegImage
=============================================

package org.apache.fop.image;

import java.util.Hashtable;
import java.net.URL;
import java.io.IOException;
import java.io.PrintWriter;
import java.awt.image.*;
import java.awt.Image;
import java.awt.Toolkit;

import java.lang.Math;

import java.util.zip.Deflater;

public class GifJpegImage implements FopImage {

    int X = 0;
    int Y = 0;
    int width = 0;
    int height = 0;
    int pixelWidth = 0;
    int pixelHeight = 0;
    URL HRef = null;
    boolean colored = true;
    int bitsPerPixel = 8;
    byte[] compressedMap;
    int compressedSize = 0;

    /** synchronization object */
    protected Integer imageWait = new Integer(0);

    public GifJpegImage(URL href, int x, int y, int w, int h)
 throws InstantiationException {

 this.HRef = href;
 this.X = x;
 this.Y = y;
 this.width = w;
 this.height = h;
 this.pixelHeight = -1;
 this.pixelWidth = -1;

 int[] tempMap;

 try {
  ImageProducer ip = (ImageProducer)HRef.getContent();
  FopImageConsumer consumer = new FopImageConsumer(this);
  ip.startProduction(consumer);

   while (! consumer.isImageReady() ) {}
   this.pixelHeight = consumer.getHeight();
   this.pixelWidth = consumer.getWidth();

   tempMap = new int[this.pixelWidth * this.pixelHeight];

   PixelGrabber pg = new PixelGrabber( ip,
        0, 0,
        this.pixelWidth, this.pixelHeight,
        tempMap,
        0, this.pixelWidth
       );
   try {
    pg.grabPixels();
   } catch (InterruptedException e) {
//    System.err.println("Image grabbing interrupted");
    throw new InstantiationException("Image grabbing interrupted");
   }

  } catch (Exception e) {
//   System.err.println("Error loading image " + HRef.toString() + " : " +
e.getClass() + " - " + e.getMessage());
   throw new InstantiationException("Error loading image " + HRef.toString()
+ " : " + e.getClass() + " - " + e.getMessage());
  }

 // try to Deflat the image
 Deflater compressor = new Deflater();
 compressor.setLevel(Deflater.DEFAULT_COMPRESSION);
 compressor.setStrategy(Deflater.DEFAULT_STRATEGY);
 byte[] byteTempMap = new byte[this.pixelWidth * this.pixelHeight * 3];
 for (int i = 0; i < this.pixelHeight; i++) {
  for (int j = 0; j < this.pixelWidth; j++) {
   int p = tempMap[i * this.pixelWidth + j];
   int r = (p >> 16) & 0xFF;
   int g = (p >>  8) & 0xFF;
   int b = (p      ) & 0xFF;
   byteTempMap[3 * (i * this.pixelWidth + j)] = (byte) (r & 0xFF);
   byteTempMap[3 * (i * this.pixelWidth + j) + 1] = (byte) (g & 0xFF);
   byteTempMap[3 * (i * this.pixelWidth + j) + 2] = (byte) (b & 0xFF);
  }
 }
 compressor.setInput(byteTempMap);
 compressor.finish();
 compressedMap = new byte[this.pixelWidth * this.pixelHeight * 3];
 compressedSize = compressor.deflate(compressedMap);
 compressor.end();

 tempMap = null;
 byteTempMap = null;

 double ratio = ((double) pixelWidth) / ((double) pixelHeight);

 // found formatting size
 if ( ( width == 0 ) && ( height == 0 ) ) {
  height = (int) (1000.0 * ((double) pixelHeight) / 2.0);
  width = (int) (1000.0 * ((double) pixelWidth) / 2.0);
 } else if ( width == 0 ) {
  width = (int) (ratio * ((double) height));
 } else if ( height == 0 ) {
  height = (int) (((double) width) / ratio);
 }

    }

 public void setDimensions(int width, int height) {
  this.width = width;
  this.height = height;
 }

 public static class FopImageConsumer implements ImageConsumer {
  int width = -1;
  int height = -1;
  GifJpegImage graphic;
  Integer imageStatus = new Integer(-1);

  public FopImageConsumer(GifJpegImage graphic) {
   this.graphic = graphic;
  }

  public void imageComplete(int status) {
   synchronized (this.imageStatus) {
    this.imageStatus = new Integer(status);
   }
  }

  public synchronized boolean isImageReady()
   throws Exception {
   synchronized (this.imageStatus) {
    if ( this.imageStatus.intValue() != -1 ) {
     String statusStr = null;
     if ( this.imageStatus.intValue() == ImageConsumer.IMAGEABORTED ) {
      statusStr = new String("Image Aborted");
     } else if ( this.imageStatus.intValue() == ImageConsumer.IMAGEERROR ) {
      statusStr = new String("Image Error");
     }
     if ( statusStr != null ) {
      throw new Exception("Error in image consumer (" + statusStr + ")");
     }

     if ( this.imageStatus.intValue() == ImageConsumer.STATICIMAGEDONE ) {
      return true;
     }
    }
    return false;
   }
  }

  public void setColorModel(ColorModel model) {}

  public void setDimensions(int width, int height) {
   this.width = width;
   this.height = height;
  }

  public void setHints(int hintflags) {}

  public void setPixels(int x, int y, int w, int h,
     ColorModel model, byte[] pixels,int off,
     int scansize) {}

  public void setPixels(int x, int y, int w, int h,
     ColorModel model, int[] pixels, int off,
     int scansize) {}

  public void setProperties(Hashtable props) {}

  public int getWidth() {
   return this.width;
  }

  public int getHeight() {
   return this.height;
  }
 }

    public String getHRef() {
     // need to be changed (URL everywhere...)
 return this.HRef.toString();
    }

    public int getWidth() {
 return this.width;
    }

    public int getHeight() {
 return this.height;
    }

    public int getPixelWidth() {
 return this.pixelWidth;
    }

    public int getPixelHeight() {
 return this.pixelHeight;
    }

    public int getX() {
 return this.X;
    }

    public int getY() {
 return this.Y;
    }

 public int getImageSize() {
  return this.compressedSize;
 }

 public byte[] getImageMap() {
  return this.compressedMap;
 }

    public boolean isColored() {
 return true;
    }

    public int getBitsPerPixel() {
 return this.bitsPerPixel;
    }
}

=============================================
JimiImage
=============================================

package org.apache.fop.image;

// Java
import java.util.Hashtable;
import java.net.URL;
import java.io.IOException;
import java.io.PrintWriter;
import java.awt.image.*;
import java.awt.*;

// png compression
import java.util.zip.Deflater;

// Jimi
import com.sun.jimi.core.*;

public class JimiImage implements FopImage {

 int X = 0;
 int Y = 0;
 int width = 0;
 int height = 0;
 int pixelWidth = 0;
 int pixelHeight = 0;
 URL HRef = null;
 boolean colored = true;
 int bitsPerPixel = 8;
 byte[] compressedMap;
 int compressedSize = 0;

 public JimiImage(URL href, int x, int y, int w, int h)
  throws InstantiationException {

  this.HRef = href;
  this.X = x;
  this.Y = y;
  this.width = w;
  this.height = h;
  this.pixelHeight = -1;
  this.pixelWidth = -1;

  int[] tempMap;

  try {
   ImageProducer ip = Jimi.getImageProducer(href, Jimi.SYNCHRONOUS |
Jimi.IN_MEMORY);
   FopImageConsumer consumer = new FopImageConsumer(this);
   ip.startProduction(consumer);

   while (! consumer.isImageReady() ) {}
   this.pixelHeight = consumer.getHeight();
   this.pixelWidth = consumer.getWidth();

   tempMap = new int[this.pixelWidth * this.pixelHeight];

   PixelGrabber pg = new PixelGrabber( ip,
        0, 0,
        this.pixelWidth, this.pixelHeight,
        tempMap,
        0, this.pixelWidth
       );
   try {
    pg.grabPixels();
   } catch (InterruptedException e) {
//    System.err.println("Image grabbing interrupted");
    throw new InstantiationException("Image grabbing interrupted");
   }

  } catch (Exception e) {
//   System.err.println("Error loading image " + HRef.toString() + " : " +
e.getClass() + " - " + e.getMessage());
   throw new InstantiationException("Error loading image " + HRef.toString()
+ " : " + e.getClass() + " - " + e.getMessage());
  }

  // try to Deflat the image
  Deflater compressor = new Deflater();
  compressor.setLevel(Deflater.DEFAULT_COMPRESSION);
  compressor.setStrategy(Deflater.DEFAULT_STRATEGY);
  byte[] byteTempMap = new byte[this.pixelWidth * this.pixelHeight * 3];
  for (int i = 0; i < this.pixelHeight; i++) {
   for (int j = 0; j < this.pixelWidth; j++) {
    int p = tempMap[i * this.pixelWidth + j];
    int r = (p >> 16) & 0xFF;
    int g = (p >>  8) & 0xFF;
    int b = (p      ) & 0xFF;
    byteTempMap[3 * (i * this.pixelWidth + j)] = (byte) (r & 0xFF);
    byteTempMap[3 * (i * this.pixelWidth + j) + 1] = (byte) (g & 0xFF);
    byteTempMap[3 * (i * this.pixelWidth + j) + 2] = (byte) (b & 0xFF);
   }
  }
  compressor.setInput(byteTempMap);
  compressor.finish();
  compressedMap = new byte[this.pixelWidth * this.pixelHeight * 3];
  compressedSize = compressor.deflate(compressedMap);
  compressor.end();

  tempMap = null;
  byteTempMap = null;

  double ratio = ((double) pixelWidth) / ((double) pixelHeight);

  // found formatting size
  if ( ( width == 0 ) && ( height == 0 ) ) {
   height = (int) (1000.0d * ((double) pixelHeight) / 2.0d);
   width = (int) (1000.0d * ((double) pixelWidth) / 2.0d);
  } else if ( width == 0 ) {
   width = (int) (ratio * ((double) height));
  } else if ( height == 0 ) {
   height = (int) (((double) width) / ratio);
  }

 }

 public void setDimensions(int width, int height) {
  this.width = width;
  this.height = height;
 }

 public static class FopImageConsumer implements ImageConsumer {
  int width = -1;
  int height = -1;
  JimiImage graphic;
  Integer imageStatus = new Integer(-1);

  public FopImageConsumer(JimiImage graphic) {
   this.graphic = graphic;
  }

  public void imageComplete(int status) {
   synchronized (this.imageStatus) {
    this.imageStatus = new Integer(status);
   }
  }

  public synchronized boolean isImageReady()
   throws Exception {
   synchronized (this.imageStatus) {
    if ( this.imageStatus.intValue() != -1 ) {
     String statusStr = null;
     if ( this.imageStatus.intValue() == ImageConsumer.IMAGEABORTED ) {
      statusStr = new String("Image Aborted");
     } else if ( this.imageStatus.intValue() == ImageConsumer.IMAGEERROR ) {
      statusStr = new String("Image Error");
     }
     if ( statusStr != null ) {
      throw new Exception("Error in image consumer (" + statusStr + ")");
     }

     if ( this.imageStatus.intValue() == ImageConsumer.STATICIMAGEDONE ) {
      return true;
     }
    }
    return false;
   }
  }

  public void setColorModel(ColorModel model) {}

  public void setDimensions(int width, int height) {
   this.width = width;
   this.height = height;
  }

  public void setHints(int hintflags) {}

  public void setPixels(int x, int y, int w, int h,
     ColorModel model, byte[] pixels,int off,
     int scansize) {}

  public void setPixels(int x, int y, int w, int h,
     ColorModel model, int[] pixels, int off,
     int scansize) {}

  public void setProperties(Hashtable props) {}

  public int getWidth() {
   return this.width;
  }

  public int getHeight() {
   return this.height;
  }
 }

 public String getHRef() {
  // need to be changed (URL everywhere...)
  return this.HRef.toString();
 }

 public int getWidth() {
  return this.width;
 }

 public int getHeight() {
  return this.height;
 }

 public int getPixelWidth() {
  return this.pixelWidth;
 }

 public int getPixelHeight() {
  return this.pixelHeight;
 }

 public int getX() {
  return this.X;
 }

 public int getY() {
  return this.Y;
 }

 public int getImageSize() {
  return this.compressedSize;
 }

 public byte[] getImageMap() {
  return this.compressedMap;
 }

 public boolean isColored() {
  return true;
 }

 public int getBitsPerPixel() {
  return this.bitsPerPixel;
 }

}

=============================================
PDFRenderer
=============================================

...

    public void renderImageArea(ImageArea area) {
 // adapted from contribution by BoBoGi
 int x = this.currentAreaContainerXPosition +
     area.getXOffset();
 int y = this.currentYPosition;
 int w = area.getContentWidth();
 int h = area.getHeight();

 this.currentYPosition -= h;

 FopImage img = area.getImage();

 int pw = img.getPixelWidth();
 int ph = img.getPixelHeight();
 byte[] pixels = img.getImageMap();
 int size = img.getImageSize();

 float rx = (x + img.getX())/1000f;
 float ry = (y + img.getY())/1000f;
 float rh = img.getHeight()/1000f;
 float rw = img.getWidth()/1000f;

 currentStream.add("ET\nq\n" + rw + " 0 0 " + rh + " " + rx + " " + (ry-rh)
     + " cm\nBI\n/W " + pw + "\n/H " + ph +
     "\n/BPC 8\n/CS /RGB\n/F [/ASCIIHexDecode /FlateDecode]\nID\n");

 for (int i = 0; i < size; i++) {
  int val = (int) (pixels[i] & 0xFF);
  if ( val < 16 ) {
      this.currentStream.add(Integer.toString(0));
  }
  this.currentStream.add(Integer.toHexString(val));
 }
 this.currentStream.add(">\n");

 currentStream.add("EI\nQ\nBT\n");

 }

...

_______________________________________

Eric SCHAEFFER
eschaeffer@posterconseil.com

POSTER CONSEIL
118 rue de Tocqueville
75017 PARIS
FRANCE
Tel. : 33-140541058
Fax : 33-140541059

----------------------------------------------------------------------------
-----------------------------
 Come to the first official Apache Software Foundation Conference!
-----------------------------------
http://ApacheCon.Com ------------------------------------
_______________________________________