Removed support for Java 1.3 building as it didn't work anymore anyway. Added a check to require Java 1.4 for building. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-0_95@637791 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_95beta
@@ -24,8 +24,8 @@ | |||
# javac.debug = on | |||
# javac.optimize = off | |||
# javac.deprecation = on | |||
# javac.source = 1.3 | |||
# javac.target = 1.3 | |||
# javac.source = 1.4 | |||
# javac.target = 1.4 | |||
# javac.fork = on | |||
## JUnit task switches |
@@ -131,15 +131,6 @@ list of possible build targets. | |||
</fileset> | |||
</path> | |||
<patternset id="exclude-jimi"> | |||
<exclude name="org/apache/fop/image/JimiImage.java" unless="jimi.present"/> | |||
</patternset> | |||
<patternset id="exclude-jai"> | |||
<exclude name="org/apache/fop/image/JAIImage.java" unless="jai.present"/> | |||
<exclude name="org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java" unless="jai.present"/> | |||
</patternset> | |||
<patternset id="exclude-jce-dependencies"> | |||
<exclude name="org/apache/fop/pdf/PDFEncryptionJCE.java" unless="jce.present"/> | |||
</patternset> | |||
@@ -214,7 +205,7 @@ list of possible build targets. | |||
<!-- =================================================================== --> | |||
<!-- Initialization target --> | |||
<!-- =================================================================== --> | |||
<target name="init" depends="init-avail, init-filters-jdk14, init-filters-jdk13"> | |||
<target name="init" depends="init-avail"> | |||
</target> | |||
<target name="init-avail"> | |||
@@ -224,30 +215,6 @@ list of possible build targets. | |||
<echo message="VM: ${java.vm.version}, ${java.vm.vendor}"/> | |||
<echo message="JAVA_HOME: ${env.JAVA_HOME}"/> | |||
<available property="jimi.present" classname="com.sun.jimi.core.Jimi" | |||
classpathref="libs-build-classpath"/> | |||
<condition property="jimi.message" value="Jimi Support PRESENT"> | |||
<equals arg1="${jimi.present}" arg2="true"/> | |||
</condition> | |||
<condition property="jimi.message" value="Jimi Support NOT Present"> | |||
<not> | |||
<equals arg1="${jimi.present}" arg2="true"/> | |||
</not> | |||
</condition> | |||
<echo message="${jimi.message}"/> | |||
<available property="jai.present" classname="javax.media.jai.JAI" | |||
classpathref="libs-build-classpath"/> | |||
<condition property="jai.message" value="JAI Support PRESENT"> | |||
<equals arg1="${jai.present}" arg2="true"/> | |||
</condition> | |||
<condition property="jai.message" value="JAI Support NOT Present"> | |||
<not> | |||
<equals arg1="${jai.present}" arg2="true"/> | |||
</not> | |||
</condition> | |||
<echo message="${jai.message}"/> | |||
<available property="jce.present" classname="javax.crypto.Cipher" | |||
classpathref="libs-build-classpath"/> | |||
<condition property="jce.message" value="JCE Support PRESENT"> | |||
@@ -261,7 +228,8 @@ list of possible build targets. | |||
<echo message="${jce.message}"/> | |||
<available property="jdk14.present" classname="java.lang.CharSequence"/> | |||
<fail message="${Name} requires at least Java 1.4!" unless="jdk14.present"/> | |||
<available property="junit.present" classname="junit.framework.TestCase" | |||
classpathref="libs-build-classpath"/> | |||
<condition property="junit.message" value="JUnit Support PRESENT"> | |||
@@ -292,22 +260,6 @@ list of possible build targets. | |||
</target> | |||
<target name="init-filters-jdk13" depends="init-avail" unless="jdk14.present"> | |||
<echo message="Use GraphicsConfiguration adapter for JDK 1.3 or earlier."/> | |||
<path id="graphics-configuration-adapter"> | |||
<pathelement location="src/java-1.3"/> | |||
</path> | |||
<property name="src.java.version.dir" value="${basedir}/src/java-1.3"/> | |||
</target> | |||
<target name="init-filters-jdk14" depends="init-avail" if="jdk14.present"> | |||
<echo message="Use GraphicsConfiguration adapter for JDK 1.4."/> | |||
<path id="graphics-configuration-adapter"> | |||
<pathelement location="src/java-1.4"/> | |||
</path> | |||
<property name="src.java.version.dir" value="${basedir}/src/java-1.4"/> | |||
</target> | |||
<!-- =================================================================== --> | |||
<!-- Help on usage --> | |||
<!-- =================================================================== --> | |||
@@ -401,12 +353,8 @@ list of possible build targets. | |||
source="${javac.source}" target="${javac.target}"> | |||
<src path="${build.gensrc.dir}"/> | |||
<src path="${src.java.dir}"/> | |||
<src refid="graphics-configuration-adapter"/> | |||
<patternset includes="**/*.java"/> | |||
<!--patternset includes="org/apache/fop/svg/GraphicsConfiguration.java"/--> | |||
<patternset refid="exclude-jce-dependencies"/> | |||
<patternset refid="exclude-jai"/> | |||
<patternset refid="exclude-jimi"/> | |||
<classpath refid="libs-build-classpath"/> | |||
</javac> | |||
<copy todir="${build.classes.dir}"> | |||
@@ -431,7 +379,6 @@ list of possible build targets. | |||
source="${javac.source}" target="${javac.target}"> | |||
<src path="${src.sandbox.dir}"/> | |||
<patternset includes="**/*.java"/> | |||
<patternset refid="exclude-jai"/> | |||
<classpath> | |||
<path refid="libs-build-classpath"/> | |||
<pathelement location="${build.classes.dir}"/> | |||
@@ -543,7 +490,6 @@ list of possible build targets. | |||
<uptodate property="jar.sources.uptodate" targetfile="${build.dir}/fop-sources.jar"> | |||
<srcfiles dir="${build.gensrc.dir}"/> | |||
<srcfiles dir="${src.java.dir}"/> | |||
<srcfiles refid="graphics-configuration-adapter"/> | |||
</uptodate> | |||
</target> | |||
@@ -1032,7 +978,6 @@ NOTE: | |||
<pathelement path="${src.java.dir}"/> | |||
<pathelement path="${src.sandbox.dir}"/> | |||
<pathelement path="${build.gensrc.dir}"/> | |||
<path refid="graphics-configuration-adapter"/> | |||
</sourcepath> | |||
<tag name="todo" scope="all" description="To do:"/> | |||
<group title="Control and Startup"> | |||
@@ -1206,25 +1151,10 @@ NOTE: | |||
--> | |||
<echo message="Make sure you have a proper Forrest installation (see http://forrest.apache.org/)"/> | |||
<!--<antcall target="site"/>--> | |||
<!-- You can provide a JDK 1.4 for a JDK 1.3 build by adding "javahome.jdk14" to build-local.properties --> | |||
<condition property="javahome.jdk14.override" value="${javahome.jdk14}"> | |||
<isset property="javahome.jdk14"/> | |||
</condition> | |||
<echo message="java home: ${javahome.jdk14.override}"/> | |||
<condition property="javahome.jdk14.override" value="${env.JAVA_HOME}"> | |||
<not> | |||
<isset property="javahome.jdk14.override"/> | |||
</not> | |||
</condition> | |||
<echo message="java home: ${javahome.jdk14.override}"/> | |||
<condition property="forrest.call" value="forrest.bat" else="forrest"> | |||
<os family="windows"/> | |||
</condition> | |||
<exec executable="${forrest.call}"> | |||
<env key="JAVA_HOME" value="${javahome.jdk14.override}"/> | |||
</exec> | |||
<exec executable="${forrest.call}"/> | |||
</target> | |||
<!-- =================================================================== --> | |||
@@ -1234,8 +1164,6 @@ NOTE: | |||
<target name="dist-bin" depends="all,javadocs,docs"> | |||
<echo message="Building the binary distribution files (zip,tar)"/> | |||
<fail message="A complete binary build requires Jimi" unless="jimi.present"/> | |||
<fail message="A complete binary build requires JAI" unless="jai.present"/> | |||
<fail message="A complete binary build requires JCE" unless="jce.present"/> | |||
<mkdir dir="${dist.bin.result.dir}"/> | |||
<copy todir="${dist.bin.result.dir}"> |
@@ -1,31 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.svg; | |||
/** | |||
* Adapter to allow subclassing java.awt.GraphicsConfiguration without | |||
* compilation errors. | |||
* The version for JDK 1.3 is just empty. | |||
* | |||
*/ | |||
abstract public class GraphicsConfiguration extends java.awt.GraphicsConfiguration { | |||
} |
@@ -1,237 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// AWT | |||
import java.awt.Color; | |||
import java.awt.color.ColorSpace; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.image.BufferedImage; | |||
import java.util.Iterator; | |||
// ImageIO | |||
import javax.imageio.ImageIO; | |||
import javax.imageio.ImageReadParam; | |||
import javax.imageio.ImageReader; | |||
import javax.imageio.metadata.IIOMetadata; | |||
import javax.imageio.stream.ImageInputStream; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.fop.util.UnitConv; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.NodeList; | |||
/** | |||
* FopImage object using ImageIO. | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class ImageIOImage extends AbstractFopImage { | |||
private byte[] softMask = null; | |||
/** | |||
* Creates a new ImageIOImage. | |||
* @param info the image info from the ImageReader | |||
*/ | |||
public ImageIOImage(FopImage.ImageInfo info) { | |||
super(info); | |||
if ("image/png".equals(info.mimeType) | |||
|| "image/tiff".equals(info.mimeType)) { | |||
this.loaded = 0; //TODO The PNG and TIFF Readers cannot read the resolution, yet. | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.image.AbstractFopImage#loadDimensions() | |||
*/ | |||
protected boolean loadDimensions() { | |||
if (this.bitmaps == null) { | |||
return loadBitmap(); | |||
} | |||
return true; | |||
} | |||
private Element getChild(Element el, String name) { | |||
NodeList nodes = el.getElementsByTagName(name); | |||
if (nodes.getLength() > 0) { | |||
return (Element)nodes.item(0); | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** @see org.apache.fop.image.AbstractFopImage#loadBitmap() */ | |||
protected boolean loadBitmap() { | |||
if (this.bitmaps != null) { | |||
return true; | |||
} | |||
try { | |||
inputStream.reset(); | |||
ImageInputStream imgStream = ImageIO.createImageInputStream(inputStream); | |||
Iterator iter = ImageIO.getImageReaders(imgStream); | |||
if (!iter.hasNext()) { | |||
log.error("No ImageReader found."); | |||
return false; | |||
} | |||
ImageReader reader = (ImageReader)iter.next(); | |||
ImageReadParam param = reader.getDefaultReadParam(); | |||
reader.setInput(imgStream, true, false); | |||
BufferedImage imageData = reader.read(0, param); | |||
//Read image resolution | |||
IIOMetadata iiometa = reader.getImageMetadata(0); | |||
if (iiometa != null && iiometa.isStandardMetadataFormatSupported()) { | |||
Element metanode = (Element)iiometa.getAsTree("javax_imageio_1.0"); | |||
Element dim = getChild(metanode, "Dimension"); | |||
if (dim != null) { | |||
Element child; | |||
child = getChild(dim, "HorizontalPixelSize"); | |||
if (child != null) { | |||
this.dpiHorizontal = UnitConv.IN2MM | |||
/ Float.parseFloat(child.getAttribute("value")); | |||
} | |||
child = getChild(dim, "VerticalPixelSize"); | |||
if (child != null) { | |||
this.dpiVertical = UnitConv.IN2MM | |||
/ Float.parseFloat(child.getAttribute("value")); | |||
} | |||
} | |||
} | |||
imgStream.close(); | |||
reader.dispose(); | |||
this.height = imageData.getHeight(); | |||
this.width = imageData.getWidth(); | |||
ColorModel cm = imageData.getColorModel(); | |||
this.bitsPerPixel = cm.getComponentSize(0); //only use first, we assume all are equal | |||
//this.colorSpace = cm.getColorSpace(); | |||
//We currently force the image to sRGB | |||
this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); | |||
int[] tmpMap = imageData.getRGB(0, 0, this.width, | |||
this.height, null, 0, this.width); | |||
if (cm.hasAlpha()) { | |||
// java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT | |||
int transparencyType = cm.getTransparency(); | |||
if (transparencyType == java.awt.Transparency.OPAQUE) { | |||
this.isTransparent = false; | |||
} else if (transparencyType == java.awt.Transparency.BITMASK) { | |||
if (cm instanceof IndexColorModel) { | |||
this.isTransparent = false; | |||
byte[] alphas = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] reds = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] greens = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] blues = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
((IndexColorModel) cm).getAlphas(alphas); | |||
((IndexColorModel) cm).getReds(reds); | |||
((IndexColorModel) cm).getGreens(greens); | |||
((IndexColorModel) cm).getBlues(blues); | |||
for (int i = 0; | |||
i < ((IndexColorModel) cm).getMapSize(); | |||
i++) { | |||
if ((alphas[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
this.transparentColor = new Color( | |||
(int)(reds[i] & 0xFF), | |||
(int)(greens[i] & 0xFF), | |||
(int)(blues[i] & 0xFF)); | |||
break; | |||
} | |||
} | |||
} else { | |||
//TODO Is there another case? | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
// TRANSLUCENT | |||
this.softMask = new byte[width * height]; | |||
imageData.getAlphaRaster().getDataElements( | |||
0, 0, width, height, this.softMask); | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
for (int i = 0; i < this.height; i++) { | |||
for (int j = 0; j < this.width; j++) { | |||
int p = tmpMap[i * this.width + j]; | |||
int r = (p >> 16) & 0xFF; | |||
int g = (p >> 8) & 0xFF; | |||
int b = (p) & 0xFF; | |||
this.bitmaps[3 * (i * this.width + j)] | |||
= (byte)(r & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 1] | |||
= (byte)(g & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 2] | |||
= (byte)(b & 0xFF); | |||
} | |||
} | |||
} catch (Exception ex) { | |||
log.error("Error while loading image: " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
return true; | |||
} | |||
/** @see org.apache.fop.image.AbstractFopImage#loadOriginalData() */ | |||
protected boolean loadOriginalData() { | |||
if (inputStream == null && getBitmaps() != null) { | |||
return false; | |||
} else { | |||
return loadDefaultOriginalData(); | |||
} | |||
} | |||
/** @see org.apache.fop.image.FopImage#hasSoftMask() */ | |||
public boolean hasSoftMask() { | |||
if (this.bitmaps == null && this.raw == null) { | |||
loadBitmap(); | |||
} | |||
return (this.softMask != null); | |||
} | |||
/** @see org.apache.fop.image.FopImage#getSoftMask() */ | |||
public byte[] getSoftMask() { | |||
if (this.bitmaps == null) { | |||
loadBitmap(); | |||
} | |||
return this.softMask; | |||
} | |||
} | |||
@@ -1,161 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// AWT | |||
import java.awt.Color; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.image.BufferedImage; | |||
// ImageIO | |||
import javax.imageio.ImageIO; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* FopImage object using ImageIO. | |||
* Special class to allow the use of JpegImage for those | |||
* renderers which can embed Jpeg directly but for renderers | |||
* which require the decoded data this class delivers it. | |||
* @see AbstractFopImage | |||
* @see JpegImage | |||
*/ | |||
public class JpegImageIOImage extends JpegImage { | |||
/** | |||
* Creates a new JpegImageIOImage. | |||
* @param info the image info from the ImageReader | |||
*/ | |||
public JpegImageIOImage(FopImage.ImageInfo info) { | |||
super(info); | |||
} | |||
/** | |||
* @see org.apache.fop.image.AbstractFopImage#loadDimensions() | |||
*/ | |||
protected boolean loadDimensions() { | |||
if (this.bitmaps == null) { | |||
return loadBitmap(); | |||
} | |||
return true; | |||
} | |||
/** @see org.apache.fop.image.AbstractFopImage#loadBitmap() */ | |||
protected boolean loadBitmap() { | |||
try { | |||
inputStream.reset(); | |||
BufferedImage imageData = ImageIO.read(inputStream); | |||
this.height = imageData.getHeight(); | |||
this.width = imageData.getWidth(); | |||
ColorModel cm = imageData.getColorModel(); | |||
this.bitsPerPixel = cm.getComponentSize(0); //only use first, we assume all are equal | |||
this.colorSpace = cm.getColorSpace(); | |||
int[] tmpMap = imageData.getRGB(0, 0, this.width, | |||
this.height, null, 0, this.width); | |||
if (cm.hasAlpha()) { | |||
// java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT | |||
int transparencyType = cm.getTransparency(); | |||
if (transparencyType == java.awt.Transparency.OPAQUE) { | |||
this.isTransparent = false; | |||
} else if (transparencyType == java.awt.Transparency.BITMASK) { | |||
if (cm instanceof IndexColorModel) { | |||
this.isTransparent = false; | |||
byte[] alphas = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] reds = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] greens = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] blues = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
((IndexColorModel) cm).getAlphas(alphas); | |||
((IndexColorModel) cm).getReds(reds); | |||
((IndexColorModel) cm).getGreens(greens); | |||
((IndexColorModel) cm).getBlues(blues); | |||
for (int i = 0; | |||
i < ((IndexColorModel) cm).getMapSize(); | |||
i++) { | |||
if ((alphas[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
this.transparentColor = new Color( | |||
(int)(reds[i] & 0xFF), | |||
(int)(greens[i] & 0xFF), | |||
(int)(blues[i] & 0xFF)); | |||
break; | |||
} | |||
} | |||
} else { | |||
// TRANSLUCENT | |||
/* | |||
* this.isTransparent = false; | |||
* for (int i = 0; i < this.width * this.height; i++) { | |||
* if (cm.getAlpha(tmpMap[i]) == 0) { | |||
* this.isTransparent = true; | |||
* this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), | |||
* cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); | |||
* break; | |||
* } | |||
* } | |||
* // or use special API... | |||
*/ | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
for (int i = 0; i < this.height; i++) { | |||
for (int j = 0; j < this.width; j++) { | |||
int p = tmpMap[i * this.width + j]; | |||
int r = (p >> 16) & 0xFF; | |||
int g = (p >> 8) & 0xFF; | |||
int b = (p) & 0xFF; | |||
this.bitmaps[3 * (i * this.width + j)] | |||
= (byte)(r & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 1] | |||
= (byte)(g & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 2] | |||
= (byte)(b & 0xFF); | |||
} | |||
} | |||
} catch (Exception ex) { | |||
log.error("Error while loading image: " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
return true; | |||
} | |||
} | |||
@@ -46,7 +46,6 @@ import org.apache.fop.fo.ElementMapping; | |||
import org.apache.fop.fo.ElementMappingRegistry; | |||
import org.apache.fop.fonts.FontCache; | |||
import org.apache.fop.hyphenation.HyphenationTreeResolver; | |||
import org.apache.fop.image.ImageFactory; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
import org.apache.fop.render.RendererFactory; | |||
import org.apache.fop.render.XMLHandlerRegistry; | |||
@@ -82,9 +81,6 @@ public class FopFactory implements ImageContext { | |||
private ColorSpaceCache colorSpaceCache = null; | |||
/** Image factory for creating fop image objects */ | |||
private ImageFactory imageFactory; | |||
/** Image manager for loading and caching image objects */ | |||
private ImageManager imageManager; | |||
@@ -155,7 +151,6 @@ public class FopFactory implements ImageContext { | |||
this.elementMappingRegistry = new ElementMappingRegistry(this); | |||
this.foURIResolver = new FOURIResolver(validateUserConfigStrictly()); | |||
this.colorSpaceCache = new ColorSpaceCache(foURIResolver); | |||
this.imageFactory = new ImageFactory(); | |||
this.imageManager = new ImageManager(this); | |||
this.rendererFactory = new RendererFactory(); | |||
this.xmlHandlers = new XMLHandlerRegistry(); | |||
@@ -290,11 +285,6 @@ public class FopFactory implements ImageContext { | |||
return this.contentHandlerFactoryRegistry; | |||
} | |||
/** @return the image factory */ | |||
public ImageFactory getImageFactory() { | |||
return this.imageFactory; | |||
} | |||
/** | |||
* Returns the image manager. | |||
* @return the image manager |
@@ -412,6 +412,7 @@ public final class FOPropertyMapping implements Constants { | |||
l.setInherited(false); | |||
l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); | |||
l.setDefault("auto"); | |||
l.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT); | |||
addPropertyMaker("top", l); | |||
// right | |||
@@ -419,6 +420,7 @@ public final class FOPropertyMapping implements Constants { | |||
l.setInherited(false); | |||
l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); | |||
l.setDefault("auto"); | |||
l.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); | |||
addPropertyMaker("right", l); | |||
// bottom | |||
@@ -426,6 +428,7 @@ public final class FOPropertyMapping implements Constants { | |||
l.setInherited(false); | |||
l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); | |||
l.setDefault("auto"); | |||
l.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT); | |||
addPropertyMaker("bottom", l); | |||
// left | |||
@@ -433,6 +436,7 @@ public final class FOPropertyMapping implements Constants { | |||
l.setInherited(false); | |||
l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); | |||
l.setDefault("auto"); | |||
l.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); | |||
addPropertyMaker("left", l); | |||
} | |||
@@ -1161,6 +1165,7 @@ public final class FOPropertyMapping implements Constants { | |||
m = new LengthProperty.Maker(PR_START_INDENT); | |||
m.setInherited(true); | |||
m.setDefault("0pt"); | |||
m.setPercentBase(LengthBase.CONTAINING_REFAREA_WIDTH); | |||
IndentPropertyMaker sCorr = new IndentPropertyMaker(m); | |||
sCorr.setCorresponding(PR_MARGIN_LEFT, PR_MARGIN_RIGHT, PR_MARGIN_TOP); | |||
sCorr.setUseParent(false); | |||
@@ -1177,6 +1182,7 @@ public final class FOPropertyMapping implements Constants { | |||
m = new LengthProperty.Maker(PR_END_INDENT); | |||
m.setInherited(true); | |||
m.setDefault("0pt"); | |||
m.setPercentBase(LengthBase.CONTAINING_REFAREA_WIDTH); | |||
IndentPropertyMaker eCorr = new IndentPropertyMaker(m); | |||
eCorr.setCorresponding(PR_MARGIN_RIGHT, PR_MARGIN_LEFT, PR_MARGIN_BOTTOM); | |||
eCorr.setUseParent(false); |
@@ -38,7 +38,6 @@ import org.apache.fop.area.AreaTreeHandler; | |||
import org.apache.fop.fo.ElementMapping.Maker; | |||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | |||
import org.apache.fop.fo.pagination.Root; | |||
import org.apache.fop.image.ImageFactory; | |||
import org.apache.fop.util.ContentHandlerFactory; | |||
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; | |||
import org.apache.fop.util.ContentHandlerFactory.ObjectSource; | |||
@@ -155,10 +154,6 @@ public class FOTreeBuilder extends DefaultHandler { | |||
log.debug("Parsing of document complete"); | |||
} | |||
foEventHandler.endDocument(); | |||
//Notify the image factory that this user agent has expired. | |||
ImageFactory imageFactory = userAgent.getFactory().getImageFactory(); | |||
imageFactory.removeContext(this.userAgent); | |||
} | |||
/** {@inheritDoc} */ |
@@ -1,376 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.awt.color.ColorSpace; | |||
import java.awt.color.ICC_ColorSpace; | |||
import java.awt.color.ICC_Profile; | |||
import java.io.InputStream; | |||
import java.awt.Color; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.datatypes.Length; | |||
/** | |||
* Base class to implement the FopImage interface. | |||
* | |||
* @see FopImage | |||
*/ | |||
public abstract class AbstractFopImage implements FopImage { | |||
/** | |||
* logging instance | |||
*/ | |||
protected static Log log = LogFactory.getLog(AbstractFopImage.class); | |||
/** | |||
* Keeps track of what has been loaded. | |||
*/ | |||
protected int loaded = 0; | |||
/** | |||
* Image width (in pixel). | |||
*/ | |||
protected int width = 0; | |||
/** | |||
* Image height (in pixel). | |||
*/ | |||
protected int height = 0; | |||
/** Horizontal bitmap resolution (in dpi) */ | |||
protected double dpiHorizontal = 72.0f; | |||
/** Vertical bitmap resolution (in dpi) */ | |||
protected double dpiVertical = 72.0f; | |||
/** | |||
* Image input stream. | |||
*/ | |||
protected InputStream inputStream = null; | |||
/** | |||
* ImageReader object (to obtain image header informations). | |||
*/ | |||
protected FopImage.ImageInfo imageInfo = null; | |||
/** | |||
* Image color space (java.awt.color.ColorSpace). | |||
*/ | |||
protected ColorSpace colorSpace = null; | |||
/** | |||
* Bits per pixel. | |||
*/ | |||
protected int bitsPerPixel = 0; | |||
/** | |||
* Image data (pixels, uncompressed). | |||
*/ | |||
protected byte[] bitmaps = null; | |||
/** | |||
* Image data (undecoded, compressed, for image formats that can be embedded without decoding. | |||
*/ | |||
protected byte[] raw = null; | |||
/** | |||
* Image transparency. | |||
*/ | |||
protected boolean isTransparent = false; | |||
/** | |||
* Transparent color (java.awt.Color). | |||
*/ | |||
protected Color transparentColor = null; | |||
/** | |||
* Photoshop generated CMYK JPEGs are inverted. | |||
*/ | |||
protected boolean invertImage = false; | |||
/** | |||
* Constructor. | |||
* Construct a new FopImage object and initialize its default properties: | |||
* <UL> | |||
* <LI>image width | |||
* <LI>image height | |||
* </UL> | |||
* The image data isn't kept in memory. | |||
* @param info image information | |||
*/ | |||
public AbstractFopImage(FopImage.ImageInfo info) { | |||
this.inputStream = info.inputStream; | |||
this.imageInfo = info; | |||
if (this.imageInfo.width != -1) { | |||
width = imageInfo.width; | |||
height = imageInfo.height; | |||
dpiHorizontal = imageInfo.dpiHorizontal; | |||
dpiVertical = imageInfo.dpiVertical; | |||
loaded = loaded | DIMENSIONS; | |||
} | |||
} | |||
/** | |||
* Get the mime type for this image. | |||
* | |||
* @return the mime type for the image | |||
*/ | |||
public String getMimeType() { | |||
return imageInfo.mimeType; | |||
} | |||
/** {@inheritDoc} */ | |||
public String getOriginalURI() { | |||
return this.imageInfo.originalURI; | |||
} | |||
/** | |||
* Load image data and initialize its properties. | |||
* | |||
* @param type the type of loading to do | |||
* @return true if the loading was successful | |||
*/ | |||
public synchronized boolean load(int type) { | |||
if ((loaded & type) != 0) { | |||
return true; | |||
} | |||
boolean success = true; | |||
if (((type & DIMENSIONS) != 0) && ((loaded & DIMENSIONS) == 0)) { | |||
success = success && loadDimensions(); | |||
if (!success) { | |||
return false; | |||
} | |||
loaded = loaded | DIMENSIONS; | |||
} | |||
if (((type & BITMAP) != 0) && ((loaded & BITMAP) == 0)) { | |||
success = success && loadBitmap(); | |||
if (success) { | |||
loaded = loaded | BITMAP; | |||
} | |||
} | |||
if (((type & ORIGINAL_DATA) != 0) && ((loaded & ORIGINAL_DATA) == 0)) { | |||
success = success && loadOriginalData(); | |||
if (success) { | |||
loaded = loaded | ORIGINAL_DATA; | |||
} | |||
} | |||
return success; | |||
} | |||
/** | |||
* Load the dimensions of the image. | |||
* All implementations should override this to get and | |||
* return the dimensions. | |||
* | |||
* @return true if the loading was successful | |||
*/ | |||
protected boolean loadDimensions() { | |||
return false; | |||
} | |||
/** | |||
* Load a bitmap array of the image. | |||
* If the renderer requires a bitmap image then the | |||
* implementations should override this to load the bitmap. | |||
* | |||
* @return true if the loading was successful | |||
*/ | |||
protected boolean loadBitmap() { | |||
return false; | |||
} | |||
/** | |||
* Load the original image data. | |||
* In some cases the original data can be used by the renderer. | |||
* This should load the data and any other associated information. | |||
* | |||
* @return true if the loading was successful | |||
*/ | |||
protected boolean loadOriginalData() { | |||
return false; | |||
} | |||
/** | |||
* Load the original image data. This is generic code for use by any | |||
* subclass that wants to use this from a loadOriginalData() implementation. | |||
* | |||
* @return true if the loading was successful | |||
*/ | |||
protected boolean loadDefaultOriginalData() { | |||
if (inputStream == null) { | |||
throw new IllegalStateException("inputStream is already null or was never set"); | |||
} | |||
try { | |||
this.raw = IOUtils.toByteArray(inputStream); | |||
} catch (java.io.IOException ex) { | |||
log.error("Error while reading raw image: " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
return true; | |||
} | |||
/** | |||
* @return the image width (in pixels) | |||
*/ | |||
public int getWidth() { | |||
return this.width; | |||
} | |||
/** | |||
* @return the image height (in pixels) | |||
*/ | |||
public int getHeight() { | |||
return this.height; | |||
} | |||
/** {@inheritDoc} */ | |||
public int getIntrinsicWidth() { | |||
return (int)(getWidth() * 72000 / getHorizontalResolution()); | |||
} | |||
/** {@inheritDoc} */ | |||
public int getIntrinsicHeight() { | |||
return (int)(getHeight() * 72000 / getVerticalResolution()); | |||
} | |||
/** {@inheritDoc} */ | |||
public Length getIntrinsicAlignmentAdjust() { | |||
return this.imageInfo.alignmentAdjust; | |||
} | |||
/** {@inheritDoc} */ | |||
public double getHorizontalResolution() { | |||
return this.dpiHorizontal; | |||
} | |||
/** {@inheritDoc} */ | |||
public double getVerticalResolution() { | |||
return this.dpiVertical; | |||
} | |||
/** | |||
* Return the image color space. | |||
* @return the image color space (java.awt.color.ColorSpace) | |||
*/ | |||
public ColorSpace getColorSpace() { | |||
return this.colorSpace; | |||
} | |||
/** | |||
* Get ICC profile for this image. | |||
* @return the icc profile or null if not applicable | |||
*/ | |||
public ICC_Profile getICCProfile() { | |||
if (this.colorSpace != null && this.colorSpace instanceof ICC_ColorSpace) { | |||
return ((ICC_ColorSpace)this.colorSpace).getProfile(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* Return the number of bits per pixel. | |||
* @return number of bits per pixel | |||
*/ | |||
public int getBitsPerPixel() { | |||
return this.bitsPerPixel; | |||
} | |||
/** | |||
* Return the image transparency. | |||
* @return true if the image is transparent | |||
*/ | |||
public boolean isTransparent() { | |||
return this.isTransparent; | |||
} | |||
/** | |||
* Check if this image has a soft mask. | |||
* | |||
* @return true if the image also has a soft transparency mask | |||
*/ | |||
public boolean hasSoftMask() { | |||
return false; | |||
} | |||
/** | |||
* Get the soft mask. | |||
* The soft mask should have the same bitdepth as the image data. | |||
* | |||
* @return the data array of soft mask values | |||
*/ | |||
public byte[] getSoftMask() { | |||
return null; | |||
} | |||
/** | |||
* Return the transparent color. | |||
* @return the transparent color (java.awt.Color) | |||
*/ | |||
public Color getTransparentColor() { | |||
return this.transparentColor; | |||
} | |||
/** @return true for CMYK images generated by Adobe Photoshop */ | |||
public boolean isInverted() { | |||
return this.invertImage; | |||
} | |||
/** | |||
* Return the image data (pixels, uncompressed). | |||
* @return the image data | |||
*/ | |||
public byte[] getBitmaps() { | |||
return this.bitmaps; | |||
} | |||
/** | |||
* Return the image data size (number of bytes taken up by the uncompressed pixels). | |||
* @return the image data size | |||
*/ | |||
public int getBitmapsSize() { | |||
return (bitmaps != null ? bitmaps.length : 0); | |||
} | |||
/** | |||
* Return the original image data (compressed). | |||
* @return the original image data | |||
*/ | |||
public byte[] getRessourceBytes() { | |||
return raw; | |||
} | |||
/** | |||
* Return the original image data size (compressed). | |||
* @return the original image data size | |||
*/ | |||
public int getRessourceBytesSize() { | |||
return (raw != null ? raw.length : 0); | |||
} | |||
} | |||
@@ -1,220 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.io.IOException; | |||
import java.awt.color.ColorSpace; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* Bitmap image. | |||
* This supports loading a bitmap image into bitmap data. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class BmpImage extends AbstractFopImage { | |||
/** | |||
* Create a bitmap image with the image data. | |||
* | |||
* @param imgInfo the image information | |||
*/ | |||
public BmpImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* Load the bitmap. | |||
* This laods the bitmap data from the bitmap image. | |||
* | |||
* @return true if it was loaded successfully | |||
*/ | |||
protected boolean loadBitmap() { | |||
int wpos = 18; | |||
int hpos = 22; // offset positioning for w and height in bmp files | |||
int[] headermap = new int[54]; | |||
int filepos = 0; | |||
byte[] palette = null; | |||
try { | |||
boolean eof = false; | |||
while ((!eof) && (filepos < 54)) { | |||
int input = inputStream.read(); | |||
if (input == -1) { | |||
eof = true; | |||
} else { | |||
headermap[filepos++] = input; | |||
} | |||
} | |||
if (headermap[28] == 4 || headermap[28] == 8) { | |||
int palettesize = 1 << headermap[28]; | |||
palette = new byte[palettesize * 3]; | |||
int countr = 0; | |||
while (!eof && countr < palettesize) { | |||
int count2 = 2; | |||
while (!eof && count2 >= -1) { | |||
int input = inputStream.read(); | |||
if (input == -1) { | |||
eof = true; | |||
} else if (count2 >= 0) { | |||
palette[countr * 3 + count2] = (byte)(input & 0xFF); | |||
} | |||
count2--; | |||
filepos++; | |||
} | |||
countr++; | |||
} | |||
} | |||
} catch (IOException ex) { | |||
log.error("Error while loading image (Bmp): " + ex.getMessage(), ex); | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
return false; | |||
} | |||
// gets h & w from headermap | |||
this.width = headermap[wpos] | |||
+ headermap[wpos + 1] * 256 | |||
+ headermap[wpos + 2] * 256 * 256 | |||
+ headermap[wpos + 3] * 256 * 256 * 256; | |||
this.height = headermap[hpos] | |||
+ headermap[hpos + 1] * 256 | |||
+ headermap[hpos + 2] * 256 * 256 | |||
+ headermap[hpos + 3] * 256 * 256 * 256; | |||
int imagestart = headermap[10] | |||
+ headermap[11] * 256 | |||
+ headermap[12] * 256 * 256 | |||
+ headermap[13] * 256 * 256 * 256; | |||
this.bitsPerPixel = headermap[28]; | |||
this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); | |||
int bytes = 0; | |||
if (this.bitsPerPixel == 1) { | |||
bytes = (this.width + 7) / 8; | |||
} else if (this.bitsPerPixel == 24) { | |||
bytes = this.width * 3; | |||
} else if (this.bitsPerPixel == 4 || this.bitsPerPixel == 8) { | |||
bytes = this.width / (8 / this.bitsPerPixel); | |||
} else { | |||
log.error("Image (" + "" | |||
+ ") has " + this.bitsPerPixel | |||
+ " which is not a supported BMP format."); | |||
return false; | |||
} | |||
if ((bytes & 0x03) != 0) { | |||
bytes |= 0x03; | |||
bytes++; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
int[] temp = new int[bytes * this.height]; | |||
try { | |||
int input; | |||
int count = 0; | |||
inputStream.skip((long)(imagestart - filepos)); | |||
while ((input = inputStream.read()) != -1) { | |||
if (count >= temp.length) { | |||
log.warn("Data longer than expected while loading image"); | |||
break; | |||
} else { | |||
temp[count++] = input; | |||
} | |||
} | |||
} catch (IOException ex) { | |||
log.error("Error while loading image (Bmp): " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
for (int i = 0; i < this.height; i++) { | |||
int x = 0; | |||
int j = 0; | |||
while (j < bytes) { | |||
int p = temp[(this.height - i - 1) * bytes + j]; | |||
if (this.bitsPerPixel == 24 && x < this.width) { | |||
int countr = 2; | |||
do { | |||
this.bitmaps[3 * (i * this.width + x) + countr] | |||
= (byte)(temp[(this.height - i - 1) * bytes + j] & 0xFF); | |||
j++; | |||
} while (--countr >= 0) | |||
; | |||
x++; | |||
} else if (this.bitsPerPixel == 1) { | |||
for (int countr = 0; | |||
countr < 8 && x < this.width; countr++) { | |||
if ((p & 0x80) != 0) { | |||
this.bitmaps[3 * (i * this.width + x)] = (byte) 0xFF; | |||
this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0xFF; | |||
this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0xFF; | |||
} else { | |||
this.bitmaps[3 * (i * this.width + x)] = (byte) 0; | |||
this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0; | |||
this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0; | |||
} | |||
p <<= 1; | |||
x++; | |||
} | |||
j++; | |||
} else if (this.bitsPerPixel == 4) { | |||
for (int countr = 0; | |||
countr < 2 && x < this.width; countr++) { | |||
int pal = ((p & 0xF0) >> 4) * 3; | |||
this.bitmaps[3 * (i * this.width + x)] = palette[pal]; | |||
this.bitmaps[3 * (i * this.width + x) + 1] = palette[pal + 1]; | |||
this.bitmaps[3 * (i * this.width + x) + 2] = palette[pal + 2]; | |||
p <<= 4; | |||
x++; | |||
} | |||
j++; | |||
} else if (this.bitsPerPixel == 8) { | |||
if (x < this.width) { | |||
p *= 3; | |||
this.bitmaps[3 * (i * this.width + x)] = palette[p]; | |||
this.bitmaps[3 * (i * this.width + x) + 1] = palette[p + 1]; | |||
this.bitmaps[3 * (i * this.width + x) + 2] = palette[p + 2]; | |||
j++; | |||
x++; | |||
} else { | |||
j = bytes; | |||
} | |||
} else { | |||
j++; | |||
} | |||
} | |||
} | |||
// This seems really strange to me, but I noticed that | |||
// JimiImage hardcodes bitsPerPixel to 8. If I do not | |||
// do this Acrobat is unable to read the resultant PDF, | |||
// so we will hardcode this... | |||
this.bitsPerPixel = 8; | |||
return true; | |||
} | |||
} | |||
@@ -1,122 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
/** | |||
* EPS image handler. | |||
* This handles the Encapulated PostScript images. | |||
* It gets the dimensions and original data from the analyser. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class EPSImage extends AbstractFopImage { | |||
private String docName; | |||
private int[] bbox; | |||
private EPSData epsData = null; | |||
/** | |||
* Create an EPS image with the image information. | |||
* | |||
* @param imgInfo the information containing the data and bounding box | |||
*/ | |||
public EPSImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
init(""); | |||
if (imgInfo.data instanceof EPSData) { | |||
epsData = (EPSData) imgInfo.data; | |||
bbox = new int[4]; | |||
bbox[0] = (int) epsData.bbox[0]; | |||
bbox[1] = (int) epsData.bbox[1]; | |||
bbox[2] = (int) epsData.bbox[2]; | |||
bbox[3] = (int) epsData.bbox[3]; | |||
loaded = loaded | ORIGINAL_DATA; | |||
} | |||
} | |||
/** | |||
* Initialize docName and bounding box. | |||
* @param name the document name | |||
*/ | |||
private void init(String name) { | |||
bbox = new int[4]; | |||
bbox[0] = 0; | |||
bbox[1] = 0; | |||
bbox[2] = 0; | |||
bbox[3] = 0; | |||
docName = name; | |||
} | |||
/** | |||
* Return the name of the eps | |||
* @return the name of the eps | |||
*/ | |||
public String getDocName() { | |||
return docName; | |||
} | |||
/** | |||
* Return the bounding box | |||
* @return an int array containing the bounding box | |||
*/ | |||
public int[] getBBox() { | |||
return bbox; | |||
} | |||
/** | |||
* Get the eps image. | |||
* | |||
* @return the original eps image data | |||
*/ | |||
public byte[] getEPSImage() { | |||
if (epsData.epsFile == null) { | |||
//log.error("ERROR LOADING EXTERNAL EPS"); | |||
} | |||
return epsData.epsFile; | |||
} | |||
/** | |||
* Data for EPS image. | |||
*/ | |||
public static class EPSData { | |||
public long[] bbox; | |||
public boolean isAscii; // True if plain ascii eps file | |||
// offsets if not ascii | |||
public long psStart = 0; | |||
public long psLength = 0; | |||
public long wmfStart = 0; | |||
public long wmfLength = 0; | |||
public long tiffStart = 0; | |||
public long tiffLength = 0; | |||
/** raw eps file */ | |||
public byte[] rawEps; | |||
/** eps part */ | |||
public byte[] epsFile; | |||
public byte[] preview = null; | |||
} | |||
} |
@@ -1,51 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
/** | |||
* Enhanced metafile image. | |||
* This supports loading a EMF image. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class EmfImage extends AbstractFopImage { | |||
/** | |||
* Create a bitmap image with the image data. | |||
* | |||
* @param imgInfo the image information | |||
*/ | |||
public EmfImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* Load the original EMF data. | |||
* This loads the original EMF data and reads the color space, | |||
* and icc profile if any. | |||
* | |||
* @return true if loaded false for any error | |||
*/ | |||
protected boolean loadOriginalData() { | |||
return loadDefaultOriginalData(); | |||
} | |||
} | |||
@@ -1,207 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import java.io.InputStream; | |||
import java.awt.color.ColorSpace; | |||
import java.awt.color.ICC_Profile; | |||
import java.awt.Color; | |||
import org.apache.fop.datatypes.Length; | |||
/** | |||
* Fop image interface for loading images. | |||
*/ | |||
public interface FopImage { | |||
/** | |||
* Flag for loading dimensions. | |||
*/ | |||
int DIMENSIONS = 1; | |||
/** | |||
* Flag for loading original data. | |||
*/ | |||
int ORIGINAL_DATA = 2; | |||
/** | |||
* Flag for loading bitmap data. | |||
*/ | |||
int BITMAP = 4; | |||
/** | |||
* Get the mime type of this image. | |||
* This is used so that when reading from the image it knows | |||
* what type of image it is. | |||
* | |||
* @return the mime type string | |||
*/ | |||
String getMimeType(); | |||
/** @return the original URI used to access this image. */ | |||
String getOriginalURI(); | |||
/** | |||
* Load particular inforamtion for this image | |||
* This must be called before attempting to get | |||
* the information. | |||
* | |||
* @param type the type of loading required | |||
* @return boolean true if the information could be loaded | |||
*/ | |||
boolean load(int type); | |||
/** | |||
* Returns the image width. | |||
* @return the width in pixels | |||
*/ | |||
int getWidth(); | |||
/** | |||
* Returns the image height. | |||
* @return the height in pixels | |||
*/ | |||
int getHeight(); | |||
/** | |||
* @return the intrinsic image width (in millipoints) | |||
*/ | |||
int getIntrinsicWidth(); | |||
/** | |||
* @return the intrinsic image width (in millipoints) | |||
*/ | |||
int getIntrinsicHeight(); | |||
/** | |||
* @return the intrinsic alignment-adjust value or NULL if the image does | |||
* not have one. | |||
*/ | |||
Length getIntrinsicAlignmentAdjust(); | |||
/** | |||
* @return the horizontal bitmap resolution (in dpi) | |||
*/ | |||
double getHorizontalResolution(); | |||
/** | |||
* @return the vertical bitmap resolution (in dpi) | |||
*/ | |||
double getVerticalResolution(); | |||
/** | |||
* Returns the color space of the image. | |||
* @return the color space | |||
*/ | |||
ColorSpace getColorSpace(); | |||
/** | |||
* Returns the ICC profile. | |||
* @return the ICC profile, null if none is available | |||
*/ | |||
ICC_Profile getICCProfile(); | |||
/** | |||
* Returns the number of bits per pixel for the image. | |||
* @return the number of bits per pixel | |||
*/ | |||
int getBitsPerPixel(); | |||
/** | |||
* Indicates whether the image is transparent. | |||
* @return True if it is transparent | |||
*/ | |||
boolean isTransparent(); | |||
/** | |||
* For transparent images. Returns the transparent color. | |||
* @return the transparent color | |||
*/ | |||
Color getTransparentColor(); | |||
/** | |||
* Indicates whether the image has a Soft Mask (See section 7.5.4 in the | |||
* PDF specs) | |||
* @return True if a Soft Mask exists | |||
*/ | |||
boolean hasSoftMask(); | |||
/** | |||
* For images with a Soft Mask. Returns the Soft Mask as an array. | |||
* @return the Soft Mask | |||
*/ | |||
byte[] getSoftMask(); | |||
/** @return true for CMYK images generated by Adobe Photoshop */ | |||
boolean isInverted(); | |||
/** | |||
* Returns the decoded and uncompressed image as a array of | |||
* width * height * [colorspace-multiplicator] pixels. | |||
* @return the bitmap | |||
*/ | |||
byte[] getBitmaps(); | |||
/** | |||
* Returns the size of the image. | |||
* width * (bitsPerPixel / 8) * height, no ? | |||
* @return the size | |||
*/ | |||
int getBitmapsSize(); | |||
/** | |||
* Returns the encoded/compressed image as an array of bytes. | |||
* @return the raw image | |||
*/ | |||
byte[] getRessourceBytes(); | |||
/** | |||
* Returns the number of bytes of the raw image. | |||
* @return the size in bytes | |||
*/ | |||
int getRessourceBytesSize(); | |||
/** | |||
* Image info class. | |||
* Information loaded from analyser and passed to image object. | |||
*/ | |||
public static class ImageInfo { | |||
/** InputStream to load the image from */ | |||
public InputStream inputStream; | |||
/** Original URI the image was accessed with */ | |||
public String originalURI; | |||
/** image width (in pixels) */ | |||
public int width; | |||
/** image height (in pixels) */ | |||
public int height; | |||
/** horizontal bitmap resolution (in dpi) */ | |||
public double dpiHorizontal = 72.0f; | |||
/** vertical bitmap resolution (in dpi) */ | |||
public double dpiVertical = 72.0f; | |||
/** implementation-specific data object (ex. a SVG DOM for SVG images) */ | |||
public Object data; | |||
/** MIME type of the image */ | |||
public String mimeType; | |||
/** implementation-specific String (ex. the namespace for XML-based images) */ | |||
public String str; | |||
/** intrinsic alignment-adjust or null if there is none */ | |||
public Length alignmentAdjust; | |||
} | |||
} | |||
@@ -1,211 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.util.Hashtable; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.ImageConsumer; | |||
import java.awt.image.ImageProducer; | |||
import java.awt.image.PixelGrabber; | |||
/** | |||
* ImageConsumer implementation for FopImage classes. | |||
*/ | |||
public class FopImageConsumer implements ImageConsumer { | |||
/** Image width in pixels */ | |||
protected int width = -1; | |||
/** Image height in pixels */ | |||
protected int height = -1; | |||
/** Image status */ | |||
protected Integer imageStatus = new Integer(-1); | |||
/** hints */ | |||
protected int hints = 0; | |||
/** Image properties */ | |||
protected Hashtable properties = null; | |||
/** Color model */ | |||
protected ColorModel cm = null; | |||
/** Image producer */ | |||
protected ImageProducer ip = null; | |||
/** | |||
* Main constructor | |||
* @param iprod ImageProducer to use | |||
*/ | |||
public FopImageConsumer(ImageProducer iprod) { | |||
this.ip = iprod; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void imageComplete(int status) { | |||
/* | |||
* log.error("Status "); | |||
* if (status == ImageConsumer.COMPLETESCANLINES) { | |||
* log.error("CompleteScanLines"); | |||
* } else if (status == ImageConsumer.IMAGEABORTED) { | |||
* log.error("ImageAborted"); | |||
* } else if (status == ImageConsumer.IMAGEERROR) { | |||
* log.error("ImageError"); | |||
* } else if (status == ImageConsumer.RANDOMPIXELORDER) { | |||
* log.error("RandomPixelOrder"); | |||
* } else if (status == ImageConsumer.SINGLEFRAME) { | |||
* log.error("SingleFrame"); | |||
* } else if (status == ImageConsumer.SINGLEFRAMEDONE) { | |||
* log.error("SingleFrameDone"); | |||
* } else if (status == ImageConsumer.SINGLEPASS) { | |||
* log.error("SinglePass"); | |||
* } else if (status == ImageConsumer.STATICIMAGEDONE) { | |||
* log.error("StaticImageDone"); | |||
* } else if (status == ImageConsumer.TOPDOWNLEFTRIGHT) { | |||
* log.error("TopDownLeftRight"); | |||
* } | |||
*/ | |||
synchronized (this.imageStatus) { | |||
// Need to stop status if image done | |||
if (imageStatus.intValue() != ImageConsumer.STATICIMAGEDONE | |||
&& imageStatus.intValue() != ImageConsumer.SINGLEFRAMEDONE) { | |||
this.imageStatus = new Integer(status); | |||
} | |||
} | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setColorModel(ColorModel model) { | |||
// log.error("setColorModel: " + model); | |||
this.cm = model; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setDimensions(int width, int height) { | |||
// log.error("setDimension: w=" + width + " h=" + height); | |||
this.width = width; | |||
this.height = height; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setHints(int hintflags) { | |||
// log.error("setHints: " + hintflags); | |||
this.hints = hintflags; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setProperties(Hashtable props) { | |||
// log.error("setProperties: " + props); | |||
this.properties = props; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setPixels(int x, int y, int w, int h, ColorModel model, | |||
byte[] pixels, int off, int scansize) { | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setPixels(int x, int y, int w, int h, ColorModel model, | |||
int[] pixels, int off, int scansize) { | |||
} | |||
/** | |||
* Indicates whether the image is ready. | |||
* @return boolean True if the image is ready, false if it's still loading | |||
* @throws Exception If an error happened while loading the image | |||
*/ | |||
public boolean isImageReady() throws Exception { | |||
/**@todo Use a better exception than Exception */ | |||
synchronized (this.imageStatus) { | |||
if (this.imageStatus.intValue() == ImageConsumer.IMAGEABORTED) { | |||
throw new Exception("Image aborted"); | |||
} | |||
if (this.imageStatus.intValue() == ImageConsumer.IMAGEERROR) { | |||
throw new Exception("Image error"); | |||
} | |||
if (imageStatus.intValue() == ImageConsumer.STATICIMAGEDONE | |||
|| imageStatus.intValue() == ImageConsumer.SINGLEFRAMEDONE) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
} | |||
/** | |||
* Returns the image width | |||
* @return the width in pixels | |||
*/ | |||
public int getWidth() { | |||
return this.width; | |||
} | |||
/** | |||
* Returns the image height | |||
* @return the height in pixels | |||
*/ | |||
public int getHeight() { | |||
return this.height; | |||
} | |||
/** | |||
* Returns the color model of the image | |||
* @return the color model | |||
*/ | |||
public ColorModel getColorModel() { | |||
return this.cm; | |||
} | |||
/** | |||
* Returns the bitmap as an array. | |||
* @return the bitmap as an array. | |||
* @throws Exception if an error occured while generating the array | |||
*/ | |||
public int[] getImage() throws Exception { | |||
int tmpMap[] = new int[this.width * this.height]; | |||
PixelGrabber pg = new PixelGrabber(this.ip, 0, 0, this.width, | |||
this.height, tmpMap, 0, this.width); | |||
pg.setDimensions(this.width, this.height); | |||
pg.setColorModel(this.cm); | |||
pg.setHints(this.hints); | |||
pg.setProperties(this.properties); | |||
try { | |||
pg.grabPixels(); | |||
} catch (InterruptedException intex) { | |||
/**@todo Use a better exception than Exception */ | |||
throw new Exception("Image grabbing interrupted : " | |||
+ intex.getMessage()); | |||
} | |||
return tmpMap; | |||
} | |||
} | |||
@@ -1,218 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.awt.image.ImageProducer; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.color.ColorSpace; | |||
import java.awt.Color; | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
import java.net.URLConnection; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* FopImage object for GIF images, using Java native classes. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class GifImage extends AbstractFopImage { | |||
/** | |||
* Create a new gif image. | |||
* | |||
* @param imgInfo the image info for this gif image | |||
*/ | |||
public GifImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* Load the bitmap for this gif image. | |||
* This loads the data and creates a bitmap byte array | |||
* of the image data. | |||
* To decode the image a dummy URLConnection is used that | |||
* will do the conversion. | |||
* | |||
* @return True if the load process succeeded | |||
*/ | |||
protected boolean loadBitmap() { | |||
int[] tmpMap = null; | |||
try { | |||
URLConnection con = new DummyConnection(inputStream); | |||
ImageProducer ip = (ImageProducer) con.getContent(); | |||
if (ip == null) { | |||
return false; | |||
} | |||
FopImageConsumer consumer = new FopImageConsumer(ip); | |||
ip.startProduction(consumer); | |||
//Load the image into memory | |||
while (!consumer.isImageReady()) { | |||
Thread.sleep(500); | |||
} | |||
this.height = consumer.getHeight(); | |||
this.width = consumer.getWidth(); | |||
try { | |||
tmpMap = consumer.getImage(); | |||
} catch (Exception ex) { | |||
log.error("Image grabbing interrupted : " | |||
+ ex.getMessage(), ex); | |||
return false; | |||
} | |||
ColorModel cm = consumer.getColorModel(); | |||
this.bitsPerPixel = 8; | |||
// this.bitsPerPixel = cm.getPixelSize(); | |||
this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); | |||
if (cm.hasAlpha()) { | |||
// java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT | |||
int transparencyType = cm.getTransparency(); | |||
if (transparencyType == java.awt.Transparency.OPAQUE) { | |||
this.isTransparent = false; | |||
} else if (transparencyType == java.awt.Transparency.BITMASK) { | |||
if (cm instanceof IndexColorModel) { | |||
IndexColorModel indexcm = (IndexColorModel) cm; | |||
this.isTransparent = false; | |||
byte[] alphas = new byte[indexcm.getMapSize()]; | |||
byte[] reds = new byte[indexcm.getMapSize()]; | |||
byte[] greens = new byte[indexcm.getMapSize()]; | |||
byte[] blues = new byte[indexcm.getMapSize()]; | |||
indexcm.getAlphas(alphas); | |||
indexcm.getReds(reds); | |||
indexcm.getGreens(greens); | |||
indexcm.getBlues(blues); | |||
for (int i = 0; | |||
i < indexcm.getMapSize(); | |||
i++) { | |||
if ((alphas[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
this.transparentColor = new Color( | |||
(int)(reds[i] & 0xFF), | |||
(int)(greens[i] & 0xFF), | |||
(int)(blues[i] & 0xFF)); | |||
break; | |||
} | |||
} | |||
} else { | |||
// TRANSLUCENT | |||
/* | |||
* this.isTransparent = false; | |||
* for (int i = 0; i < this.width * this.height; i++) { | |||
* if (cm.getAlpha(tmpMap[i]) == 0) { | |||
* this.isTransparent = true; | |||
* this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), | |||
* cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); | |||
* break; | |||
* } | |||
* } | |||
*/ | |||
// use special API... | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} catch (Exception ex) { | |||
log.error("Error while loading image (Gif): " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
for (int i = 0; i < this.height; i++) { | |||
for (int j = 0; j < this.width; j++) { | |||
int p = tmpMap[i * this.width + j]; | |||
int r = (p >> 16) & 0xFF; | |||
int g = (p >> 8) & 0xFF; | |||
int b = (p) & 0xFF; | |||
this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); | |||
} | |||
} | |||
return true; | |||
} | |||
/** {@inheritDoc} */ | |||
protected boolean loadOriginalData() { | |||
return loadDefaultOriginalData(); | |||
} | |||
/** | |||
* A dummy url connection for a gif image in an input stream. | |||
*/ | |||
protected static class DummyConnection extends URLConnection { | |||
private InputStream inputStream; | |||
DummyConnection(InputStream is) { | |||
super(null); | |||
inputStream = is; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public InputStream getInputStream() throws IOException { | |||
return inputStream; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void connect() throws IOException { | |||
// do nothing | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public String getContentType() { | |||
return "image/gif"; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getContentLength() { | |||
try { | |||
return inputStream.available(); | |||
} catch (IOException e) { | |||
return -1; | |||
} | |||
} | |||
} | |||
} | |||
@@ -1,72 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// FOP | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* Image cache holder. | |||
* This interface is used for caching images. | |||
*/ | |||
public interface ImageCache { | |||
/** | |||
* Get an image from the cache. | |||
* | |||
* @param url the url and key for the image | |||
* @param context the user agent context | |||
* @return the requested image | |||
*/ | |||
FopImage getImage(String url, FOUserAgent context); | |||
/** | |||
* Release an image in the current context. | |||
* | |||
* @param url the url and key for the image | |||
* @param context the user agent context | |||
*/ | |||
void releaseImage(String url, FOUserAgent context); | |||
/** | |||
* Invalidate image. | |||
* If during loading this image is found to be invalid | |||
* it will be invalidated to prevent further attempts at | |||
* loading the image. | |||
* | |||
* @param url the url and key for the image | |||
* @param context the user agent context | |||
*/ | |||
void invalidateImage(String url, FOUserAgent context); | |||
/** | |||
* Remove a context and handle all images in the context. | |||
* | |||
* @param context the user agent context | |||
*/ | |||
void removeContext(FOUserAgent context); | |||
/** | |||
* Forces the cache to fully cleared. | |||
*/ | |||
void clearAll(); | |||
} | |||
@@ -1,708 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.io.InputStream; | |||
import java.lang.ref.Reference; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.SoftReference; | |||
import java.lang.reflect.Constructor; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.Map.Entry; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.datatypes.URISpecification; | |||
import org.apache.fop.image.analyser.ImageReaderFactory; | |||
import org.apache.xmlgraphics.util.Service; | |||
/** | |||
* Create FopImage objects (with a configuration file - not yet implemented). | |||
* @author Eric SCHAEFFER | |||
*/ | |||
public final class ImageFactory { | |||
/** | |||
* logging instance | |||
*/ | |||
protected static Log log = LogFactory.getLog(FopImage.class); | |||
private HashMap imageMimeTypes = new HashMap(); | |||
private ImageCache cache = new ContextImageCache(true); | |||
/** | |||
* Main constructor for the ImageFactory. | |||
*/ | |||
public ImageFactory() { | |||
/* @todo The mappings set up below of image mime types to implementing | |||
* classes should be made externally configurable | |||
*/ | |||
ImageProvider jaiImage = new ImageProvider("JAIImage", "org.apache.fop.image.JAIImage"); | |||
ImageProvider jimiImage = new ImageProvider("JIMIImage", "org.apache.fop.image.JimiImage"); | |||
ImageProvider imageIoImage = new ImageProvider( | |||
"ImageIOImage", "org.apache.fop.image.ImageIOImage"); | |||
ImageProvider gifImage = new ImageProvider("GIFImage", "org.apache.fop.image.GifImage"); | |||
ImageProvider jpegImage = new ImageProvider("JPEGImage", "org.apache.fop.image.JpegImage"); | |||
ImageProvider jpegImageIOImage = new ImageProvider( | |||
"JPEGImage", "org.apache.fop.image.JpegImageIOImage"); | |||
ImageProvider bmpImage = new ImageProvider("BMPImage", "org.apache.fop.image.BmpImage"); | |||
ImageProvider epsImage = new ImageProvider("EPSImage", "org.apache.fop.image.EPSImage"); | |||
ImageProvider pngImage = new ImageProvider("PNGImage", "org.apache.fop.image.PNGImage"); | |||
ImageProvider tiffImage = new ImageProvider("TIFFImage", "org.apache.fop.image.TIFFImage"); | |||
ImageProvider xmlImage = new ImageProvider("XMLImage", "org.apache.fop.image.XMLImage"); | |||
ImageProvider emfImage = new ImageProvider("EMFImage", "org.apache.fop.image.EmfImage"); | |||
ImageMimeType imt = new ImageMimeType("image/gif"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(imageIoImage); | |||
imt.addProvider(jaiImage); | |||
imt.addProvider(jimiImage); | |||
imt.addProvider(gifImage); | |||
imt = new ImageMimeType("image/jpeg"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(jpegImageIOImage); | |||
imt.addProvider(jpegImage); | |||
imt = new ImageMimeType("image/bmp"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(bmpImage); | |||
imt = new ImageMimeType("image/eps"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(epsImage); | |||
imt = new ImageMimeType("image/png"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
//Image I/O is faster and more memory-efficient than own codec for PNG | |||
imt.addProvider(imageIoImage); | |||
imt.addProvider(pngImage); | |||
imt = new ImageMimeType("image/tga"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(jaiImage); | |||
imt.addProvider(imageIoImage); | |||
imt.addProvider(jimiImage); | |||
imt = new ImageMimeType("image/tiff"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(tiffImage); //Slower but supports CCITT embedding | |||
imt.addProvider(imageIoImage); //Fast but doesn't support CCITT embedding | |||
imt.addProvider(jaiImage); //Fast but doesn't support CCITT embedding | |||
imt = new ImageMimeType("image/svg+xml"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(xmlImage); | |||
imt = new ImageMimeType("text/xml"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(xmlImage); | |||
imt = new ImageMimeType("image/emf"); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(emfImage); | |||
Iterator iter = Service.providers(RegisterableImageProvider.class, true); | |||
while (iter.hasNext()) { | |||
RegisterableImageProvider impl = (RegisterableImageProvider)iter.next(); | |||
imt = new ImageMimeType(impl.getSupportedMimeType()); | |||
imageMimeTypes.put(imt.getMimeType(), imt); | |||
imt.addProvider(new ImageProvider(impl.getName(), impl.getClassName())); | |||
} | |||
} | |||
/** | |||
* Get the url string from a wrapped url. | |||
* | |||
* @param href the input wrapped url | |||
* @return the raw url | |||
*/ | |||
public static String getURL(String href) { | |||
return URISpecification.getURL(href); | |||
} | |||
/** | |||
* Get the image from the cache or load. | |||
* If this returns null then the image could not be loaded | |||
* due to an error. Messages should be logged. | |||
* Before calling this the getURL(url) must be used. | |||
* | |||
* @param url the url for the image | |||
* @param context the user agent context | |||
* @return the fop image instance | |||
*/ | |||
public FopImage getImage(String url, FOUserAgent context) { | |||
return cache.getImage(url, context); | |||
} | |||
/** | |||
* Release an image from the cache. | |||
* This can be used if the renderer has its own cache of | |||
* the image. | |||
* The image should then be put into the weak cache. | |||
* | |||
* @param url the url for the image | |||
* @param context the user agent context | |||
*/ | |||
public void releaseImage(String url, FOUserAgent context) { | |||
cache.releaseImage(url, context); | |||
} | |||
/** | |||
* Release the context and all images in the context. | |||
* | |||
* @param context the context to remove | |||
*/ | |||
public void removeContext(FOUserAgent context) { | |||
cache.removeContext(context); | |||
} | |||
/** | |||
* Create an FopImage objects. | |||
* @param href the url for the image | |||
* @param ua the user agent context | |||
* @return the fop image instance | |||
*/ | |||
public FopImage loadImage(String href, FOUserAgent ua) { | |||
Source source = ua.resolveURI(href); | |||
if (source == null) { | |||
return null; | |||
} | |||
// Got a valid source, obtain an InputStream from it | |||
InputStream in = null; | |||
if (source instanceof StreamSource) { | |||
in = ((StreamSource)source).getInputStream(); | |||
} | |||
if (in == null) { | |||
try { | |||
in = new java.net.URL(source.getSystemId()).openStream(); | |||
} catch (Exception ex) { | |||
log.error("Unable to obtain stream from id '" | |||
+ source.getSystemId() + "'"); | |||
} | |||
} | |||
if (in == null) { | |||
return null; | |||
} | |||
//Make sure the InputStream is decorated with a BufferedInputStream | |||
if (!(in instanceof java.io.BufferedInputStream)) { | |||
in = new java.io.BufferedInputStream(in); | |||
} | |||
// Check image type | |||
FopImage.ImageInfo imgInfo = null; | |||
try { | |||
imgInfo = ImageReaderFactory.make(source.getSystemId(), in, ua); | |||
} catch (Exception e) { | |||
log.error("Error while recovering image information (" | |||
+ href + ") : " + e.getMessage(), e); | |||
return null; | |||
} | |||
if (imgInfo == null) { | |||
try { | |||
in.close(); | |||
in = null; | |||
} catch (Exception e) { | |||
log.debug("Error closing the InputStream for the image", e); | |||
} | |||
log.error("No ImageReader for this type of image (" + href + ")"); | |||
return null; | |||
} | |||
// Associate mime-type to FopImage class | |||
String imgMimeType = imgInfo.mimeType; | |||
Class imageClass = getImageClass(imgMimeType); | |||
if (imageClass == null) { | |||
log.error("Unsupported image type (" + href + "): " + imgMimeType); | |||
return null; | |||
} else { | |||
if (log.isDebugEnabled()) { | |||
log.debug("Loading " + imgMimeType + " with " + imageClass.getName() | |||
+ ": " + href); | |||
} | |||
} | |||
// load the right image class | |||
// return new <FopImage implementing class> | |||
Object imageInstance = null; | |||
try { | |||
Class[] imageConstructorParameters = new Class[1]; | |||
imageConstructorParameters[0] = org.apache.fop.image.FopImage.ImageInfo.class; | |||
Constructor imageConstructor = imageClass.getDeclaredConstructor( | |||
imageConstructorParameters); | |||
Object[] initArgs = new Object[1]; | |||
initArgs[0] = imgInfo; | |||
imageInstance = imageConstructor.newInstance(initArgs); | |||
} catch (java.lang.reflect.InvocationTargetException ex) { | |||
Throwable t = ex.getTargetException(); | |||
String msg; | |||
if (t != null) { | |||
msg = t.getMessage(); | |||
} else { | |||
msg = ex.getMessage(); | |||
} | |||
log.error("Error creating FopImage object (" | |||
+ href + "): " + msg, (t == null) ? ex : t); | |||
return null; | |||
} catch (InstantiationException ie) { | |||
log.error("Error creating FopImage object (" | |||
+ href + "): Could not instantiate " + imageClass.getName() + " instance"); | |||
return null; | |||
} catch (Exception ex) { | |||
log.error("Error creating FopImage object (" | |||
+ href + "): " + ex.getMessage(), ex); | |||
return null; | |||
} | |||
if (!(imageInstance instanceof org.apache.fop.image.FopImage)) { | |||
log.error("Error creating FopImage object (" + href + "): " + "class " | |||
+ imageClass.getName() | |||
+ " doesn't implement org.apache.fop.image.FopImage interface"); | |||
return null; | |||
} | |||
return (FopImage) imageInstance; | |||
} | |||
private Class getImageClass(String imgMimeType) { | |||
ImageMimeType imt = (ImageMimeType)imageMimeTypes.get(imgMimeType); | |||
if (imt == null) { | |||
return null; | |||
} | |||
return imt.getFirstImplementingClass(); | |||
} | |||
/** | |||
* Forces all the image caches to be cleared. This should normally only be used in | |||
* testing environments. If you happen to think that you need to call this yourself | |||
* in a production environment, please notify the development team so we can look | |||
* into the issue. A call like this shouldn't be necessary anymore like it may have | |||
* been with FOP 0.20.5. | |||
*/ | |||
public void clearCaches() { | |||
cache.clearAll(); | |||
} | |||
} | |||
/** | |||
* Basic image cache. | |||
* This keeps track of invalid images. | |||
*/ | |||
class BasicImageCache implements ImageCache { | |||
private Set invalid = Collections.synchronizedSet(new java.util.HashSet()); | |||
//private Map contextStore = Collections.synchronizedMap(new java.util.HashMap()); | |||
public FopImage getImage(String url, FOUserAgent context) { | |||
if (invalid.contains(url)) { | |||
return null; | |||
} | |||
//TODO Doesn't seem to be fully implemented. Do we need it at all? Not referenced. | |||
return null; | |||
} | |||
public void releaseImage(String url, FOUserAgent context) { | |||
// do nothing | |||
} | |||
public void invalidateImage(String url, FOUserAgent context) { | |||
// cap size of invalid list | |||
if (invalid.size() > 100) { | |||
invalid.clear(); | |||
} | |||
invalid.add(url); | |||
} | |||
public void removeContext(FOUserAgent context) { | |||
// do nothing | |||
} | |||
/** {@inheritDoc} */ | |||
public void clearAll() { | |||
invalid.clear(); | |||
} | |||
} | |||
/** | |||
* This is the context image cache. | |||
* This caches images on the basis of the given context. | |||
* Common images in different contexts are currently not handled. | |||
* There are two possiblities, each context handles its own images | |||
* and renderers can cache information or images are shared and | |||
* all information is retained. | |||
* Once a context is removed then all images are placed into a | |||
* weak hashmap so they may be garbage collected. | |||
*/ | |||
class ContextImageCache implements ImageCache { | |||
// if this cache is collective then images can be shared | |||
// among contexts, this implies that the base directory | |||
// is either the same or does not effect the images being | |||
// loaded | |||
private boolean collective; | |||
private Map contextStore = Collections.synchronizedMap(new java.util.HashMap()); | |||
private Set invalid = null; | |||
private Map refStore = null; | |||
private ReferenceQueue refQueue = new ReferenceQueue(); | |||
public ContextImageCache(boolean col) { | |||
collective = col; | |||
if (collective) { | |||
refStore = Collections.synchronizedMap(new java.util.HashMap()); | |||
invalid = Collections.synchronizedSet(new java.util.HashSet()); | |||
} | |||
} | |||
// sync around lookups and puts | |||
// another sync around load for a particular image | |||
public FopImage getImage(String url, FOUserAgent context) { | |||
ImageLoader im = null; | |||
// this protects the finding or creating of a new | |||
// ImageLoader for multi threads | |||
synchronized (this) { | |||
if (collective && invalid.contains(url)) { | |||
return null; | |||
} | |||
Context con = (Context) contextStore.get(context); | |||
if (con == null) { | |||
con = new Context(context, collective); | |||
contextStore.put(context, con); | |||
} else { | |||
if (con.invalid(url)) { | |||
return null; | |||
} | |||
im = con.getImage(url); | |||
} | |||
if (im == null && collective) { | |||
Iterator i = contextStore.values().iterator(); | |||
while (i.hasNext()) { | |||
Context c = (Context)i.next(); | |||
if (c != con) { | |||
im = c.getImage(url); | |||
if (im != null) { | |||
break; | |||
} | |||
} | |||
} | |||
if (im == null) { | |||
Reference ref = (Reference)refStore.get(url); | |||
if (ref != null) { | |||
im = (ImageLoader) ref.get(); | |||
if (im == null) { | |||
//Remove key if its value has been garbage collected | |||
refStore.remove(url); | |||
} | |||
} | |||
} | |||
} | |||
if (im != null) { | |||
con.putImage(url, im); | |||
} else { | |||
im = con.getImage(url, this); | |||
} | |||
} | |||
// the ImageLoader is synchronized so images with the | |||
// same url will not be loaded at the same time | |||
if (im != null) { | |||
return im.loadImage(); | |||
} | |||
return null; | |||
} | |||
public void releaseImage(String url, FOUserAgent context) { | |||
Context con = (Context) contextStore.get(context); | |||
if (con != null) { | |||
if (collective) { | |||
ImageLoader im = con.getImage(url); | |||
refStore.put(url, wrapInReference(im, url)); | |||
} | |||
con.releaseImage(url); | |||
} | |||
} | |||
public void invalidateImage(String url, FOUserAgent context) { | |||
if (collective) { | |||
// cap size of invalid list | |||
if (invalid.size() > 100) { | |||
invalid.clear(); | |||
} | |||
invalid.add(url); | |||
} | |||
Context con = (Context) contextStore.get(context); | |||
if (con != null) { | |||
con.invalidateImage(url); | |||
} | |||
} | |||
private Reference wrapInReference(Object obj, Object key) { | |||
return new SoftReferenceWithKey(obj, key, refQueue); | |||
} | |||
private static class SoftReferenceWithKey extends SoftReference { | |||
private Object key; | |||
public SoftReferenceWithKey(Object referent, Object key, ReferenceQueue q) { | |||
super(referent, q); | |||
this.key = key; | |||
} | |||
} | |||
public void removeContext(FOUserAgent context) { | |||
Context con = (Context) contextStore.get(context); | |||
if (con != null) { | |||
if (collective) { | |||
Map images = con.getImages(); | |||
Iterator iter = images.entrySet().iterator(); | |||
while (iter.hasNext()) { | |||
Entry entry = (Entry)iter.next(); | |||
refStore.put(entry.getKey(), | |||
wrapInReference(entry.getValue(), entry.getKey())); | |||
} | |||
} | |||
contextStore.remove(context); | |||
} | |||
//House-keeping (remove cleared references) | |||
checkReferenceQueue(); | |||
} | |||
/** | |||
* Checks the reference queue if any references have been cleared and removes them from the | |||
* cache. | |||
*/ | |||
private void checkReferenceQueue() { | |||
SoftReferenceWithKey ref; | |||
while ((ref = (SoftReferenceWithKey)refQueue.poll()) != null) { | |||
refStore.remove(ref.key); | |||
} | |||
} | |||
class Context { | |||
private Map images = Collections.synchronizedMap(new java.util.HashMap()); | |||
private Set invalid = null; | |||
private FOUserAgent userAgent; | |||
public Context(FOUserAgent ua, boolean inv) { | |||
userAgent = ua; | |||
if (inv) { | |||
invalid = Collections.synchronizedSet(new java.util.HashSet()); | |||
} | |||
} | |||
public ImageLoader getImage(String url, ImageCache c) { | |||
if (images.containsKey(url)) { | |||
return (ImageLoader) images.get(url); | |||
} | |||
ImageLoader loader = new ImageLoader(url, c, userAgent); | |||
images.put(url, loader); | |||
return loader; | |||
} | |||
public void putImage(String url, ImageLoader image) { | |||
images.put(url, image); | |||
} | |||
public ImageLoader getImage(String url) { | |||
return (ImageLoader) images.get(url); | |||
} | |||
public void releaseImage(String url) { | |||
images.remove(url); | |||
} | |||
public Map getImages() { | |||
return images; | |||
} | |||
public void invalidateImage(String url) { | |||
invalid.add(url); | |||
} | |||
public boolean invalid(String url) { | |||
return invalid.contains(url); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public void clearAll() { | |||
this.refStore.clear(); | |||
this.invalid.clear(); | |||
//The context-sensitive caches are not cleared so there are no negative side-effects | |||
//in a multi-threaded environment. Not that it's a good idea to use this method at | |||
//all except in testing environments. If such a calls is necessary in normal environments | |||
//we need to check on memory leaks! | |||
} | |||
} | |||
/** | |||
* Encapsulates a class of type FopImage by holding its class name. | |||
* This allows dynamic loading of the class at runtime. | |||
*/ | |||
class ImageProvider { | |||
private String name = null; | |||
private String className = null; | |||
private boolean checked = false; | |||
private Class clazz = null; | |||
/** | |||
* Creates an ImageProvider with a given name and implementing class. | |||
* The class name should refer to a class of type {@link FopImage}. | |||
* However, this is not checked on construction. | |||
* @param name The name of the provider | |||
* @param className The full class name of the class implementing this provider | |||
*/ | |||
public ImageProvider(String name, String className) { | |||
setName(name); | |||
setClassName(className); | |||
} | |||
/** | |||
* Returns the provider name. | |||
* @return The provider name | |||
*/ | |||
public String getName() { | |||
return name; | |||
} | |||
private void setName(String name) { | |||
this.name = name; | |||
} | |||
/** | |||
* Returns the implementing class name. | |||
* @return The implementing class name | |||
*/ | |||
public String getClassName() { | |||
return className; | |||
} | |||
private void setClassName(String className) { | |||
this.className = className; | |||
} | |||
/** | |||
* Returns the implementing class as a {@link Class} object. | |||
* @return The implementing class or null if it couldn't be loaded. | |||
*/ | |||
public Class getImplementingClass() { | |||
if (!checked) { | |||
try { | |||
clazz = Class.forName(getClassName()); | |||
} catch (ClassNotFoundException cnfe) { | |||
//nop | |||
} catch (LinkageError le) { | |||
// This can happen if fop was build with support for a | |||
// particular provider (e.g. a binary fop distribution) | |||
// but the required support files (e.g. jai, jimi) are not | |||
// available in the current runtime environment. | |||
ImageFactory.log.debug("Image support provider " + getName() | |||
+ " could not be loaded. If " + getName() + " should be" | |||
+ " available please make sure all required external libraries" | |||
+ " are on the classpath."); | |||
} | |||
checked = true; | |||
} | |||
return clazz; | |||
} | |||
} | |||
/** | |||
* Holds a mime type for a particular image format plus a list of | |||
* {@link ImageProvider} objects which support the particular image format. | |||
*/ | |||
class ImageMimeType { | |||
private String mimeType = null; | |||
private List providers = null; | |||
/** | |||
* Constructor for a particular mime type. | |||
* @param mimeType The mime type | |||
*/ | |||
public ImageMimeType(String mimeType) { | |||
setMimeType(mimeType); | |||
} | |||
/** | |||
* Returns the mime type. | |||
* @return The mime type | |||
*/ | |||
public String getMimeType() { | |||
return mimeType; | |||
} | |||
private void setMimeType(String mimeType) { | |||
this.mimeType = mimeType; | |||
} | |||
/** | |||
* Returns the class from the first available provider. | |||
* @return The first available class or null if none can be found | |||
*/ | |||
public Class getFirstImplementingClass() { | |||
if (providers == null) { | |||
return null; | |||
} | |||
for (Iterator it = providers.iterator(); it.hasNext();) { | |||
ImageProvider ip = (ImageProvider)it.next(); | |||
Class clazz = ip.getImplementingClass(); | |||
if (clazz != null) { | |||
return clazz; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Adds a new provider. | |||
* The provider is added to the end of the current provider list. | |||
* @param The new provider to add | |||
*/ | |||
public void addProvider(ImageProvider provider) { | |||
if (providers == null) { | |||
providers = new ArrayList(4); // Assume we only have a few providers | |||
} | |||
if (!providers.contains(provider)) { | |||
providers.add(provider); | |||
} | |||
} | |||
} |
@@ -1,64 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* Class to load images. | |||
*/ | |||
class ImageLoader { | |||
private String url; | |||
private ImageCache cache; | |||
private boolean valid = true; | |||
private FOUserAgent userAgent; | |||
private FopImage image = null; | |||
/** | |||
* Main constructor. | |||
* @param url URL to the image | |||
* @param cache Image cache | |||
* @param ua User agent | |||
*/ | |||
public ImageLoader(String url, ImageCache cache, FOUserAgent ua) { | |||
this.url = url; | |||
this.cache = cache; | |||
this.userAgent = ua; | |||
} | |||
/** | |||
* Loads the image. | |||
* @return the loaded image | |||
*/ | |||
public synchronized FopImage loadImage() { | |||
if (!valid || image != null) { | |||
return image; | |||
} | |||
ImageFactory imageFactory = userAgent.getFactory().getImageFactory(); | |||
image = imageFactory.loadImage(url, userAgent); | |||
if (image == null) { | |||
cache.invalidateImage(url, userAgent); | |||
valid = false; | |||
} | |||
return image; | |||
} | |||
} |
@@ -1,194 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// AWT | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.Color; | |||
// JAI | |||
import javax.media.jai.JAI; | |||
import javax.media.jai.RenderedOp; | |||
import org.apache.commons.io.IOUtils; | |||
// Sun codec | |||
import com.sun.media.jai.codec.FileCacheSeekableStream; | |||
/** | |||
* FopImage object using JAI. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class JAIImage extends AbstractFopImage { | |||
/** | |||
* Create a new JAI image. | |||
* | |||
* @param imgInfo the image info for this JAI image | |||
*/ | |||
public JAIImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadDimensions() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.bitmaps != null; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadBitmap() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.bitmaps != null; | |||
} | |||
/** | |||
* Loads the image from the inputstream | |||
*/ | |||
protected void loadImage() { | |||
com.sun.media.jai.codec.FileCacheSeekableStream seekableInput = null; | |||
RenderedOp imageOp = null; | |||
try { | |||
seekableInput = new FileCacheSeekableStream(inputStream); | |||
imageOp = JAI.create("stream", seekableInput); | |||
this.height = imageOp.getHeight(); | |||
this.width = imageOp.getWidth(); | |||
ColorModel cm = imageOp.getColorModel(); | |||
//this.bitsPerPixel = 8; | |||
this.bitsPerPixel = cm.getPixelSize(); | |||
// TODO: the getRGB() function converts the image into the RGB | |||
// colorspace. However, here we assume the image colorspace is kept. | |||
// It should be either one of them, but not both. Unfortunately | |||
// there are other hacks for images in the CMYK colorspace (e.g. in | |||
// the PDF output) that would need to be changed as well. | |||
//this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); | |||
this.colorSpace = cm.getColorSpace(); | |||
BufferedImage imageData = imageOp.getAsBufferedImage(); | |||
int[] tmpMap = imageData.getRGB(0, 0, this.width, | |||
this.height, null, 0, this.width); | |||
if (cm.hasAlpha()) { | |||
// java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT | |||
int transparencyType = cm.getTransparency(); | |||
if (transparencyType == java.awt.Transparency.OPAQUE) { | |||
this.isTransparent = false; | |||
} else if (transparencyType == java.awt.Transparency.BITMASK) { | |||
if (cm instanceof IndexColorModel) { | |||
this.isTransparent = false; | |||
byte[] alphas = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] reds = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] greens = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] blues = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
((IndexColorModel) cm).getAlphas(alphas); | |||
((IndexColorModel) cm).getReds(reds); | |||
((IndexColorModel) cm).getGreens(greens); | |||
((IndexColorModel) cm).getBlues(blues); | |||
for (int i = 0; | |||
i < ((IndexColorModel) cm).getMapSize(); | |||
i++) { | |||
if ((alphas[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
this.transparentColor = new Color( | |||
(int)(reds[i] & 0xFF), | |||
(int)(greens[i] & 0xFF), | |||
(int)(blues[i] & 0xFF)); | |||
break; | |||
} | |||
} | |||
} else { | |||
// TRANSLUCENT | |||
/* | |||
* this.isTransparent = false; | |||
* for (int i = 0; i < this.width * this.height; i++) { | |||
* if (cm.getAlpha(tmpMap[i]) == 0) { | |||
* this.isTransparent = true; | |||
* this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), | |||
* cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); | |||
* break; | |||
* } | |||
* } | |||
* // or use special API... | |||
*/ | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
for (int i = 0; i < this.height; i++) { | |||
for (int j = 0; j < this.width; j++) { | |||
int p = tmpMap[i * this.width + j]; | |||
int r = (p >> 16) & 0xFF; | |||
int g = (p >> 8) & 0xFF; | |||
int b = (p) & 0xFF; | |||
this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); | |||
} | |||
} | |||
} catch (Exception ex) { | |||
log.error("Error while loading image (JAI): " + ex.getMessage(), ex); | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
if (imageOp != null) { | |||
imageOp.dispose(); | |||
} | |||
if (seekableInput != null) { | |||
IOUtils.closeQuietly(seekableInput); | |||
} | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
protected boolean loadOriginalData() { | |||
return loadDefaultOriginalData(); | |||
} | |||
} | |||
@@ -1,186 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import java.awt.image.ImageProducer; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.color.ColorSpace; | |||
import java.awt.Color; | |||
import org.apache.commons.io.IOUtils; | |||
// Jimi | |||
import com.sun.jimi.core.Jimi; | |||
/** | |||
* FopImage object for several images types, using Jimi. | |||
* See Jimi documentation for supported image types. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class JimiImage extends AbstractFopImage { | |||
/** | |||
* Create a new Jimi image. | |||
* | |||
* @param imgInfo the image info for this Jimi image | |||
*/ | |||
public JimiImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadDimensions() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.bitmaps != null; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadBitmap() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.bitmaps != null; | |||
} | |||
/** | |||
* Loads the image from the inputstream | |||
*/ | |||
protected void loadImage() { | |||
int[] tmpMap = null; | |||
try { | |||
ImageProducer ip = Jimi.getImageProducer(inputStream, | |||
Jimi.SYNCHRONOUS | Jimi.IN_MEMORY); | |||
FopImageConsumer consumer = new FopImageConsumer(ip); | |||
ip.startProduction(consumer); | |||
while (!consumer.isImageReady()) { | |||
Thread.sleep(500); | |||
} | |||
this.height = consumer.getHeight(); | |||
this.width = consumer.getWidth(); | |||
try { | |||
tmpMap = consumer.getImage(); | |||
} catch (Exception ex) { | |||
log.error("Image grabbing interrupted", ex); | |||
return; | |||
} | |||
ColorModel cm = consumer.getColorModel(); | |||
this.bitsPerPixel = 8; | |||
// this.bitsPerPixel = cm.getPixelSize(); | |||
this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); | |||
if (cm.hasAlpha()) { | |||
// java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT | |||
int transparencyType = cm.getTransparency(); | |||
if (transparencyType == java.awt.Transparency.OPAQUE) { | |||
this.isTransparent = false; | |||
} else if (transparencyType == java.awt.Transparency.BITMASK) { | |||
if (cm instanceof IndexColorModel) { | |||
this.isTransparent = false; | |||
byte[] alphas = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] reds = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] greens = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
byte[] blues = new byte[ | |||
((IndexColorModel) cm).getMapSize()]; | |||
((IndexColorModel) cm).getAlphas(alphas); | |||
((IndexColorModel) cm).getReds(reds); | |||
((IndexColorModel) cm).getGreens(greens); | |||
((IndexColorModel) cm).getBlues(blues); | |||
for (int i = 0; | |||
i < ((IndexColorModel) cm).getMapSize(); | |||
i++) { | |||
if ((alphas[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
this.transparentColor = new Color( | |||
(int)(reds[i] & 0xFF), | |||
(int)(greens[i] & 0xFF), | |||
(int)(blues[i] & 0xFF)); | |||
break; | |||
} | |||
} | |||
} else { | |||
// TRANSLUCENT | |||
/* | |||
* this.isTransparent = false; | |||
* for (int i = 0; i < this.width * this.height; i++) { | |||
* if (cm.getAlpha(tmpMap[i]) == 0) { | |||
* this.isTransparent = true; | |||
* this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), | |||
* cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); | |||
* break; | |||
* } | |||
* } | |||
*/ | |||
// use special API... | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} else { | |||
this.isTransparent = false; | |||
} | |||
} catch (Throwable ex) { | |||
log.error("Error while loading image (Jimi): " + ex.getMessage(), ex); | |||
return; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
// Should take care of the ColorSpace and bitsPerPixel | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
for (int i = 0; i < this.height; i++) { | |||
for (int j = 0; j < this.width; j++) { | |||
int p = tmpMap[i * this.width + j]; | |||
int r = (p >> 16) & 0xFF; | |||
int g = (p >> 8) & 0xFF; | |||
int b = (p) & 0xFF; | |||
this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); | |||
this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); | |||
} | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
protected boolean loadOriginalData() { | |||
return loadDefaultOriginalData(); | |||
} | |||
} | |||
@@ -1,239 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import java.awt.color.ColorSpace; | |||
import java.awt.color.ICC_Profile; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||
import org.apache.fop.util.CMYKColorSpace; | |||
/** | |||
* FopImage object for JPEG images, Using Java native classes. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class JpegImage extends AbstractFopImage { | |||
private ICC_Profile iccProfile = null; | |||
private boolean foundICCProfile = false; | |||
private boolean hasAPPEMarker = false; | |||
/** | |||
* Create a jpeg image with the info. | |||
* | |||
* @param imgInfo the image info for this jpeg | |||
*/ | |||
public JpegImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
} | |||
/** | |||
* Load the original jpeg data. | |||
* This loads the original jpeg data and reads the color space, | |||
* and icc profile if any. | |||
* | |||
* @return true if loaded false for any error | |||
*/ | |||
protected boolean loadOriginalData() { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
ByteArrayOutputStream iccStream = null; | |||
int index = 0; | |||
boolean cont = true; | |||
try { | |||
byte[] readBuf = new byte[4096]; | |||
int bytesRead; | |||
while ((bytesRead = inputStream.read(readBuf)) != -1) { | |||
baos.write(readBuf, 0, bytesRead); | |||
} | |||
} catch (java.io.IOException ex) { | |||
log.error("Error while loading image (Jpeg): " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
this.raw = baos.toByteArray(); | |||
this.bitsPerPixel = 8; | |||
this.isTransparent = false; | |||
//Check for SOI (Start of image) marker (FFD8) | |||
if (this.raw.length > (index + 2) | |||
&& uByte(this.raw[index]) == 255 /*0xFF*/ | |||
&& uByte(this.raw[index + 1]) == 216 /*0xD8*/) { | |||
index += 2; | |||
while (index < this.raw.length && cont) { | |||
//check to be sure this is the begining of a header | |||
if (this.raw.length > (index + 2) | |||
&& uByte(this.raw[index]) == 255 /*0xFF*/) { | |||
//192 or 194 are the header bytes that contain | |||
// the jpeg width height and color depth. | |||
if (uByte(this.raw[index + 1]) == 192 /*0xC0*/ | |||
|| uByte(this.raw[index + 1]) == 194 /*0xC2*/) { | |||
this.height = calcBytes(this.raw[index + 5], | |||
this.raw[index + 6]); | |||
this.width = calcBytes(this.raw[index + 7], | |||
this.raw[index + 8]); | |||
int numComponents = this.raw[index + 9]; | |||
if (numComponents == 1) { | |||
this.colorSpace = ColorSpace.getInstance( | |||
ColorSpace.CS_GRAY); | |||
} else if (numComponents == 3) { | |||
this.colorSpace = ColorSpace.getInstance( | |||
ColorSpace.CS_LINEAR_RGB); | |||
} else if (numComponents == 4) { | |||
// howto create CMYK color space | |||
/* | |||
this.colorSpace = ColorSpace.getInstance( | |||
ColorSpace.CS_CIEXYZ); | |||
*/ | |||
this.colorSpace = CMYKColorSpace.getInstance(); | |||
} else { | |||
log.error("Unknown ColorSpace for image: " | |||
+ ""); | |||
return false; | |||
} | |||
if (foundICCProfile) { | |||
cont = false; | |||
break; | |||
} | |||
index += calcBytes(this.raw[index + 2], | |||
this.raw[index + 3]) + 2; | |||
} else if (uByte(this.raw[index + 1]) == 226 /*0xE2*/ | |||
&& this.raw.length > (index + 60)) { | |||
// Check if ICC profile | |||
byte[] iccString = new byte[11]; | |||
System.arraycopy(this.raw, index + 4, | |||
iccString, 0, 11); | |||
if ("ICC_PROFILE".equals(new String(iccString))) { | |||
int chunkSize = calcBytes( | |||
this.raw[index + 2], | |||
this.raw[index + 3]) + 2; | |||
if (iccStream == null) { | |||
iccStream = new ByteArrayOutputStream(); | |||
} | |||
iccStream.write(this.raw, | |||
index + 18, chunkSize - 18); | |||
} | |||
index += calcBytes(this.raw[index + 2], | |||
this.raw[index + 3]) + 2; | |||
// Check for Adobe APPE Marker | |||
} else if ((uByte(this.raw[index]) == 0xff | |||
&& uByte(this.raw[index + 1]) == 0xee | |||
&& uByte(this.raw[index + 2]) == 0 | |||
&& uByte(this.raw[index + 3]) == 14 | |||
&& "Adobe".equals(new String(this.raw, index + 4, 5)))) { | |||
// The reason for reading the APPE marker is that Adobe Photoshop | |||
// generates CMYK JPEGs with inverted values. The correct thing | |||
// to do would be to interpret the values in the marker, but for now | |||
// only assume that if APPE marker is present and colorspace is CMYK, | |||
// the image is inverted. | |||
hasAPPEMarker = true; | |||
index += calcBytes(this.raw[index + 2], | |||
this.raw[index + 3]) + 2; | |||
} else { | |||
index += calcBytes(this.raw[index + 2], | |||
this.raw[index + 3]) + 2; | |||
} | |||
} else { | |||
cont = false; | |||
} | |||
} | |||
} else { | |||
log.error("Error while loading " | |||
+ "JpegImage - Invalid JPEG Header."); | |||
return false; | |||
} | |||
if (iccStream != null && iccStream.size() > 0) { | |||
int padding = (8 - (iccStream.size() % 8)) % 8; | |||
if (padding != 0) { | |||
try { | |||
iccStream.write(new byte[padding]); | |||
} catch (Exception ex) { | |||
log.error("Error while aligning ICC stream: " + ex.getMessage(), ex); | |||
return false; | |||
} | |||
} | |||
try { | |||
iccProfile = ICC_Profile.getInstance(iccStream.toByteArray()); | |||
} catch (IllegalArgumentException iae) { | |||
log.warn("An ICC profile is present but it is invalid (" | |||
+ iae.getMessage() + "). The color profile will be ignored. (" | |||
+ this.getOriginalURI() + ")"); | |||
} | |||
if (iccProfile.getNumComponents() != this.colorSpace.getNumComponents()) { | |||
log.warn("The number of components of the ICC profile (" | |||
+ iccProfile.getNumComponents() | |||
+ ") doesn't match the image (" | |||
+ this.colorSpace.getNumComponents() | |||
+ "). Ignoring the ICC color profile."); | |||
this.iccProfile = null; | |||
} | |||
} else if (this.colorSpace == null) { | |||
log.error("ColorSpace not specified for JPEG image"); | |||
return false; | |||
} | |||
if (hasAPPEMarker && this.colorSpace.getType() == ColorSpace.TYPE_CMYK) { | |||
if (log.isDebugEnabled()) { | |||
log.debug("JPEG has an Adobe APPE marker. Note: CMYK Image will be inverted. (" | |||
+ this.getOriginalURI() + ")"); | |||
} | |||
this.invertImage = true; | |||
} | |||
return true; | |||
} | |||
/** | |||
* Get the ICC profile for this Jpeg image. | |||
* | |||
* @return the icc profile or null if not found | |||
*/ | |||
public ICC_Profile getICCProfile() { | |||
return iccProfile; | |||
} | |||
private int calcBytes(byte bOne, byte bTwo) { | |||
return (uByte(bOne) * 256) + uByte(bTwo); | |||
} | |||
private int uByte(byte bIn) { | |||
if (bIn < 0) { | |||
return 256 + bIn; | |||
} else { | |||
return bIn; | |||
} | |||
} | |||
} | |||
@@ -1,87 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import java.io.IOException; | |||
import org.apache.xmlgraphics.image.codec.png.PNGRed; | |||
import org.apache.xmlgraphics.image.codec.png.PNGDecodeParam; | |||
import org.apache.xmlgraphics.image.codec.util.SeekableStream; | |||
import org.apache.xmlgraphics.image.rendered.CachableRed; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* FopImage object using PNG | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class PNGImage extends XmlGraphicsCommonsImage { | |||
/** | |||
* Constructs a new PNGImage instance. | |||
* @param imgReader basic metadata for the image | |||
*/ | |||
public PNGImage(FopImage.ImageInfo imgReader) { | |||
super(imgReader); | |||
this.loaded = 0; //TODO The PNGReader cannot read the resolution, yet. | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected CachableRed decodeImage(SeekableStream stream) throws IOException { | |||
PNGDecodeParam param = new PNGDecodeParam(); | |||
param.setPerformGammaCorrection(true); | |||
param.setDisplayExponent(2.2f); // sRGB gamma | |||
PNGRed red = new PNGRed(stream, param); | |||
String unit = (String)red.getProperty("pixel_units"); | |||
if ("Meters".equals(unit)) { | |||
this.dpiHorizontal = ((Integer)red.getProperty("x_pixels_per_unit")).intValue() | |||
* 25.4f / 1000f; | |||
this.dpiVertical = ((Integer)red.getProperty("y_pixels_per_unit")).intValue() | |||
* 25.4f / 1000f; | |||
} | |||
return red; | |||
} | |||
/** | |||
* Load the original PNG data. | |||
* This loads the original PNG data as is into memory. | |||
* | |||
* @return true if loaded false for any error | |||
*/ | |||
protected boolean loadOriginalData() { | |||
try { | |||
seekableInput.seek(0); | |||
this.raw = IOUtils.toByteArray(seekableInput); | |||
} catch (java.io.IOException ex) { | |||
log.error("Error while loading raw image: " + ex.getMessage(), ex); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(inputStream); | |||
inputStream = null; | |||
} | |||
return true; | |||
} | |||
} |
@@ -1,49 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
/** | |||
* This interface is used to dynamically register FopImage implementations. | |||
* <p> | |||
* NOTE: Please don't rely on this interface too much. It is a temporary measure | |||
* until the whole image package can be redesigned. The redesign will likely | |||
* provide a different mechanism to dynamically register new implementations. | |||
*/ | |||
public interface RegisterableImageProvider { | |||
/** | |||
* Returns the MIME type the implementation supports. | |||
* @return the MIME type | |||
*/ | |||
String getSupportedMimeType(); | |||
/** | |||
* Returns the name of the implementation. | |||
* @return the name | |||
*/ | |||
String getName(); | |||
/** | |||
* Returns the fully qualified class name for the implementing class. | |||
* @return the class name | |||
*/ | |||
String getClassName(); | |||
} |
@@ -1,207 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import java.awt.color.ColorSpace; | |||
import java.io.IOException; | |||
import org.apache.xmlgraphics.image.codec.util.SeekableStream; | |||
import org.apache.xmlgraphics.image.codec.tiff.TIFFDirectory; | |||
import org.apache.xmlgraphics.image.codec.tiff.TIFFField; | |||
import org.apache.xmlgraphics.image.codec.tiff.TIFFImageDecoder; | |||
import org.apache.xmlgraphics.image.rendered.CachableRed; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* TIFF implementation using the Batik codecs. | |||
*/ | |||
public class TIFFImage extends XmlGraphicsCommonsImage { | |||
private int compression = 0; | |||
private int stripCount = 0; | |||
private long stripOffset = 0; | |||
private long stripLength = 0; | |||
private int fillOrder = 1; | |||
/** | |||
* Constructs a new BatikImage instance. | |||
* @param imgReader basic metadata for the image | |||
*/ | |||
public TIFFImage(FopImage.ImageInfo imgReader) { | |||
super(imgReader); | |||
} | |||
/** | |||
* The compression type set in the TIFF directory | |||
* @return the TIFF compression type | |||
*/ | |||
public int getCompression() { | |||
return compression; | |||
} | |||
/** | |||
* The number of strips in the image | |||
* @return the number of strips in the image | |||
*/ | |||
public int getStripCount() { | |||
return stripCount; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* org.apache.xmlgraphics.image.codec.util.SeekableStream) | |||
*/ | |||
protected CachableRed decodeImage(SeekableStream stream) throws IOException { | |||
org.apache.xmlgraphics.image.codec.tiff.TIFFImage img | |||
= new org.apache.xmlgraphics.image.codec.tiff.TIFFImage | |||
(stream, null, 0); | |||
TIFFDirectory dir = (TIFFDirectory)img.getProperty("tiff_directory"); | |||
TIFFField fld = dir.getField(TIFFImageDecoder.TIFF_RESOLUTION_UNIT); | |||
int resUnit = fld.getAsInt(0); | |||
fld = dir.getField(TIFFImageDecoder.TIFF_X_RESOLUTION); | |||
double xRes = fld.getAsDouble(0); | |||
fld = dir.getField(TIFFImageDecoder.TIFF_Y_RESOLUTION); | |||
double yRes = fld.getAsDouble(0); | |||
switch (resUnit) { | |||
case 2: //inch | |||
this.dpiHorizontal = xRes; | |||
this.dpiVertical = yRes; | |||
break; | |||
case 3: //cm | |||
this.dpiHorizontal = xRes * 2.54f; | |||
this.dpiVertical = yRes * 2.54f; | |||
break; | |||
default: | |||
//ignored | |||
log.warn("Cannot determine bitmap resolution." | |||
+ " Unimplemented resolution unit: " + resUnit); | |||
} | |||
fld = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION); | |||
if (fld != null) { | |||
compression = fld.getAsInt(0); | |||
} | |||
fld = dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE); | |||
if (fld != null) { | |||
bitsPerPixel = fld.getAsInt(0); | |||
} | |||
fld = dir.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP); | |||
if (fld == null) { | |||
stripCount = 1; | |||
} else { | |||
stripCount = (int)(dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH) | |||
/ fld.getAsLong(0)); | |||
} | |||
fld = dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER); | |||
if (fld != null) { | |||
fillOrder = fld.getAsInt(0); | |||
} | |||
stripOffset = dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS).getAsLong(0); | |||
stripLength = dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS).getAsLong(0); | |||
if (this.bitsPerPixel == 1) { | |||
this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); | |||
} | |||
return img; | |||
} | |||
/** | |||
* Load the original TIFF data. | |||
* This loads only strip 1 of the original TIFF data. | |||
* | |||
* @return true if loaded false for any error | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadOriginalData() { | |||
if (loadDimensions()) { | |||
byte[] readBuf = new byte[(int)stripLength]; | |||
int bytesRead; | |||
try { | |||
this.seekableInput.reset(); | |||
this.seekableInput.skip(stripOffset); | |||
bytesRead = seekableInput.read(readBuf); | |||
if (bytesRead != stripLength) { | |||
log.error("Error while loading image: length mismatch on read"); | |||
return false; | |||
} | |||
// need to invert bytes if fill order = 2 | |||
if (fillOrder == 2) { | |||
for (int i = 0; i < (int)stripLength; i++) { | |||
readBuf[i] = flipTable[readBuf[i] & 0xff]; | |||
} | |||
} | |||
this.raw = readBuf; | |||
return true; | |||
} catch (IOException ioe) { | |||
log.error("Error while loading image strip 1 (TIFF): ", ioe); | |||
return false; | |||
} finally { | |||
IOUtils.closeQuietly(seekableInput); | |||
IOUtils.closeQuietly(inputStream); | |||
this.seekableInput = null; | |||
this.inputStream = null; | |||
this.cr = null; | |||
} | |||
} | |||
return false; | |||
} | |||
// Table to be used when fillOrder = 2, for flipping bytes. | |||
// Copied from XML Graphics Commons' TIFFFaxDecoder class | |||
private static byte[] flipTable = { | |||
0, -128, 64, -64, 32, -96, 96, -32, | |||
16, -112, 80, -48, 48, -80, 112, -16, | |||
8, -120, 72, -56, 40, -88, 104, -24, | |||
24, -104, 88, -40, 56, -72, 120, -8, | |||
4, -124, 68, -60, 36, -92, 100, -28, | |||
20, -108, 84, -44, 52, -76, 116, -12, | |||
12, -116, 76, -52, 44, -84, 108, -20, | |||
28, -100, 92, -36, 60, -68, 124, -4, | |||
2, -126, 66, -62, 34, -94, 98, -30, | |||
18, -110, 82, -46, 50, -78, 114, -14, | |||
10, -118, 74, -54, 42, -86, 106, -22, | |||
26, -102, 90, -38, 58, -70, 122, -6, | |||
6, -122, 70, -58, 38, -90, 102, -26, | |||
22, -106, 86, -42, 54, -74, 118, -10, | |||
14, -114, 78, -50, 46, -82, 110, -18, | |||
30, -98, 94, -34, 62, -66, 126, -2, | |||
1, -127, 65, -63, 33, -95, 97, -31, | |||
17, -111, 81, -47, 49, -79, 113, -15, | |||
9, -119, 73, -55, 41, -87, 105, -23, | |||
25, -103, 89, -39, 57, -71, 121, -7, | |||
5, -123, 69, -59, 37, -91, 101, -27, | |||
21, -107, 85, -43, 53, -75, 117, -11, | |||
13, -115, 77, -51, 45, -83, 109, -19, | |||
29, -99, 93, -35, 61, -67, 125, -3, | |||
3, -125, 67, -61, 35, -93, 99, -29, | |||
19, -109, 83, -45, 51, -77, 115, -13, | |||
11, -117, 75, -53, 43, -85, 107, -21, | |||
27, -101, 91, -37, 59, -69, 123, -5, | |||
7, -121, 71, -57, 39, -89, 103, -25, | |||
23, -105, 87, -41, 55, -73, 119, -9, | |||
15, -113, 79, -49, 47, -81, 111, -17, | |||
31, -97, 95, -33, 63, -65, 127, -1, | |||
}; | |||
// end | |||
} |
@@ -1,78 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
// Java | |||
import org.w3c.dom.Document; | |||
import javax.xml.parsers.SAXParserFactory; | |||
/** | |||
* This is an implementation for XML-based images such as SVG. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public class XMLImage extends AbstractFopImage { | |||
private Document doc; | |||
private String namespace = ""; | |||
/** | |||
* @see org.apache.fop.image.AbstractFopImage#AbstractFopImage(FopImage.ImageInfo) | |||
*/ | |||
public XMLImage(FopImage.ImageInfo imgInfo) { | |||
super(imgInfo); | |||
if (imgInfo.data instanceof Document) { | |||
doc = (Document)imgInfo.data; | |||
loaded = loaded | ORIGINAL_DATA; | |||
} | |||
namespace = imgInfo.str; | |||
} | |||
/** | |||
* Returns the fully qualified classname of an XML parser for | |||
* Batik classes that apparently need it (error messages, perhaps) | |||
* @return an XML parser classname | |||
*/ | |||
public static String getParserName() { | |||
try { | |||
SAXParserFactory factory = SAXParserFactory.newInstance(); | |||
return factory.newSAXParser().getXMLReader().getClass().getName(); | |||
} catch (Exception e) { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the XML document as a DOM document. | |||
* @return the DOM document | |||
*/ | |||
public Document getDocument() { | |||
return this.doc; | |||
} | |||
/** | |||
* Returns the namespace of the XML document. | |||
* @return the namespace | |||
*/ | |||
public String getNameSpace() { | |||
return this.namespace; | |||
} | |||
} |
@@ -1,239 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image; | |||
import java.awt.Color; | |||
import java.awt.Transparency; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.IndexColorModel; | |||
import java.awt.image.RenderedImage; | |||
import java.awt.image.WritableRaster; | |||
import java.awt.image.BufferedImage; | |||
import java.io.IOException; | |||
import org.apache.xmlgraphics.image.GraphicsUtil; | |||
import org.apache.xmlgraphics.image.codec.util.SeekableStream; | |||
import org.apache.xmlgraphics.image.codec.util.MemoryCacheSeekableStream; | |||
import org.apache.xmlgraphics.image.codec.util.FileCacheSeekableStream; | |||
import org.apache.xmlgraphics.image.rendered.CachableRed; | |||
import org.apache.commons.io.IOUtils; | |||
/** | |||
* Abstract FopImage implementation which uses the internal codecs from XML Graphics Commons. | |||
* | |||
* @see AbstractFopImage | |||
* @see FopImage | |||
*/ | |||
public abstract class XmlGraphicsCommonsImage extends AbstractFopImage { | |||
private byte[] softMask = null; | |||
/** | |||
* The InputStream wrapped into a SeekableStream for decoding. | |||
*/ | |||
protected SeekableStream seekableInput = null; | |||
/** | |||
* The Batik representation of the image | |||
*/ | |||
protected CachableRed cr = null; | |||
/** | |||
* Constructs a new BatikImage instance. | |||
* @param imgReader basic metadata for the image | |||
*/ | |||
public XmlGraphicsCommonsImage(FopImage.ImageInfo imgReader) { | |||
super(imgReader); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadDimensions() { | |||
if (seekableInput == null && inputStream != null) { | |||
try { | |||
seekableInput = new FileCacheSeekableStream(inputStream); | |||
} catch (IOException ioe) { | |||
seekableInput = new MemoryCacheSeekableStream(inputStream); | |||
} | |||
try { | |||
this.bitsPerPixel = 8; | |||
cr = decodeImage(seekableInput); | |||
this.height = cr.getHeight(); | |||
this.width = cr.getWidth(); | |||
this.isTransparent = false; | |||
this.softMask = null; | |||
ColorModel cm = cr.getColorModel(); | |||
this.height = cr.getHeight(); | |||
this.width = cr.getWidth(); | |||
this.isTransparent = false; | |||
this.softMask = null; | |||
int transparencyType = cm.getTransparency(); | |||
if (cm instanceof IndexColorModel) { | |||
if (transparencyType == Transparency.BITMASK) { | |||
// Use 'transparent color'. | |||
IndexColorModel icm = (IndexColorModel)cm; | |||
int numColor = icm.getMapSize(); | |||
byte [] alpha = new byte[numColor]; | |||
icm.getAlphas(alpha); | |||
for (int i = 0; i < numColor; i++) { | |||
if ((alpha[i] & 0xFF) == 0) { | |||
this.isTransparent = true; | |||
int red = (icm.getRed (i)) & 0xFF; | |||
int grn = (icm.getGreen(i)) & 0xFF; | |||
int blu = (icm.getBlue (i)) & 0xFF; | |||
this.transparentColor = new Color(red, grn, blu); | |||
break; | |||
} | |||
} | |||
} | |||
} else { | |||
cr = GraphicsUtil.convertTosRGB(cr); | |||
} | |||
// Get our current ColorModel | |||
cm = cr.getColorModel(); | |||
if (this.colorSpace == null) { | |||
this.colorSpace = cm.getColorSpace(); | |||
} | |||
} catch (IOException ioe) { | |||
log.error("Error while loading image (Batik): " + ioe.getMessage(), ioe); | |||
IOUtils.closeQuietly(seekableInput); | |||
IOUtils.closeQuietly(inputStream); | |||
seekableInput = null; | |||
inputStream = null; | |||
return false; | |||
} | |||
} | |||
return this.height != -1; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected boolean loadBitmap() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.bitmaps != null; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public boolean hasSoftMask() { | |||
if (this.bitmaps == null && this.raw == null) { | |||
loadImage(); | |||
} | |||
return (this.softMask != null); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public byte[] getSoftMask() { | |||
if (this.bitmaps == null) { | |||
loadImage(); | |||
} | |||
return this.softMask; | |||
} | |||
/** | |||
* Decodes the image from the stream. | |||
* @param stream the stream to read the image from | |||
* @return the decoded image | |||
* @throws IOException in case an I/O problem occurs | |||
*/ | |||
protected abstract CachableRed decodeImage(SeekableStream stream) throws IOException; | |||
/** | |||
* Loads the image from the InputStream. | |||
*/ | |||
protected void loadImage() { | |||
if (loadDimensions()) { | |||
try { | |||
if (cr == null) { | |||
throw new IllegalStateException( | |||
"Can't load the bitmaps data without the CachableRed instance"); | |||
} | |||
// Get our current ColorModel | |||
ColorModel cm = cr.getColorModel(); | |||
// It has an alpha channel so generate a soft mask. | |||
if (!this.isTransparent && cm.hasAlpha()) { | |||
this.softMask = new byte[this.width * this.height]; | |||
} | |||
this.bitmaps = new byte[this.width * this.height * 3]; | |||
constructBitmaps(cr, this.bitmaps, this.softMask); | |||
} catch (Exception ex) { | |||
log.error("Error while loading image (Batik): " + ex.getMessage(), ex); | |||
} finally { | |||
// Make sure we clean up | |||
IOUtils.closeQuietly(seekableInput); | |||
IOUtils.closeQuietly(inputStream); | |||
seekableInput = null; | |||
inputStream = null; | |||
cr = null; | |||
} | |||
} | |||
} | |||
private static void constructBitmaps(RenderedImage red, byte[] bitmaps, byte[] softMask) { | |||
WritableRaster wr = (WritableRaster)red.getData(); | |||
ColorModel cm = red.getColorModel(); | |||
BufferedImage bi = new BufferedImage | |||
(cm, wr.createWritableTranslatedChild(0, 0), | |||
cm.isAlphaPremultiplied(), null); | |||
int width = red.getWidth(); | |||
int height = red.getHeight(); | |||
int [] tmpMap = new int[width]; | |||
int idx = 0; | |||
int sfIdx = 0; | |||
for (int y = 0; y < height; y++) { | |||
tmpMap = bi.getRGB(0, y, width, 1, tmpMap, 0, width); | |||
if (softMask != null) { | |||
for (int x = 0; x < width; x++) { | |||
int pix = tmpMap[x]; | |||
softMask[sfIdx++] = (byte)(pix >>> 24); | |||
bitmaps[idx++] = (byte)((pix >>> 16) & 0xFF); | |||
bitmaps[idx++] = (byte)((pix >>> 8) & 0xFF); | |||
bitmaps[idx++] = (byte)((pix) & 0xFF); | |||
} | |||
} else { | |||
for (int x = 0; x < width; x++) { | |||
int pix = tmpMap[x]; | |||
bitmaps[idx++] = (byte)((pix >> 16) & 0xFF); | |||
bitmaps[idx++] = (byte)((pix >> 8) & 0xFF); | |||
bitmaps[idx++] = (byte)((pix) & 0xFF); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,134 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for BMP image type. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public class BMPReader implements ImageReader { | |||
/** Length of the BMP header */ | |||
protected static final int BMP_SIG_LENGTH = 46; | |||
/** offset to width */ | |||
private static final int WIDTH_OFFSET = 18; | |||
/** offset to height */ | |||
private static final int HEIGHT_OFFSET = 22; | |||
/** offset to horizontal res */ | |||
private static final int HRES_OFFSET = 38; | |||
/** offset to vertical res */ | |||
private static final int VRES_OFFSET = 42; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(bis); | |||
boolean supported = ((header[0] == (byte) 0x42) | |||
&& (header[1] == (byte) 0x4d)); | |||
if (supported) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.dpiHorizontal = ua.getFactory().getSourceResolution(); | |||
info.dpiVertical = info.dpiHorizontal; | |||
getDimension(header, info); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = bis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/bmp"; | |||
} | |||
private void getDimension(byte[] header, FopImage.ImageInfo info) { | |||
// little endian notation | |||
int byte1 = header[WIDTH_OFFSET] & 0xff; | |||
int byte2 = header[WIDTH_OFFSET + 1] & 0xff; | |||
int byte3 = header[WIDTH_OFFSET + 2] & 0xff; | |||
int byte4 = header[WIDTH_OFFSET + 3] & 0xff; | |||
long l = (long) ((byte4 << 24) | (byte3 << 16) | |||
| (byte2 << 8) | byte1); | |||
info.width = (int) (l & 0xffffffff); | |||
byte1 = header[HEIGHT_OFFSET] & 0xff; | |||
byte2 = header[HEIGHT_OFFSET + 1] & 0xff; | |||
byte3 = header[HEIGHT_OFFSET + 2] & 0xff; | |||
byte4 = header[HEIGHT_OFFSET + 3] & 0xff; | |||
l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
info.height = (int) (l & 0xffffffff); | |||
byte1 = header[HRES_OFFSET] & 0xff; | |||
byte2 = header[HRES_OFFSET + 1] & 0xff; | |||
byte3 = header[HRES_OFFSET + 2] & 0xff; | |||
byte4 = header[HRES_OFFSET + 3] & 0xff; | |||
l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
if (l > 0) { | |||
info.dpiHorizontal = l / 39.37d; | |||
} | |||
byte1 = header[VRES_OFFSET] & 0xff; | |||
byte2 = header[VRES_OFFSET + 1] & 0xff; | |||
byte3 = header[VRES_OFFSET + 2] & 0xff; | |||
byte4 = header[VRES_OFFSET + 3] & 0xff; | |||
l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
if (l > 0) { | |||
info.dpiVertical = l / 39.37d; | |||
} | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) | |||
throws IOException { | |||
byte[] header = new byte[BMP_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(BMP_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
} |
@@ -1,162 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for EMF image type. | |||
* | |||
* @author Peter Herweg | |||
*/ | |||
public class EMFReader implements ImageReader { | |||
/** Length of the EMF header */ | |||
protected static final int EMF_SIG_LENGTH = 88; | |||
/** offset to signature */ | |||
private static final int SIGNATURE_OFFSET = 40; | |||
/** offset to width */ | |||
private static final int WIDTH_OFFSET = 32; | |||
/** offset to height */ | |||
private static final int HEIGHT_OFFSET = 36; | |||
/** offset to horizontal resolution in pixel */ | |||
private static final int HRES_PIXEL_OFFSET = 72; | |||
/** offset to vertical resolution in pixel */ | |||
private static final int VRES_PIXEL_OFFSET = 76; | |||
/** offset to horizontal resolution in mm */ | |||
private static final int HRES_MM_OFFSET = 80; | |||
/** offset to vertical resolution in mm */ | |||
private static final int VRES_MM_OFFSET = 84; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(bis); | |||
boolean supported | |||
= ( (header[SIGNATURE_OFFSET + 0] == (byte) 0x20) | |||
&& (header[SIGNATURE_OFFSET + 1] == (byte) 0x45) | |||
&& (header[SIGNATURE_OFFSET + 2] == (byte) 0x4D) | |||
&& (header[SIGNATURE_OFFSET + 3] == (byte) 0x46) ); | |||
if (supported) { | |||
FopImage.ImageInfo info = getDimension(header); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = bis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/emf"; | |||
} | |||
private FopImage.ImageInfo getDimension(byte[] header) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
long value = 0; | |||
int byte1; | |||
int byte2; | |||
int byte3; | |||
int byte4; | |||
// little endian notation | |||
//resolution | |||
byte1 = header[HRES_MM_OFFSET] & 0xff; | |||
byte2 = header[HRES_MM_OFFSET + 1] & 0xff; | |||
byte3 = header[HRES_MM_OFFSET + 2] & 0xff; | |||
byte4 = header[HRES_MM_OFFSET + 3] & 0xff; | |||
long hresMM = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
byte1 = header[VRES_MM_OFFSET] & 0xff; | |||
byte2 = header[VRES_MM_OFFSET + 1] & 0xff; | |||
byte3 = header[VRES_MM_OFFSET + 2] & 0xff; | |||
byte4 = header[VRES_MM_OFFSET + 3] & 0xff; | |||
long vresMM = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
byte1 = header[HRES_PIXEL_OFFSET] & 0xff; | |||
byte2 = header[HRES_PIXEL_OFFSET + 1] & 0xff; | |||
byte3 = header[HRES_PIXEL_OFFSET + 2] & 0xff; | |||
byte4 = header[HRES_PIXEL_OFFSET + 3] & 0xff; | |||
long hresPixel = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
byte1 = header[VRES_PIXEL_OFFSET] & 0xff; | |||
byte2 = header[VRES_PIXEL_OFFSET + 1] & 0xff; | |||
byte3 = header[VRES_PIXEL_OFFSET + 2] & 0xff; | |||
byte4 = header[VRES_PIXEL_OFFSET + 3] & 0xff; | |||
long vresPixel = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
info.dpiHorizontal = hresPixel / (hresMM / 25.4f); | |||
info.dpiVertical = vresPixel / (vresMM / 25.4f); | |||
//width | |||
byte1 = header[WIDTH_OFFSET] & 0xff; | |||
byte2 = header[WIDTH_OFFSET + 1] & 0xff; | |||
byte3 = header[WIDTH_OFFSET + 2] & 0xff; | |||
byte4 = header[WIDTH_OFFSET + 3] & 0xff; | |||
value = (long) ((byte4 << 24) | (byte3 << 16) | |||
| (byte2 << 8) | byte1); | |||
value = Math.round(value / 100f / 25.4f * info.dpiHorizontal); | |||
info.width = (int) (value & 0xffffffff); | |||
//height | |||
byte1 = header[HEIGHT_OFFSET] & 0xff; | |||
byte2 = header[HEIGHT_OFFSET + 1] & 0xff; | |||
byte3 = header[HEIGHT_OFFSET + 2] & 0xff; | |||
byte4 = header[HEIGHT_OFFSET + 3] & 0xff; | |||
value = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); | |||
value = Math.round(value / 100f / 25.4f * info.dpiVertical); | |||
info.height = (int) (value & 0xffffffff); | |||
return info; | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) | |||
throws IOException { | |||
byte[] header = new byte[EMF_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(EMF_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
} |
@@ -1,253 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.image.EPSImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for EPS document image type. | |||
* | |||
* @version $Id$ | |||
*/ | |||
public class EPSReader implements ImageReader { | |||
private static final byte[] EPS_HEADER_ASCII = "%!PS".getBytes(); | |||
private static final byte[] BOUNDINGBOX = "%%BoundingBox: ".getBytes(); | |||
//private static final byte[] HIRESBOUNDINGBOX = "%%HiResBoundingBox: ".getBytes(); | |||
//TODO Implement HiResBoundingBox, ImageInfo probably needs some changes for that | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
boolean isEPS = false; | |||
bis.mark(32); | |||
byte[] header = new byte[30]; | |||
bis.read(header, 0, 30); | |||
bis.reset(); | |||
EPSImage.EPSData data = new EPSImage.EPSData(); | |||
// Check if binary header | |||
if (getLong(header, 0) == 0xC6D3D0C5) { | |||
data.isAscii = false; | |||
isEPS = true; | |||
data.psStart = getLong(header, 4); | |||
data.psLength = getLong(header, 8); | |||
data.wmfStart = getLong(header, 12); | |||
data.wmfLength = getLong(header, 16); | |||
data.tiffStart = getLong(header, 20); | |||
data.tiffLength = getLong(header, 24); | |||
} else { | |||
// Check if plain ascii | |||
byte[] epsh = "%!PS".getBytes(); | |||
if (EPS_HEADER_ASCII[0] == header[0] | |||
&& EPS_HEADER_ASCII[1] == header[1] | |||
&& EPS_HEADER_ASCII[2] == header[2] | |||
&& EPS_HEADER_ASCII[3] == header[3]) { | |||
data.isAscii = true; | |||
isEPS = true; | |||
} | |||
} | |||
if (isEPS) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.data = data; | |||
readEPSImage(bis, data); | |||
data.bbox = readBBox(data); | |||
if (data.bbox != null) { | |||
info.width = (int) (data.bbox[2] - data.bbox[0]); | |||
info.height = (int) (data.bbox[3] - data.bbox[1]); | |||
// image data read | |||
IOUtils.closeQuietly(bis); | |||
info.inputStream = null; | |||
return info; | |||
} else { | |||
// Ain't eps if no BoundingBox | |||
isEPS = false; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/eps"; | |||
} | |||
private long getLong(byte[] buf, int idx) { | |||
int b1 = buf[idx] & 0xff; | |||
int b2 = buf[idx + 1] & 0xff; | |||
int b3 = buf[idx + 2] & 0xff; | |||
int b4 = buf[idx + 3] & 0xff; | |||
return (long) ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); | |||
} | |||
/** | |||
* Read the eps file and extract eps part. | |||
* | |||
* @param bis The InputStream | |||
* @param data EPSData object to write the results to | |||
* @exception IOException If an I/O error occurs | |||
*/ | |||
private void readEPSImage(InputStream bis, EPSImage.EPSData data) | |||
throws IOException { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
byte[] file; | |||
byte[] readBuf = new byte[20480]; | |||
int bytesRead; | |||
int index = 0; | |||
boolean cont = true; | |||
try { | |||
while ((bytesRead = bis.read(readBuf)) != -1) { | |||
baos.write(readBuf, 0, bytesRead); | |||
} | |||
} catch (java.io.IOException ex) { | |||
throw new IOException("Error while loading EPS image: " | |||
+ ex.getMessage()); | |||
} | |||
file = baos.toByteArray(); | |||
if (data.isAscii) { | |||
data.rawEps = null; | |||
data.epsFile = new byte[file.length]; | |||
System.arraycopy(file, 0, data.epsFile, 0, data.epsFile.length); | |||
} else { | |||
data.rawEps = new byte[file.length]; | |||
data.epsFile = new byte[(int) data.psLength]; | |||
System.arraycopy(file, 0, data.rawEps, 0, data.rawEps.length); | |||
System.arraycopy(data.rawEps, (int) data.psStart, data.epsFile, 0, | |||
(int) data.psLength); | |||
} | |||
} | |||
/** | |||
* Get embedded TIFF preview or null. | |||
* | |||
* @param data The EPS payload | |||
* @return The embedded preview | |||
*/ | |||
public byte[] getPreview(EPSImage.EPSData data) { | |||
if (data.preview == null) { | |||
if (data.tiffLength > 0) { | |||
data.preview = new byte[(int) data.tiffLength]; | |||
System.arraycopy(data.rawEps, (int) data.tiffStart, data.preview, 0, | |||
(int) data.tiffLength); | |||
} | |||
} | |||
return data.preview; | |||
} | |||
/** | |||
* Extract bounding box from eps part. | |||
* | |||
* @param data The EPS payload | |||
* @return An Array of four coordinates making up the bounding box | |||
*/ | |||
private long[] readBBox(EPSImage.EPSData data) { | |||
long[] mbbox = null; | |||
int idx = 0; | |||
boolean found = false; | |||
while (!found && (data.epsFile.length > (idx + BOUNDINGBOX.length))) { | |||
boolean sfound = true; | |||
int i = idx; | |||
for (i = idx; sfound && (i - idx) < BOUNDINGBOX.length; i++) { | |||
if (BOUNDINGBOX[i - idx] != data.epsFile[i]) { | |||
sfound = false; | |||
} | |||
} | |||
if (sfound) { | |||
found = true; | |||
idx = i; | |||
} else { | |||
idx++; | |||
} | |||
} | |||
if (!found) { | |||
return mbbox; | |||
} | |||
mbbox = new long[4]; | |||
idx += readLongString(data, mbbox, 0, idx); | |||
idx += readLongString(data, mbbox, 1, idx); | |||
idx += readLongString(data, mbbox, 2, idx); | |||
idx += readLongString(data, mbbox, 3, idx); | |||
return mbbox; | |||
} | |||
private int readLongString(EPSImage.EPSData data, long[] mbbox, int i, int idx) { | |||
while (idx < data.epsFile.length && (data.epsFile[idx] == 32)) { | |||
idx++; | |||
} | |||
int nidx = idx; | |||
// check also for ANSI46(".") to identify floating point values | |||
while (nidx < data.epsFile.length | |||
&& ((data.epsFile[nidx] >= 48 && data.epsFile[nidx] <= 57) | |||
|| (data.epsFile[nidx] == 45) | |||
|| (data.epsFile[nidx] == 46))) { | |||
nidx++; | |||
} | |||
byte[] num = new byte[nidx - idx]; | |||
System.arraycopy(data.epsFile, idx, num, 0, nidx - idx); | |||
String ns = new String(num); | |||
//if( ns.indexOf(".") != -1 ) { | |||
// do something like logging a warning | |||
//} | |||
// then parse the double and round off to the next math. Integer | |||
mbbox[i] = (long) Math.ceil(Double.parseDouble(ns)); | |||
return (1 + nidx - idx); | |||
} | |||
} | |||
@@ -1,104 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for GIF image type. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public class GIFReader implements ImageReader { | |||
private static final int GIF_SIG_LENGTH = 10; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(bis); | |||
boolean supported = ((header[0] == 'G') | |||
&& (header[1] == 'I') | |||
&& (header[2] == 'F') | |||
&& (header[3] == '8') | |||
&& (header[4] == '7' || header[4] == '9') | |||
&& (header[5] == 'a')); | |||
if (supported) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.dpiHorizontal = ua.getFactory().getSourceResolution(); | |||
info.dpiVertical = info.dpiHorizontal; | |||
getDimension(header, info); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = bis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/gif"; | |||
} | |||
private void getDimension(byte[] header, FopImage.ImageInfo info) { | |||
// little endian notation | |||
int byte1 = header[6] & 0xff; | |||
int byte2 = header[7] & 0xff; | |||
info.width = ((byte2 << 8) | byte1) & 0xffff; | |||
byte1 = header[8] & 0xff; | |||
byte2 = header[9] & 0xff; | |||
info.height = ((byte2 << 8) | byte1) & 0xffff; | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) | |||
throws IOException { | |||
byte[] header = new byte[GIF_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(GIF_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
} | |||
@@ -1,56 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader objects read image headers to determine the image size. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public interface ImageReader { | |||
/** | |||
* Verify image type. If the stream does not contain image data expected by | |||
* the reader it must reset the stream to the start. This is so that the | |||
* next reader can start reading from the start. The reader must not close | |||
* the stream unless it can handle the image and it has read the | |||
* information. | |||
* | |||
* @param bis Image buffered input stream | |||
* @param uri URI to the image | |||
* @param ua The user agent | |||
* @return <code>true</code> if image type is the handled one | |||
* @exception IOException if an I/O error occurs | |||
*/ | |||
FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) | |||
throws IOException; | |||
} | |||
@@ -1,108 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.xmlgraphics.util.Service; | |||
/** | |||
* Factory for ImageReader objects. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public class ImageReaderFactory { | |||
private static List formats = new java.util.ArrayList(); | |||
/** logger */ | |||
protected static Log log = LogFactory.getLog(ImageReaderFactory.class); | |||
static { | |||
registerFormat(new JPEGReader()); | |||
registerFormat(new BMPReader()); | |||
registerFormat(new GIFReader()); | |||
registerFormat(new PNGReader()); | |||
registerFormat(new TIFFReader()); | |||
registerFormat(new EPSReader()); | |||
registerFormat(new EMFReader()); | |||
//Dynamic registration of ImageReaders | |||
Iterator iter = Service.providers(ImageReader.class, true); | |||
while (iter.hasNext()) { | |||
registerFormat((ImageReader)iter.next()); | |||
} | |||
// the xml parser through batik closes the stream when finished | |||
// so there is a workaround in the SVGReader | |||
registerFormat(new SVGReader()); | |||
registerFormat(new SVGZReader()); | |||
registerFormat(new XMLReader()); | |||
} | |||
/** | |||
* Registers a new ImageReader. | |||
* | |||
* @param reader An ImageReader instance | |||
*/ | |||
public static void registerFormat(ImageReader reader) { | |||
formats.add(reader); | |||
} | |||
/** | |||
* ImageReader maker. | |||
* | |||
* @param uri URI to the image | |||
* @param in image input stream | |||
* @param ua user agent | |||
* @return An ImageInfo object describing the image | |||
*/ | |||
public static FopImage.ImageInfo make(String uri, InputStream in, | |||
FOUserAgent ua) { | |||
ImageReader reader; | |||
try { | |||
for (int count = 0; count < formats.size(); count++) { | |||
reader = (ImageReader) formats.get(count); | |||
FopImage.ImageInfo info = reader.verifySignature(uri, in, ua); | |||
if (info != null) { | |||
return info; | |||
} | |||
} | |||
log.warn("No ImageReader found for " + uri); | |||
in.close(); | |||
} catch (IOException ex) { | |||
log.error("Error while recovering Image Informations (" | |||
+ uri + ")", ex); | |||
} | |||
return null; | |||
} | |||
} | |||
@@ -1,264 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for JPEG image type. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public class JPEGReader implements ImageReader { | |||
/** | |||
* Only SOFn and APPn markers are defined as SOFn is needed for the height and | |||
* width search. APPn is also defined because if the JPEG contains thumbnails | |||
* the dimensions of the thumnail would also be after the SOFn marker enclosed | |||
* inside the APPn marker. And we don't want to confuse those dimensions with | |||
* the image dimensions. | |||
*/ | |||
private static final int MARK = 0xff; // Beginning of a Marker | |||
private static final int NULL = 0x00; // Special case for 0xff00 | |||
private static final int SOF1 = 0xc0; // Baseline DCT | |||
private static final int SOF2 = 0xc1; // Extended Sequential DCT | |||
private static final int SOF3 = 0xc2; // Progrssive DCT only PDF 1.3 | |||
private static final int SOFA = 0xca; // Progressice DCT only PDF 1.3 | |||
private static final int APP0 = 0xe0; // Application marker, JFIF | |||
private static final int APPF = 0xef; // Application marker | |||
private static final int SOS = 0xda; // Start of Scan | |||
private static final int SOI = 0xd8; // start of Image | |||
private static final int JPG_SIG_LENGTH = 2; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream fis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(fis); | |||
boolean supported = ((header[0] == (byte) 0xff) | |||
&& (header[1] == (byte) 0xd8)); | |||
if (supported) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.dpiHorizontal = ua.getFactory().getSourceResolution(); | |||
info.dpiVertical = info.dpiHorizontal; | |||
getDimension(fis, info); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = fis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/jpeg"; | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) throws IOException { | |||
byte[] header = new byte[JPG_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(JPG_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
private void getDimension(InputStream imageStream, | |||
FopImage.ImageInfo info) | |||
throws IOException { | |||
try { | |||
int pos=0, avail = imageStream.available(); | |||
imageStream.mark(avail); | |||
int marker = NULL; | |||
long length, skipped; | |||
outer: | |||
while (true) { | |||
do { | |||
if (avail == 0) { | |||
imageStream.reset(); | |||
avail = 2*pos; | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
//Marker first byte (FF) | |||
marker = imageStream.read(); | |||
pos++; avail--; | |||
} while (marker != MARK); | |||
do { | |||
if (avail == 0) { | |||
imageStream.reset(); | |||
avail = 2*pos; | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
//Marker second byte | |||
marker = imageStream.read(); | |||
pos++; avail--; | |||
} while (marker == MARK); | |||
switch (marker) { | |||
case SOI: | |||
break; | |||
case NULL: | |||
break; | |||
case APP0: | |||
if (avail < 14) { | |||
imageStream.reset(); | |||
avail = 2 * pos; | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
int reclen = this.read2bytes(imageStream); | |||
pos += 2; avail -= 2; | |||
this.skip(imageStream, 7); | |||
pos += 7; avail -= 7; | |||
int densityUnits = imageStream.read(); | |||
pos++; avail--; | |||
int xdensity = this.read2bytes(imageStream); | |||
pos += 2; avail -= 2; | |||
int ydensity = this.read2bytes(imageStream); | |||
pos += 2; avail -= 2; | |||
if (densityUnits == 2) { | |||
info.dpiHorizontal = xdensity * 28.3464567 / 72; //dpi | |||
info.dpiVertical = ydensity * 28.3464567 / 72; //dpi | |||
} else if (densityUnits == 1) { | |||
info.dpiHorizontal = xdensity; | |||
info.dpiVertical = ydensity; | |||
} else { | |||
// Use resolution specified in | |||
// FOUserAgent.getFactory() (default 72dpi). | |||
} | |||
int restlen = reclen - 12; | |||
if (avail < restlen) { | |||
imageStream.reset(); | |||
avail = 2 * pos; | |||
if (avail < pos + restlen + 10) { | |||
avail = (int)(pos + restlen + 10); | |||
} | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
skipped = this.skip(imageStream, restlen - 2); | |||
pos += skipped; avail -= skipped; | |||
if (skipped != restlen - 2) { | |||
throw new IOException("Skipping Error"); | |||
} | |||
break; | |||
case SOF1: | |||
case SOF2: | |||
case SOF3: // SOF3 and SOFA are only supported by PDF 1.3 | |||
case SOFA: | |||
while (avail < 7) { | |||
imageStream.reset(); | |||
avail = 2*pos; | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
this.skip(imageStream, 3); | |||
pos+=3; avail-=3; | |||
info.height = this.read2bytes(imageStream); | |||
pos+=2; avail-=2; | |||
info.width = this.read2bytes(imageStream); | |||
pos+=2; avail-=2; | |||
break outer; | |||
default: | |||
while (avail < 2) { | |||
imageStream.reset(); | |||
avail = 2*pos; | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
length = this.read2bytes(imageStream); | |||
pos+=2; avail-=2; | |||
if (avail < length) { | |||
imageStream.reset(); | |||
avail = 2*pos; | |||
if (avail < pos+length+10) { | |||
avail = (int)(pos+length+10); | |||
} | |||
imageStream.mark(avail); | |||
pos = (int)this.skip(imageStream, pos); | |||
avail -= pos; | |||
} | |||
skipped = this.skip(imageStream, length - 2); | |||
pos += skipped; avail -= skipped; | |||
if (skipped != length - 2) { | |||
throw new IOException("Skipping Error"); | |||
} | |||
} | |||
} | |||
imageStream.reset(); | |||
} catch (IOException ioe) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ioe; | |||
} | |||
} | |||
private int read2bytes(InputStream imageStream) throws IOException { | |||
int byte1 = imageStream.read(); | |||
int byte2 = imageStream.read(); | |||
return (int) ((byte1 << 8) | byte2); | |||
} | |||
private long skip(InputStream imageStream, long n) throws IOException { | |||
long discarded = 0; | |||
while (discarded != n) { | |||
imageStream.read(); | |||
discarded++; | |||
} | |||
return discarded; // scope for exception | |||
} | |||
} | |||
@@ -1,115 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for PNG image type. | |||
* | |||
* @author Pankaj Narula | |||
* @version $Id$ | |||
*/ | |||
public class PNGReader implements ImageReader { | |||
private static final int PNG_SIG_LENGTH = 24; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(bis); | |||
boolean supported = ((header[0] == (byte) 0x89) | |||
&& (header[1] == (byte) 0x50) | |||
&& (header[2] == (byte) 0x4e) | |||
&& (header[3] == (byte) 0x47) | |||
&& (header[4] == (byte) 0x0d) | |||
&& (header[5] == (byte) 0x0a) | |||
&& (header[6] == (byte) 0x1a) | |||
&& (header[7] == (byte) 0x0a)); | |||
if (supported) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.dpiHorizontal = ua.getFactory().getSourceResolution(); | |||
info.dpiVertical = info.dpiHorizontal; | |||
getDimension(header, info); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = bis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/png"; | |||
} | |||
private void getDimension(byte[] header, FopImage.ImageInfo info) { | |||
// png is always big endian | |||
int byte1 = header[16] & 0xff; | |||
int byte2 = header[17] & 0xff; | |||
int byte3 = header[18] & 0xff; | |||
int byte4 = header[19] & 0xff; | |||
long l = (long) ((byte1 << 24) | |||
| (byte2 << 16) | |||
| (byte3 << 8) | |||
| (byte4)); | |||
info.width = (int) l; | |||
byte1 = header[20] & 0xff; | |||
byte2 = header[21] & 0xff; | |||
byte3 = header[22] & 0xff; | |||
byte4 = header[23] & 0xff; | |||
l = (long) ((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4); | |||
info.height = (int) l; | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) | |||
throws IOException { | |||
byte[] header = new byte[PNG_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(PNG_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
} |
@@ -1,188 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
import java.awt.geom.AffineTransform; | |||
// XML | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.svg.SVGDocument; | |||
// Batik | |||
import org.apache.batik.dom.svg.SAXSVGDocumentFactory; | |||
import org.apache.batik.dom.svg.SVGOMDocument; | |||
import org.apache.batik.bridge.BridgeContext; | |||
import org.apache.batik.bridge.UnitProcessor; | |||
import org.apache.batik.dom.svg.SVGDOMImplementation; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
// FOP | |||
import org.apache.fop.image.XMLImage; | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.svg.SVGUserAgent; | |||
import org.apache.fop.util.UnclosableInputStream; | |||
/** | |||
* ImageReader object for SVG document image type. | |||
*/ | |||
public class SVGReader implements ImageReader { | |||
/** Logger instance */ | |||
protected static Log log = LogFactory.getLog(SVGReader.class); | |||
/** SVG's MIME type */ | |||
public static final String SVG_MIME_TYPE = "image/svg+xml"; | |||
private boolean batik = true; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream fis, | |||
FOUserAgent ua) throws IOException { | |||
FopImage.ImageInfo info = loadImage(uri, fis, ua); | |||
if (info != null) { | |||
IOUtils.closeQuietly(fis); | |||
} | |||
return info; | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return SVG_MIME_TYPE; | |||
} | |||
/** | |||
* This means the external svg document will be loaded twice. Possibly need | |||
* a slightly different design for the image stuff. | |||
* | |||
* @param uri @todo Description of the Parameter | |||
* @param fis @todo Description of the Parameter | |||
* @param ua @todo Description of the Parameter | |||
* @return @todo Description of the Return Value | |||
*/ | |||
private FopImage.ImageInfo loadImage(String uri, InputStream bis, | |||
FOUserAgent ua) { | |||
if (batik) { | |||
try { | |||
Loader loader = new Loader(); | |||
return loader.getImage(uri, bis, | |||
ua.getSourcePixelUnitToMillimeter()); | |||
} catch (NoClassDefFoundError e) { | |||
batik = false; | |||
log.warn("Batik not in class path", e); | |||
return null; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* This method is put in another class so that the classloader does not | |||
* attempt to load batik related classes when constructing the SVGReader | |||
* class. | |||
*/ | |||
class Loader { | |||
private FopImage.ImageInfo getImage(String uri, InputStream fis, | |||
float pixelUnitToMM) { | |||
// parse document and get the size attributes of the svg element | |||
try { | |||
fis = new UnclosableInputStream(fis); | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
//Set the resolution to that of the FOUserAgent | |||
info.dpiHorizontal = 25.4f / pixelUnitToMM; | |||
info.dpiVertical = info.dpiHorizontal; | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.str = SVGDOMImplementation.SVG_NAMESPACE_URI; | |||
int length = fis.available(); | |||
fis.mark(length + 1); | |||
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( | |||
XMLImage.getParserName()); | |||
SVGDocument doc = (SVGDocument) factory.createSVGDocument(uri, fis); | |||
info.data = doc; | |||
Element e = doc.getRootElement(); | |||
String s; | |||
SVGUserAgent userAg = new SVGUserAgent(pixelUnitToMM, | |||
new AffineTransform()); | |||
BridgeContext ctx = new BridgeContext(userAg); | |||
UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e); | |||
// 'width' attribute - default is 100% | |||
s = e.getAttributeNS(null, | |||
SVGOMDocument.SVG_WIDTH_ATTRIBUTE); | |||
if (s.length() == 0) { | |||
s = SVGOMDocument.SVG_SVG_WIDTH_DEFAULT_VALUE; | |||
} | |||
info.width = Math.round(UnitProcessor.svgHorizontalLengthToUserSpace( | |||
s, SVGOMDocument.SVG_WIDTH_ATTRIBUTE, uctx)); | |||
// 'height' attribute - default is 100% | |||
s = e.getAttributeNS(null, | |||
SVGOMDocument.SVG_HEIGHT_ATTRIBUTE); | |||
if (s.length() == 0) { | |||
s = SVGOMDocument.SVG_SVG_HEIGHT_DEFAULT_VALUE; | |||
} | |||
info.height = Math.round(UnitProcessor.svgVerticalLengthToUserSpace( | |||
s, SVGOMDocument.SVG_HEIGHT_ATTRIBUTE, uctx)); | |||
return info; | |||
} catch (NoClassDefFoundError ncdfe) { | |||
try { | |||
fis.reset(); | |||
} catch (IOException ioe) { | |||
// we're more interested in the original exception | |||
} | |||
batik = false; | |||
log.warn("Batik not in class path", ncdfe); | |||
return null; | |||
} catch (IOException e) { | |||
// If the svg is invalid then it throws an IOException | |||
// so there is no way of knowing if it is an svg document | |||
log.debug("Error while trying to load stream as an SVG file: " | |||
+ e.getMessage()); | |||
// assuming any exception means this document is not svg | |||
// or could not be loaded for some reason | |||
try { | |||
fis.reset(); | |||
} catch (IOException ioe) { | |||
// we're more interested in the original exception | |||
} | |||
return null; | |||
} | |||
} | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.zip.GZIPInputStream; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.image.FopImage; | |||
/** | |||
* Implements a reader for gzipped XMLFiles. | |||
* | |||
* <p> | |||
* The current implementation is limited to SVG files only. | |||
*/ | |||
public class SVGZReader extends XMLReader { | |||
/** | |||
* Default constructor. | |||
*/ | |||
public SVGZReader() { | |||
} | |||
/** {@inheritDoc} */ | |||
protected FopImage.ImageInfo loadImage(final String uri, | |||
final InputStream bis, final FOUserAgent ua) { | |||
try { | |||
return new SVGReader().verifySignature(uri, | |||
new GZIPInputStream(bis), ua); | |||
} catch (final IOException e) { | |||
// ignore | |||
} | |||
return null; | |||
} | |||
} |
@@ -1,117 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.apps.FOUserAgent; | |||
/** | |||
* ImageReader object for TIFF image type. | |||
* | |||
* @author Pankaj Narula, Michael Lee | |||
* @version $Id$ | |||
*/ | |||
public class TIFFReader implements ImageReader { | |||
private static final int TIFF_SIG_LENGTH = 8; | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream bis, | |||
FOUserAgent ua) throws IOException { | |||
byte[] header = getDefaultHeader(bis); | |||
boolean supported = false; | |||
// first 2 bytes = II (little endian encoding) | |||
if (header[0] == (byte) 0x49 && header[1] == (byte) 0x49) { | |||
// look for '42' in byte 3 and '0' in byte 4 | |||
if (header[2] == 42 && header[3] == 0) { | |||
supported = true; | |||
} | |||
} | |||
// first 2 bytes == MM (big endian encoding) | |||
if (header[0] == (byte) 0x4D && header[1] == (byte) 0x4D) { | |||
// look for '42' in byte 4 and '0' in byte 3 | |||
if (header[2] == 0 && header[3] == 42) { | |||
supported = true; | |||
} | |||
} | |||
if (supported) { | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.dpiHorizontal = ua.getFactory().getSourceResolution(); | |||
info.dpiVertical = info.dpiHorizontal; | |||
getDimension(header, info); | |||
info.originalURI = uri; | |||
info.mimeType = getMimeType(); | |||
info.inputStream = bis; | |||
return info; | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "image/tiff"; | |||
} | |||
private void getDimension(byte[] header, FopImage.ImageInfo info) { | |||
// currently not setting the width and height | |||
// these are set again by the Jimi image reader. | |||
// I suppose I'll do it one day to be complete. Or | |||
// someone else will. | |||
// Note: bytes 4,5,6,7 contain the byte offset in the stream of the first IFD block | |||
info.width = -1; | |||
info.height = -1; | |||
} | |||
private byte[] getDefaultHeader(InputStream imageStream) | |||
throws IOException { | |||
byte[] header = new byte[TIFF_SIG_LENGTH]; | |||
try { | |||
imageStream.mark(TIFF_SIG_LENGTH + 1); | |||
imageStream.read(header); | |||
imageStream.reset(); | |||
} catch (IOException ex) { | |||
try { | |||
imageStream.reset(); | |||
} catch (IOException exbis) { | |||
// throw the original exception, not this one | |||
} | |||
throw ex; | |||
} | |||
return header; | |||
} | |||
} | |||
@@ -1,167 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.image.analyser; | |||
// Java | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
import java.util.Map; | |||
// XML | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
// FOP | |||
import org.apache.fop.image.FopImage; | |||
import org.apache.fop.util.UnclosableInputStream; | |||
import org.apache.fop.apps.FOUserAgent; | |||
// Commons-Logging | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
/** ImageReader object for XML document image type. */ | |||
public class XMLReader implements ImageReader { | |||
/** | |||
* logging instance | |||
*/ | |||
private Log log = LogFactory.getLog(XMLReader.class); | |||
private static Map converters = new java.util.HashMap(); | |||
/** | |||
* Registers a Converter implementation with XMLReader. | |||
* | |||
* @param ns The namespace to associate with this converter | |||
* @param conv The actual Converter implementation | |||
*/ | |||
public static void setConverter(String ns, Converter conv) { | |||
converters.put(ns, conv); | |||
} | |||
/** {@inheritDoc} */ | |||
public FopImage.ImageInfo verifySignature(String uri, InputStream fis, | |||
FOUserAgent ua) | |||
throws IOException { | |||
FopImage.ImageInfo info = loadImage(uri, fis, ua); | |||
if (info != null) { | |||
info.originalURI = uri; | |||
IOUtils.closeQuietly(fis); | |||
} | |||
return info; | |||
} | |||
/** | |||
* Returns the MIME type supported by this implementation. | |||
* | |||
* @return The MIME type | |||
*/ | |||
public String getMimeType() { | |||
return "text/xml"; | |||
} | |||
/** | |||
* Creates an ImageInfo object from an XML image read from a stream. | |||
* | |||
* (todo) This means the external svg document will be loaded twice. Possibly need | |||
* a slightly different design for the image stuff. | |||
* | |||
* @param uri The URI to the image | |||
* @param bis The InputStream | |||
* @param ua The user agent | |||
* @return An ImageInfo object describing the image | |||
*/ | |||
protected FopImage.ImageInfo loadImage(String uri, InputStream bis, | |||
FOUserAgent ua) { | |||
return createDocument(bis, ua); | |||
} | |||
/** | |||
* Creates an ImageInfo object from an XML image read from a stream. | |||
* | |||
* @param input The InputStream | |||
* @param ua The user agent | |||
* @return An ImageInfo object describing the image | |||
*/ | |||
public FopImage.ImageInfo createDocument(final InputStream input, final FOUserAgent ua) { | |||
Document doc = null; | |||
FopImage.ImageInfo info = new FopImage.ImageInfo(); | |||
info.mimeType = getMimeType(); | |||
try { | |||
final InputStream is = new UnclosableInputStream(input); | |||
int length = is.available(); | |||
is.mark(length); | |||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||
doc = dbf.newDocumentBuilder().parse(is); | |||
info.data = doc; | |||
Element root = doc.getDocumentElement(); | |||
log.debug("XML image namespace: " + root.getAttribute("xmlns")); | |||
String ns = root.getAttribute("xmlns"); | |||
info.str = ns; | |||
Converter conv = (Converter) converters.get(ns); | |||
if (conv != null) { | |||
FopImage.ImageInfo i = conv.convert(doc); | |||
if (i != null) { | |||
info = i; | |||
} | |||
} | |||
} catch (Exception e) { | |||
log.debug("Error while constructing image from XML", e); | |||
try { | |||
input.reset(); | |||
} catch (IOException ioe) { | |||
// throw the original exception, not this one | |||
} | |||
return null; | |||
} | |||
if (info != null) { | |||
try { | |||
input.close(); | |||
} catch (IOException io) { | |||
// ignore | |||
} | |||
} | |||
return info; | |||
} | |||
/** | |||
* This interface is to be implemented for XML to image converters. | |||
*/ | |||
public static interface Converter { | |||
/** | |||
* This method is called for a DOM document to be converted into an | |||
* ImageInfo object. | |||
* | |||
* @param doc The DOM document to convert | |||
* @return An ImageInfo object describing the image | |||
*/ | |||
FopImage.ImageInfo convert(Document doc); | |||
} | |||
} | |||
@@ -1,23 +0,0 @@ | |||
<!-- | |||
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. | |||
--> | |||
<!-- $Id$ --> | |||
<HTML> | |||
<TITLE>org.apache.fop.image.analyser Package</TITLE> | |||
<BODY> | |||
<P>Image analyzers for determining the format of an image and to preload its intrinsic size.</P> | |||
</BODY> | |||
</HTML> |
@@ -18,6 +18,6 @@ | |||
<HTML> | |||
<TITLE>org.apache.fop.image Package</TITLE> | |||
<BODY> | |||
<P>Contains image loading adapters for various image sources and the image cache.</P> | |||
<P>Contains image loading adapters for various image sources.</P> | |||
</BODY> | |||
</HTML> |
@@ -1,73 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.ps; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.fop.image.EPSImage; | |||
import org.apache.fop.image.FopImage; | |||
/** | |||
* Utility code for rendering images in PostScript. | |||
*/ | |||
public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils { | |||
/** logging instance */ | |||
protected static Log log = LogFactory.getLog(PSImageUtils.class); | |||
/** | |||
* Renders an EPS image to PostScript. | |||
* @param img EPS image to render | |||
* @param x x position | |||
* @param y y position | |||
* @param w width | |||
* @param h height | |||
* @param gen PS generator | |||
* @deprecated Use {@link #renderEPS(java.io.InputStream, String, java.awt.geom.Rectangle2D, | |||
* java.awt.geom.Rectangle2D, PSGenerator)} instead | |||
*/ | |||
public static void renderEPS(EPSImage img, | |||
float x, float y, float w, float h, | |||
PSGenerator gen) { | |||
try { | |||
if (!img.load(FopImage.ORIGINAL_DATA)) { | |||
gen.commentln("%EPS image could not be processed: " + img); | |||
return; | |||
} | |||
int[] bbox = img.getBBox(); | |||
int bboxw = bbox[2] - bbox[0]; | |||
int bboxh = bbox[3] - bbox[1]; | |||
String name = img.getDocName(); | |||
if (name == null || name.length() == 0) { | |||
name = img.getOriginalURI(); | |||
} | |||
renderEPS(img.getEPSImage(), name, | |||
x, y, w, h, | |||
bbox[0], bbox[1], bboxw, bboxh, gen); | |||
} catch (Exception e) { | |||
log.error("PSRenderer.renderImageArea(): Error rendering bitmap (" | |||
+ e.getMessage() + ")", e); | |||
} | |||
} | |||
} |
@@ -1,70 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.rtf; | |||
import org.apache.batik.transcoder.TranscoderException; | |||
import org.apache.batik.transcoder.TranscoderInput; | |||
import org.apache.batik.transcoder.TranscoderOutput; | |||
import org.apache.batik.transcoder.image.JPEGTranscoder; | |||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.image.XMLImage; | |||
/** | |||
* Helper class for converting SVG to bitmap images. | |||
*/ | |||
public final class SVGConverter { | |||
/** logger instance */ | |||
private static Log log = LogFactory.getLog(SVGConverter.class); | |||
/** | |||
* Constructor is private, because it's just a utility class. | |||
*/ | |||
private SVGConverter() { | |||
} | |||
/** | |||
* Converts a SVG image to a JPEG bitmap. | |||
* @param image the SVG image | |||
* @return a byte array containing the JPEG image | |||
*/ | |||
public static byte[] convertToJPEG(XMLImage image) { | |||
JPEGTranscoder transcoder = new JPEGTranscoder(); | |||
/* TODO Disabled to avoid side-effect due to the mixing of source and target resolutions | |||
* This should be reenabled when it has been determined how exactly to handle this | |||
transcoder.addTranscodingHint(ImageTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, | |||
new Float(25.4f / 300)); //300dpi should be enough for now. | |||
*/ | |||
transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(0.9f)); | |||
TranscoderInput input = new TranscoderInput(image.getDocument()); | |||
ByteArrayOutputStream baout = new ByteArrayOutputStream(16384); | |||
TranscoderOutput output = new TranscoderOutput(baout); | |||
try { | |||
transcoder.transcode(input, output); | |||
return baout.toByteArray(); | |||
} catch (TranscoderException e) { | |||
log.error(e); | |||
return null; | |||
} | |||
} | |||
} |
@@ -36,17 +36,19 @@ import javax.xml.transform.sax.TransformerHandler; | |||
import javax.xml.transform.stream.StreamResult; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.w3c.dom.Document; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||
import org.apache.xpath.XPathAPI; | |||
import org.apache.xpath.objects.XObject; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.Fop; | |||
import org.apache.fop.apps.FopFactory; | |||
import org.apache.fop.apps.MimeConstants; | |||
import org.apache.fop.render.xml.XMLRenderer; | |||
import org.apache.xpath.XPathAPI; | |||
import org.apache.xpath.objects.XObject; | |||
import org.w3c.dom.Document; | |||
/** | |||
* Tests URI resolution facilities. | |||
@@ -85,9 +87,6 @@ public class URIResolutionTestCase extends AbstractFOPTestCase { | |||
private void innerTestFO1(boolean withStream) throws Exception { | |||
FOUserAgent ua = fopFactory.newFOUserAgent(); | |||
//Reset the image caches to force URI resolution! | |||
ua.getFactory().getImageFactory().clearCaches(); | |||
File foFile = new File(getBaseDir(), "test/xml/uri-resolution1.fo"); | |||
MyURIResolver resolver = new MyURIResolver(withStream); |