aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java5
-rw-r--r--src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java5
-rw-r--r--src/java/org/apache/fop/render/ps/PSFontUtils.java241
-rw-r--r--src/java/org/apache/fop/render/ps/PSGenerator.java34
-rw-r--r--src/java/org/apache/fop/render/ps/PSProcSets.java80
-rw-r--r--src/java/org/apache/fop/render/ps/PSRenderer.java28
-rw-r--r--src/java/org/apache/fop/render/ps/PSResource.java7
-rw-r--r--src/java/org/apache/fop/util/SubInputStream.java92
8 files changed, 399 insertions, 93 deletions
diff --git a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java
index 7a8eacce9..81a202940 100644
--- a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java
+++ b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003,2004 The Apache Software Foundation.
+ * Copyright 2003-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ import org.apache.fop.apps.Fop;
* This class is a wrapper for the <tt>AbstractPSDocumentGraphics2D</tt> that
* is used to create EPS (Encapsulated PostScript) files instead of PS file.
*
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
* @version $Id$
* @see org.apache.fop.render.ps.PSGraphics2D
* @see org.apache.fop.render.ps.AbstractPSDocumentGraphics2D
@@ -70,7 +69,7 @@ public class EPSDocumentGraphics2D extends AbstractPSDocumentGraphics2D {
PSProcSets.writeFOPStdProcSet(gen);
PSProcSets.writeFOPEPSProcSet(gen);
if (fontInfo != null) {
- PSProcSets.writeFontDict(gen, fontInfo);
+ PSFontUtils.writeFontDict(gen, fontInfo);
}
gen.writeDSCComment(DSCConstants.END_PROLOG);
}
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java
index 4a3a05801..afa4a83e2 100644
--- a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java
+++ b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@ import org.apache.fop.fonts.FontSetup;
* <tt>PSGraphics2D</tt>.
*
* @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
* @version $Id$
* @see org.apache.fop.render.ps.PSGraphics2D
*/
@@ -114,7 +113,7 @@ public class PSDocumentGraphics2D extends AbstractPSDocumentGraphics2D {
PSProcSets.writeFOPStdProcSet(gen);
PSProcSets.writeFOPEPSProcSet(gen);
if (fontInfo != null) {
- PSProcSets.writeFontDict(gen, fontInfo);
+ PSFontUtils.writeFontDict(gen, fontInfo);
}
gen.writeDSCComment(DSCConstants.END_SETUP);
}
diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java
new file mode 100644
index 000000000..8bcd0c413
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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 java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.EndianUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontType;
+import org.apache.fop.fonts.Glyphs;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.util.ASCIIHexOutputStream;
+import org.apache.fop.util.SubInputStream;
+
+/**
+ * Utility code for font handling in PostScript.
+ */
+public class PSFontUtils {
+
+ /**
+ * Generates the PostScript code for the font dictionary.
+ * @param gen PostScript generator to use for output
+ * @param fontInfo available fonts
+ * @return a Map of PSResource instances representing all defined fonts (key: font key)
+ * @throws IOException in case of an I/O problem
+ */
+ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo)
+ throws IOException {
+ gen.commentln("%FOPBeginFontDict");
+ gen.writeln("/FOPFonts 100 dict dup begin");
+
+ // write("/gfF1{/Helvetica findfont} bd");
+ // write("/gfF3{/Helvetica-Bold findfont} bd");
+ Map fonts = fontInfo.getFonts();
+ Map fontResources = new java.util.HashMap();
+ Iterator iter = fonts.keySet().iterator();
+ while (iter.hasNext()) {
+ String key = (String)iter.next();
+ Typeface tf = (Typeface)fonts.get(key);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ PSResource fontRes = new PSResource("font", tf.getFontName());
+ fontResources.put(key, fontRes);
+ boolean embeddedFont = false;
+ if (FontType.TYPE1 == tf.getFontType()) {
+ if (tf instanceof CustomFont) {
+ CustomFont cf = (CustomFont)tf;
+ InputStream in = getInputStreamOnFont(gen, cf);
+ if (in != null) {
+ gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
+ fontRes);
+ embedType1Font(gen, in);
+ gen.writeDSCComment(DSCConstants.END_RESOURCE);
+ gen.notifyResourceUsage(fontRes, false);
+ embeddedFont = true;
+ }
+ }
+ }
+ if (!embeddedFont) {
+ gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ //Resource usage shall be handled by renderer
+ //gen.notifyResourceUsage(fontRes, true);
+ }
+ gen.commentln("%FOPBeginFontKey: " + key);
+ gen.writeln("/" + key + " /" + tf.getFontName() + " def");
+ gen.commentln("%FOPEndFontKey");
+ }
+ gen.writeln("end def");
+ gen.commentln("%FOPEndFontDict");
+ gen.commentln("%FOPBeginFontReencode");
+ defineWinAnsiEncoding(gen);
+
+ //Rewrite font encodings
+ iter = fonts.keySet().iterator();
+ while (iter.hasNext()) {
+ String key = (String)iter.next();
+ Typeface fm = (Typeface)fonts.get(key);
+ if (null == fm.getEncoding()) {
+ //ignore (ZapfDingbats and Symbol run through here
+ //TODO: ZapfDingbats and Symbol should get getEncoding() fixed!
+ } else if ("WinAnsiEncoding".equals(fm.getEncoding())) {
+ gen.writeln("/" + fm.getFontName() + " findfont");
+ gen.writeln("dup length dict begin");
+ gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall");
+ gen.writeln(" /Encoding " + fm.getEncoding() + " def");
+ gen.writeln(" currentdict");
+ gen.writeln("end");
+ gen.writeln("/" + fm.getFontName() + " exch definefont pop");
+ } else {
+ System.out.println("Only WinAnsiEncoding is supported. Font '"
+ + fm.getFontName() + "' asks for: " + fm.getEncoding());
+ }
+ }
+ gen.commentln("%FOPEndFontReencode");
+ return fontResources;
+ }
+
+ /**
+ * This method reads a Type 1 font from a stream and embeds it into a PostScript stream.
+ * Note: Only the IBM PC Format as described in section 3.3 of the Adobe Technical Note #5040
+ * is supported.
+ * @param gen The PostScript generator
+ * @param in the InputStream from which to read the Type 1 font
+ * @throws IOException in case an I/O problem occurs
+ */
+ private static void embedType1Font(PSGenerator gen, InputStream in) throws IOException {
+ boolean finished = false;
+ while (!finished) {
+ int segIndicator = in.read();
+ if (segIndicator < 0) {
+ throw new IOException("Unexpected end-of-file while reading segment indicator");
+ } else if (segIndicator != 128) {
+ throw new IOException("Expected ASCII 128, found: " + segIndicator);
+ }
+ int segType = in.read();
+ if (segType < 0) {
+ throw new IOException("Unexpected end-of-file while reading segment type");
+ }
+ int dataSegLen = 0;
+ switch (segType) {
+ case 1: //ASCII
+ dataSegLen = EndianUtils.readSwappedInteger(in);
+
+ BufferedReader reader = new BufferedReader(
+ new java.io.InputStreamReader(
+ new SubInputStream(in, dataSegLen), "US-ASCII"));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ gen.writeln(line);
+ }
+ break;
+ case 2: //binary
+ dataSegLen = EndianUtils.readSwappedInteger(in);
+
+ SubInputStream sin = new SubInputStream(in, dataSegLen);
+ ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+ IOUtils.copy(sin, hexOut);
+ gen.newLine();
+ break;
+ case 3: //EOF
+ finished = true;
+ break;
+ default: throw new IOException("Unsupported segment type: " + segType);
+ }
+ }
+ }
+
+ private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font)
+ throws IOException {
+ if (font.isEmbeddable()) {
+ Source source = null;
+ if (font.getEmbedFileName() != null) {
+ source = gen.resolveURI(font.getEmbedFileName());
+ }
+ if (source == null && font.getEmbedResourceName() != null) {
+ source = new StreamSource(PSFontUtils.class
+ .getResourceAsStream(font.getEmbedResourceName()));
+ }
+ if (source == null) {
+ return null;
+ }
+ InputStream in = null;
+ if (source instanceof StreamSource) {
+ in = ((StreamSource) source).getInputStream();
+ }
+ if (in == null && source.getSystemId() != null) {
+ try {
+ in = new java.net.URL(source.getSystemId()).openStream();
+ } catch (MalformedURLException e) {
+ new FileNotFoundException(
+ "File not found. URL could not be resolved: "
+ + e.getMessage());
+ }
+ }
+ 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);
+ }
+ return in;
+ } else {
+ return null;
+ }
+ }
+
+ private static void defineWinAnsiEncoding(PSGenerator gen) throws IOException {
+ gen.writeln("/WinAnsiEncoding [");
+ for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) {
+ if (i > 0) {
+ if ((i % 5) == 0) {
+ gen.newLine();
+ } else {
+ gen.write(" ");
+ }
+ }
+ final char ch = Glyphs.WINANSI_ENCODING[i];
+ final String glyphname = Glyphs.charToGlyphName(ch);
+ if ("".equals(glyphname)) {
+ gen.write("/" + Glyphs.NOTDEF);
+ } else {
+ gen.write("/");
+ gen.write(glyphname);
+ }
+ }
+ gen.newLine();
+ gen.writeln("] def");
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSGenerator.java b/src/java/org/apache/fop/render/ps/PSGenerator.java
index efd07cab0..14d5b18fa 100644
--- a/src/java/org/apache/fop/render/ps/PSGenerator.java
+++ b/src/java/org/apache/fop/render/ps/PSGenerator.java
@@ -31,6 +31,8 @@ import java.util.Locale;
import java.util.Set;
import java.util.Stack;
+import javax.xml.transform.Source;
+
/**
* This class is used to output PostScript code to an OutputStream.
*
@@ -85,6 +87,17 @@ public class PSGenerator {
}
/**
+ * Attempts to resolve the given URI. PSGenerator should be subclasses to provide more
+ * sophisticated URI resolution.
+ * @param uri URI to access
+ * @return A {@link javax.xml.transform.Source} object, or null if the URI
+ * cannot be resolved.
+ */
+ public Source resolveURI(String uri) {
+ return new javax.xml.transform.stream.StreamSource(uri);
+ }
+
+ /**
* Writes a newline character to the OutputStream.
*
* @throws IOException In case of an I/O problem
@@ -241,15 +254,15 @@ public class PSGenerator {
initialSize += initialSize / 2;
StringBuffer sb = new StringBuffer(initialSize);
if ((Long.getLong(text) != null)
- || (text.indexOf(" ") >= 0)
+ || (text.indexOf(' ') >= 0)
|| forceParentheses) {
- sb.append("(");
+ sb.append('(');
for (int i = 0; i < text.length(); i++) {
final char c = text.charAt(i);
escapeChar(c, sb);
}
- sb.append(")");
+ sb.append(')');
return sb.toString();
} else {
return text;
@@ -315,6 +328,8 @@ public class PSGenerator {
} else if (params[i] instanceof Date) {
DateFormat datef = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
tempBuffer.append(convertStringToDSC(datef.format((Date)params[i])));
+ } else if (params[i] instanceof PSResource) {
+ tempBuffer.append(((PSResource)params[i]).getResourceSpecification());
} else {
throw new IllegalArgumentException("Unsupported parameter type: "
+ params[i].getClass().getName());
@@ -528,6 +543,15 @@ public class PSGenerator {
}
/**
+ * Indicates whether a particular resource is supplied, rather than needed.
+ * @param res the resource
+ * @return true if the resource is registered as being supplied.
+ */
+ public boolean isResourceSupplied(PSResource res) {
+ return documentSuppliedResources.contains(res);
+ }
+
+ /**
* Writes a DSC comment for the accumulated used resources, either at page level or
* at document level.
* @param pageLevel true if the DSC comment for the page level should be generated,
@@ -562,9 +586,7 @@ public class PSGenerator {
tempBuffer.append("%%+ ");
}
PSResource res = (PSResource)i.next();
- tempBuffer.append(res.getType());
- tempBuffer.append(" ");
- tempBuffer.append(res.getName());
+ tempBuffer.append(res.getResourceSpecification());
first = false;
}
writeln(tempBuffer.toString());
diff --git a/src/java/org/apache/fop/render/ps/PSProcSets.java b/src/java/org/apache/fop/render/ps/PSProcSets.java
index c56a26a72..56ee8fd0e 100644
--- a/src/java/org/apache/fop/render/ps/PSProcSets.java
+++ b/src/java/org/apache/fop/render/ps/PSProcSets.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 2001-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,6 @@
package org.apache.fop.render.ps;
import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.fop.fonts.Typeface;
-import org.apache.fop.fonts.Glyphs;
-import org.apache.fop.fonts.FontInfo;
/**
* This class defines the basic resources (procsets) used by FOP's PostScript
@@ -211,76 +205,4 @@ public final class PSProcSets {
((EPSProcSet)EPS_PROCSET).writeTo(gen);
}
- /**
- * Generates the PostScript code for the font dictionary.
- * @param gen PostScript generator to use for output
- * @param fontInfo available fonts
- * @throws IOException in case of an I/O problem
- */
- public static void writeFontDict(PSGenerator gen, FontInfo fontInfo)
- throws IOException {
- gen.commentln("%FOPBeginFontDict");
- gen.writeln("/FOPFonts 100 dict dup begin");
-
- // write("/gfF1{/Helvetica findfont} bd");
- // write("/gfF3{/Helvetica-Bold findfont} bd");
- Map fonts = fontInfo.getFonts();
- Iterator iter = fonts.keySet().iterator();
- while (iter.hasNext()) {
- String key = (String)iter.next();
- Typeface fm = (Typeface)fonts.get(key);
- gen.writeln("/" + key + " /" + fm.getFontName() + " def");
- }
- gen.writeln("end def");
- gen.commentln("%FOPEndFontDict");
- gen.commentln("%FOPBeginFontReencode");
- defineWinAnsiEncoding(gen);
-
- //Rewrite font encodings
- iter = fonts.keySet().iterator();
- while (iter.hasNext()) {
- String key = (String)iter.next();
- Typeface fm = (Typeface)fonts.get(key);
- if (null == fm.getEncoding()) {
- //ignore (ZapfDingbats and Symbol run through here
- //TODO: ZapfDingbats and Symbol should get getEncoding() fixed!
- } else if ("WinAnsiEncoding".equals(fm.getEncoding())) {
- gen.writeln("/" + fm.getFontName() + " findfont");
- gen.writeln("dup length dict begin");
- gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall");
- gen.writeln(" /Encoding " + fm.getEncoding() + " def");
- gen.writeln(" currentdict");
- gen.writeln("end");
- gen.writeln("/" + fm.getFontName() + " exch definefont pop");
- } else {
- System.out.println("Only WinAnsiEncoding is supported. Font '"
- + fm.getFontName() + "' asks for: " + fm.getEncoding());
- }
- }
- gen.commentln("%FOPEndFontReencode");
- }
-
- private static void defineWinAnsiEncoding(PSGenerator gen) throws IOException {
- gen.writeln("/WinAnsiEncoding [");
- for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) {
- if (i > 0) {
- if ((i % 5) == 0) {
- gen.newLine();
- } else {
- gen.write(" ");
- }
- }
- final char ch = Glyphs.WINANSI_ENCODING[i];
- final String glyphname = Glyphs.charToGlyphName(ch);
- if ("".equals(glyphname)) {
- gen.write("/" + Glyphs.NOTDEF);
- } else {
- gen.write("/");
- gen.write(glyphname);
- }
- }
- gen.newLine();
- gen.writeln("] def");
- }
-
}
diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java
index f674942bb..7d3c046a1 100644
--- a/src/java/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/java/org/apache/fop/render/ps/PSRenderer.java
@@ -28,6 +28,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.xml.transform.Source;
+
// FOP
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
@@ -103,6 +105,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
/** Used to temporarily store PSSetupCode instance until they can be written. */
private List setupCodeList;
+
+ /** This is a map of PSResource instances of all fonts defined (key: font key) */
+ private Map fontResources;
/**
* @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
@@ -561,7 +566,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
log.debug("rendering areas to PostScript");
//Setup for PostScript generation
- this.gen = new PSGenerator(outputStream);
+ this.gen = new PSGenerator(outputStream) {
+ /** Need to subclass PSGenerator to have better URI resolution */
+ public Source resolveURI(String uri) {
+ return userAgent.resolveURI(uri);
+ }
+ };
this.currentPageNumber = 0;
//PostScript Header
@@ -585,6 +595,20 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
* @see org.apache.fop.render.Renderer#stopRenderer()
*/
public void stopRenderer() throws IOException {
+ //Notify resource usage for font which are not supplied
+ Map fonts = fontInfo.getUsedFonts();
+ Iterator e = fonts.keySet().iterator();
+ while (e.hasNext()) {
+ String key = (String)e.next();
+ //Typeface font = (Typeface)fonts.get(key);
+ PSResource res = (PSResource)this.fontResources.get(key);
+ boolean supplied = gen.isResourceSupplied(res);
+ if (!supplied) {
+ gen.notifyResourceUsage(res, true);
+ }
+ }
+
+ //Write trailer
gen.writeDSCComment(DSCConstants.TRAILER);
gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber));
gen.writeResources(false);
@@ -623,7 +647,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer {
//Setup
gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
writeSetupCodeList(setupCodeList, "SetupCode");
- PSProcSets.writeFontDict(gen, fontInfo);
+ this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
gen.writeln("FOPFonts begin");
gen.writeDSCComment(DSCConstants.END_SETUP);
} catch (IOException ioe) {
diff --git a/src/java/org/apache/fop/render/ps/PSResource.java b/src/java/org/apache/fop/render/ps/PSResource.java
index d37850a2e..b68bcaf6d 100644
--- a/src/java/org/apache/fop/render/ps/PSResource.java
+++ b/src/java/org/apache/fop/render/ps/PSResource.java
@@ -53,4 +53,11 @@ public class PSResource {
return this.name;
}
+ /** @return the <resource> specification as defined in DSC v3.0 spec. */
+ public String getResourceSpecification() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getType()).append(" ").append(PSGenerator.convertStringToDSC(getName()));
+ return sb.toString();
+ }
+
}
diff --git a/src/java/org/apache/fop/util/SubInputStream.java b/src/java/org/apache/fop/util/SubInputStream.java
new file mode 100644
index 000000000..83b146402
--- /dev/null
+++ b/src/java/org/apache/fop/util/SubInputStream.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.util;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class is a FilterInputStream descendant that reads from an underlying InputStream
+ * up to a defined number of bytes or the end of the underlying stream. Closing this InputStream
+ * will not result in the underlying InputStream to be closed, too.
+ * <p>
+ * This InputStream can be used to read chunks from a larger file of which the length is
+ * known in advance.
+ */
+public class SubInputStream extends FilterInputStream {
+
+ /** Indicates the number of bytes remaning to be read from the underlying InputStream. */
+ private long bytesToRead;
+
+ /**
+ * Creates a new SubInputStream.
+ * @param in the InputStream to read from
+ * @param maxLen the maximum number of bytes to read from the underlying InputStream until
+ * the end-of-file is signalled.
+ */
+ public SubInputStream(InputStream in, long maxLen) {
+ super(in);
+ this.bytesToRead = maxLen;
+ }
+
+ /** @see java.io.InputStream#read() */
+ public int read() throws IOException {
+ if (bytesToRead > 0) {
+ int result = super.read();
+ if (result <= 0) {
+ bytesToRead--;
+ return result;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ /** @see java.io.InputStream#read(byte[], int, int) */
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (bytesToRead == 0) {
+ return -1;
+ }
+ int effRead = (int)Math.min(bytesToRead, len);
+ //cast to int is safe because len can never be bigger than Integer.MAX_VALUE
+
+ int result = super.read(b, off, effRead);
+ if (result >= 0) {
+ bytesToRead -= result;
+ }
+ return result;
+ }
+
+ /** @see java.io.InputStream#skip(long) */
+ public long skip(long n) throws IOException {
+ long effRead = Math.min(bytesToRead, n);
+ long result = super.skip(effRead);
+ bytesToRead -= result;
+ return result;
+ }
+
+ /** @see java.io.InputStream#close() */
+ public void close() throws IOException {
+ //Don't close the underlying InputStream!!!
+ this.bytesToRead = 0;
+ }
+}