Index: src/org/apache/fop/image/BmpImage.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/BmpImage.java,v retrieving revision 1.3.2.1 diff -u -w -w -u -r1.3.2.1 BmpImage.java --- src/org/apache/fop/image/BmpImage.java 25 Feb 2003 13:38:22 -0000 1.3.2.1 +++ src/org/apache/fop/image/BmpImage.java 5 Apr 2005 15:41:23 -0000 @@ -51,11 +51,10 @@ package org.apache.fop.image; // Java -import java.net.URL; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; -// FOP import org.apache.fop.datatypes.ColorSpace; import org.apache.fop.image.analyser.ImageReader; @@ -81,6 +80,10 @@ int[] headermap = new int[54]; int filepos = 0; InputStream file = null; + // BUGFIX: do always close the inputstream (the garbage collector may do this + // too late when handling pdfs with many images!!) + try + { byte palette[] = null; try { file = this.m_href.openStream(); @@ -235,6 +238,21 @@ j++; } } + } + finally + { + if (file!=null) + { + try + { + file.close(); + } + catch(IOException e) + { + // can be ignored + } + } + } // This seems really strange to me, but I noticed that JimiImage hardcodes // m_bitsPerPixel to 8. If I do not do this Acrobat is unable to read the resultant PDF, Index: src/org/apache/fop/image/FopImageFactory.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/FopImageFactory.java,v retrieving revision 1.25.2.13 diff -u -w -w -u -r1.25.2.13 FopImageFactory.java --- src/org/apache/fop/image/FopImageFactory.java 10 May 2003 18:29:58 -0000 1.25.2.13 +++ src/org/apache/fop/image/FopImageFactory.java 5 Apr 2005 15:41:24 -0000 @@ -51,28 +51,111 @@ package org.apache.fop.image; // FOP -import org.apache.fop.image.analyser.ImageReaderFactory; -import org.apache.fop.image.analyser.ImageReader; -import org.apache.fop.configuration.Configuration; -import org.apache.fop.messaging.MessageHandler; - -// Java import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.MalformedURLException; import java.lang.reflect.Constructor; -import java.util.Map; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.fop.configuration.Configuration; +import org.apache.fop.image.analyser.ImageReader; +import org.apache.fop.image.analyser.ImageReaderFactory; +import org.apache.fop.messaging.MessageHandler; /** * create FopImage objects (with a configuration file - not yet implemented). + * + * This image cache does now support automatic clearing of the cache, if there + * are too many images inside the cache. This behaviour can be controlled by + * the following fop configuration parameters (stored e.g. with + * org.apache.fop.configuration.Configuration.put(param,value)): + *
+ * + * Note (1): if the max parameter is 0, the cache will not be cleared automatically + * (same behaviour as before). + *
+ *
+ * Note (2): the parameters must be stored as strings, because the Configuration object + * returns Integer object as -1! + * * @author Eric SCHAEFFER */ public class FopImageFactory { + // prevent instantiation - protected FopImageFactory() {} + protected FopImageFactory() {}; + + // contains the sorted cache entries + private static SortedSet m_imageCache = new TreeSet(); + + // contains the keys which point to the cache entries + private static HashMap m_urlMap=new HashMap(); + + /** + * The minimum cache size. + */ + private static int DEFAULT_MIN=25; - private static Map m_urlMap = new java.util.HashMap(); + /** + * The maximum cache size. + */ + private static int DEFAULT_MAX=100; + + /** + * The mimimum image cache size in bytes. + */ + private static long DEFAULT_MIN_SIZE=1048576; + + /** + * The maximum image cache size in bytes. + */ + private static long DEFAULT_MAX_SIZE=2097152; /** * The class name of the generic image handler. @@ -112,9 +195,44 @@ } // check if already created - FopImage imageObject = (FopImage)m_urlMap.get(href); - if (imageObject != null) - return imageObject; + // use the image cache + ImageCacheEntry cacheentry=(ImageCacheEntry)m_urlMap.get(href); + FopImage image=null; + if (cacheentry != null) + { + m_imageCache.remove(cacheentry); + + // this increases the modification count + image=cacheentry.getFopimage(); + + // re-sort (this can be inside this method, because it is synchronized + m_imageCache.add(cacheentry); + + return image; + } + + String _debug=org.apache.fop.configuration.Configuration.getStringValue("imagecache_debug"); + if (_debug==null) + _debug="false"; + boolean imagecache_debug=_debug.toLowerCase().startsWith("t"); + if (imagecache_debug) + { + StringBuffer sb=new StringBuffer(); + for (Iterator iterator=m_imageCache.iterator();iterator.hasNext();) + { + ImageCacheEntry ce=(ImageCacheEntry)iterator.next(); + + sb.append(" * "+ce.getUrl()+"\n"); + sb.append(" - image size: "+ce.getImageSize()+"\n"); + sb.append(" - last access: "+new java.util.Date(ce.getLastaccessTime())+"\n"); + } + + if (cacheentry==null) + MessageHandler.log("Adding new image: "+href); + else + MessageHandler.log("Found image: "+href); + MessageHandler.log("\n"+sb.toString()+"\n"); + } try { // try url as complete first, this can cause @@ -264,7 +382,62 @@ + "class " + imageClass.getName() + " doesn't implement org.apache.fop.image.FopImage interface"); } - m_urlMap.put(href, imageInstance); + + // BUGFIX: this checks the map size (otherwise we will always get out of memory + // errors or too man open files errors when building large pdf files) + cacheentry=new ImageCacheEntry((FopImage)imageInstance,href); + + int min=org.apache.fop.configuration.Configuration.getIntValue("imagecache_min"); + if (min<0) + min=DEFAULT_MIN; + int max=org.apache.fop.configuration.Configuration.getIntValue("imagecache_max"); + if (max<0) + max=DEFAULT_MAX; + long max_size=org.apache.fop.configuration.Configuration.getIntValue("imagecache_max_size"); + if (max_size<0) + max_size=DEFAULT_MAX_SIZE; + long min_size=org.apache.fop.configuration.Configuration.getIntValue("imagecache_min_size"); + if (min_size<0) + min_size=DEFAULT_MIN_SIZE; + + // get image cache size + long cachesize=0; + for (Iterator it=m_imageCache.iterator();it.hasNext();) + { + ImageCacheEntry ce=(ImageCacheEntry)it.next(); + if (ce.getImageSize()>=0) + cachesize+=ce.getImageSize(); + } + + if ((max!=0) && + ((m_imageCache.size()>max) || (max_size<=cachesize))) + { + if (m_imageCache.size()>max) + MessageHandler.log("clearing fop image cache (from "+m_imageCache.size()+" elements to "+min+" elements)"); + else + MessageHandler.log("clearing fop image cache (from "+cachesize+" byte to "+min_size+" byte)"); + + // cache size exceeded, shrink to min + do + { + ImageCacheEntry ce=(ImageCacheEntry)m_imageCache.first(); + + if (imagecache_debug) + MessageHandler.log(" * deleting "+ce.getUrl()+" ("+ce.getImageSize()+" byte)"); + + cachesize-=ce.getImageSize(); + m_imageCache.remove(ce); + m_urlMap.remove(ce.getUrl()); + + } while((m_imageCache.size()>min) || (min_size<=cachesize)); + } + + // add new entry to sorted map + m_imageCache.add(cacheentry); + + // and add to url map too + m_urlMap.put(href,cacheentry); + return (FopImage)imageInstance; } @@ -297,6 +470,16 @@ */ public static synchronized void resetCache() { m_urlMap.clear(); - } + m_imageCache.clear(); } + /** + * Returns the cached image count. + * + * @return the cached image count. + */ + public static int getCacheSize() + { + return m_urlMap.size(); + } +} \ No newline at end of file Index: src/org/apache/fop/image/JAIImage.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/JAIImage.java,v retrieving revision 1.2.2.1 diff -u -w -w -u -r1.2.2.1 JAIImage.java --- src/org/apache/fop/image/JAIImage.java 25 Feb 2003 13:38:22 -0000 1.2.2.1 +++ src/org/apache/fop/image/JAIImage.java 5 Apr 2005 15:41:24 -0000 @@ -51,23 +51,21 @@ package org.apache.fop.image; // Java -import java.net.URL; -import java.io.InputStream; - -// AWT +import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; -import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; -// JAI import javax.media.jai.JAI; import javax.media.jai.RenderedOp; -// Sun codec -import com.sun.media.jai.codec.FileCacheSeekableStream; -// FOP + import org.apache.fop.datatypes.ColorSpace; -import org.apache.fop.pdf.PDFColor; import org.apache.fop.image.analyser.ImageReader; +import org.apache.fop.pdf.PDFColor; + +import com.sun.media.jai.codec.FileCacheSeekableStream; /** * FopImage object using JAI. @@ -87,7 +85,13 @@ protected void loadImage() throws FopImageException { try { - InputStream inputStream = this.m_href.openStream(); + // BUGFIX: do always close the inputstream (the garbage collector may do this + // too late when handling pdfs with many images!!) + InputStream inputStream=null; + try + { + inputStream = this.m_href.openStream(); + /* * BufferedInputStream inputStream = this.m_imageReader.getInputStream(); * inputStream.reset(); @@ -180,6 +184,21 @@ & 0xFF); } } + } + finally + { + if (inputStream!=null) + { + try + { + inputStream.close(); + } + catch(IOException e) + { + // can be ignored + } + } + } } catch (Exception ex) { throw new FopImageException("Error while loading image " Index: src/org/apache/fop/image/JimiImage.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/JimiImage.java,v retrieving revision 1.8.2.3 diff -u -w -w -u -r1.8.2.3 JimiImage.java --- src/org/apache/fop/image/JimiImage.java 21 Sep 2003 11:50:53 -0000 1.8.2.3 +++ src/org/apache/fop/image/JimiImage.java 5 Apr 2005 15:41:24 -0000 @@ -51,18 +51,18 @@ package org.apache.fop.image; // Java -import java.net.URL; -import java.awt.image.ImageProducer; import java.awt.image.ColorModel; +import java.awt.image.ImageProducer; import java.awt.image.IndexColorModel; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; -// Jimi -import com.sun.jimi.core.Jimi; - -// FOP import org.apache.fop.datatypes.ColorSpace; -import org.apache.fop.pdf.PDFColor; import org.apache.fop.image.analyser.ImageReader; +import org.apache.fop.pdf.PDFColor; + +import com.sun.jimi.core.Jimi; /** * FopImage object for several images types, using Jimi. @@ -94,7 +94,13 @@ protected void loadImage() throws FopImageException { int[] tmpMap = null; try { - ImageProducer ip = Jimi.getImageProducer(this.m_href.openStream(), + // BUGFIX: do always close the inputstream (the garbage collector may do this + // too late when handling pdfs with many images!!) + InputStream inputStream=null; + try + { + inputStream=this.m_href.openStream(); + ImageProducer ip = Jimi.getImageProducer(inputStream, Jimi.SYNCHRONOUS | Jimi.IN_MEMORY); FopImageConsumer consumer = new FopImageConsumer(ip); @@ -170,6 +176,21 @@ } else { this.m_isTransparent = false; } + } + finally + { + if (inputStream!=null) + { + try + { + inputStream.close(); + } + catch(IOException e) + { + // can be ignored + } + } + } } catch (Exception ex) { throw new FopImageException("Error while loading image " + this.m_href.toString() + " : " Index: src/org/apache/fop/image/JpegImage.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/JpegImage.java,v retrieving revision 1.1.2.4 diff -u -w -w -u -r1.1.2.4 JpegImage.java --- src/org/apache/fop/image/JpegImage.java 27 Jul 2003 22:10:41 -0000 1.1.2.4 +++ src/org/apache/fop/image/JpegImage.java 5 Apr 2005 15:41:25 -0000 @@ -51,14 +51,14 @@ package org.apache.fop.image; // Java -import java.net.URL; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.net.URL; -// FOP import org.apache.fop.datatypes.ColorSpace; -import org.apache.fop.pdf.DCTFilter; import org.apache.fop.image.analyser.ImageReader; +import org.apache.fop.pdf.DCTFilter; /** * FopImage object for JPEG images, Using Java native classes. @@ -85,7 +85,7 @@ protected void loadImage() throws FopImageException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream iccStream = new ByteArrayOutputStream(); - InputStream inStream; + InputStream inStream=null; this.m_colorSpace = new ColorSpace(ColorSpace.DEVICE_UNKNOWN); byte[] readBuf = new byte[4096]; int bytes_read; @@ -95,6 +95,11 @@ this.m_compressionType = new DCTFilter(); this.m_compressionType.setApplied(true); + // BUGFIX: do always close the inputstream (the garbage collector may do this + // too late when handling pdfs with many images!!) + try + { + try { inStream = this.m_href.openStream(); @@ -227,6 +232,22 @@ throw new FopImageException( "\n1 Error while loading image " + this.m_href.toString() + " : JpegImage - Invalid JPEG Header."); } + } + finally + { + if (inStream!=null) + { + try + { + inStream.close(); + } + catch(IOException e) + { + // can be ignored + } + } + } + if (iccStream.size() > 0) { byte[] align = new byte[((iccStream.size()) % 8) + 8]; try {iccStream.write(align);} catch (Exception e) { Index: src/org/apache/fop/image/TiffImage.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/image/Attic/TiffImage.java,v retrieving revision 1.1.2.2 diff -u -w -w -u -r1.1.2.2 TiffImage.java --- src/org/apache/fop/image/TiffImage.java 25 Feb 2003 13:38:22 -0000 1.1.2.2 +++ src/org/apache/fop/image/TiffImage.java 5 Apr 2005 15:41:25 -0000 @@ -52,6 +52,7 @@ // Java import java.net.URL; +import java.io.IOException; import java.io.InputStream; // Sun codec @@ -83,7 +84,13 @@ protected void loadImage() throws FopImageException { try { - InputStream inputStream = this.m_href.openStream(); + InputStream inputStream = null; + // BUGFIX: do always close the inputstream (the garbage collector may do this + // too late when handling pdfs with many images!!) + try + { + inputStream=this.m_href.openStream(); + /* * BufferedInputStream inputStream = this.m_imageReader.getInputStream(); * inputStream.reset(); @@ -185,6 +192,21 @@ } this.m_bitmaps = readBuf; + } + finally + { + if (inputStream!=null) + { + try + { + inputStream.close(); + } + catch(IOException e) + { + // can be ignored + } + } + } } catch (FopImageException fie) { org.apache.fop.messaging.MessageHandler.logln("Reverting to TIFF image handling through JAI: " + fie.getMessage());