aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/org/apache/poi/hssf/dev/BiffViewer.java12
-rw-r--r--src/java/org/apache/poi/hssf/record/RecordInputStream.java32
-rw-r--r--src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java16
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawFactory.java38
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java51
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawPictureShape.java61
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawTextParagraph.java8
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawTextShape.java5
-rw-r--r--src/java/org/apache/poi/sl/draw/ImageRenderer.java7
-rw-r--r--src/java/org/apache/poi/util/Dimension2DDouble.java73
-rw-r--r--src/java/org/apache/poi/util/IOUtils.java31
-rw-r--r--src/java/org/apache/poi/util/LittleEndianInputStream.java59
12 files changed, 275 insertions, 118 deletions
diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
index cb17d84b56..631be8fd2d 100644
--- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java
+++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
@@ -527,20 +527,16 @@ public final class BiffViewer {
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null || off < 0 || len < 0 || b.length < off+len) {
+ throw new IllegalArgumentException();
+ }
if (_currentPos >= _currentSize) {
fillNextBuffer();
}
if (_currentPos >= _currentSize) {
return -1;
}
- int availSize = _currentSize - _currentPos;
- int result;
- if (len > availSize) {
- System.err.println("Unexpected request to read past end of current biff record");
- result = availSize;
- } else {
- result = len;
- }
+ final int result = Math.min(len, _currentSize - _currentPos);
System.arraycopy(_data, _currentPos, b, off, result);
_currentPos += result;
_overallStreamPos += result;
diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
index 8347655a81..400553721e 100644
--- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java
+++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
@@ -106,8 +106,8 @@ public final class RecordInputStream implements LittleEndianInput {
private final LittleEndianInput _lei;
- public SimpleHeaderInput(InputStream in) {
- _lei = getLEI(in);
+ private SimpleHeaderInput(LittleEndianInput lei) {
+ _lei = lei;
}
@Override
public int available() {
@@ -129,8 +129,12 @@ public final class RecordInputStream implements LittleEndianInput {
public RecordInputStream(InputStream in, EncryptionInfo key, int initialOffset) throws RecordFormatException {
if (key == null) {
- _dataInput = getLEI(in);
- _bhi = new SimpleHeaderInput(in);
+ _dataInput = (in instanceof LittleEndianInput)
+ // accessing directly is an optimisation
+ ? (LittleEndianInput)in
+ // less optimal, but should work OK just the same. Often occurs in junit tests.
+ : new LittleEndianInputStream(in);
+ _bhi = new SimpleHeaderInput(_dataInput);
} else {
Biff8DecryptingStream bds = new Biff8DecryptingStream(in, initialOffset, key);
_dataInput = bds;
@@ -195,11 +199,9 @@ public final class RecordInputStream implements LittleEndianInput {
private int readNextSid() {
int nAvailable = _bhi.available();
if (nAvailable < EOFRecord.ENCODED_SIZE) {
- /*if (nAvailable > 0) {
- // some scrap left over?
- // ex45582-22397.xls has one extra byte after the last record
- // Excel reads that file OK
- }*/
+ // some scrap left over, if nAvailable > 0?
+ // ex45582-22397.xls has one extra byte after the last record
+ // Excel reads that file OK
return INVALID_SID_VALUE;
}
int result = _bhi.readRecordSID();
@@ -305,14 +307,8 @@ public final class RecordInputStream implements LittleEndianInput {
@Override
public double readDouble() {
- long valueLongBits = readLong();
- /*if (Double.isNaN(result)) {
- // YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}.
- // HSSF prior to version 3.7 had a bug: it could write Double.NaN but could not read such a file back.
- // This behavior was fixed in POI-3.7.
- //throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN)
- }*/
- return Double.longBitsToDouble(valueLongBits);
+ // YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}.
+ return Double.longBitsToDouble(readLong());
}
public void readPlain(byte[] buf, int off, int len) {
@@ -329,7 +325,7 @@ public final class RecordInputStream implements LittleEndianInput {
readFully(buf, off, len, false);
}
- protected void readFully(byte[] buf, int off, int len, boolean isPlain) {
+ private void readFully(byte[] buf, int off, int len, boolean isPlain) {
int origLen = len;
if (buf == null) {
throw new NullPointerException();
diff --git a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
index a92c8dc590..c37ce006a0 100644
--- a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
@@ -40,6 +40,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
+import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -48,11 +49,24 @@ import org.apache.poi.util.POILogger;
* For now this class renders only images supported by the javax.imageio.ImageIO framework.
**/
public class BitmapImageRenderer implements ImageRenderer {
- private final static POILogger LOG = POILogFactory.getLogger(ImageRenderer.class);
+ private final static POILogger LOG = POILogFactory.getLogger(BitmapImageRenderer.class);
protected BufferedImage img;
@Override
+ public boolean canRender(String contentType) {
+ PictureType[] pts = {
+ PictureType.JPEG, PictureType.PNG, PictureType.BMP, PictureType.GIF
+ };
+ for (PictureType pt : pts) {
+ if (pt.contentType.equalsIgnoreCase(contentType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
public void loadImage(InputStream data, String contentType) throws IOException {
img = readImage(data, contentType);
}
diff --git a/src/java/org/apache/poi/sl/draw/DrawFactory.java b/src/java/org/apache/poi/sl/draw/DrawFactory.java
index 98c41ed993..99c9942b67 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFactory.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFactory.java
@@ -22,8 +22,6 @@ import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
-import java.util.HashMap;
-import java.util.Map;
import org.apache.poi.sl.usermodel.Background;
import org.apache.poi.sl.usermodel.ConnectorShape;
@@ -40,18 +38,18 @@ import org.apache.poi.sl.usermodel.TableShape;
import org.apache.poi.sl.usermodel.TextBox;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextShape;
-import org.apache.poi.util.JvmBugs;
public class DrawFactory {
- protected static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<>();
+ private static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<>();
/**
* Set a custom draw factory for the current thread.
* This is a fallback, for operations where usercode can't set a graphics context.
* Preferably use the rendering hint {@link Drawable#DRAW_FACTORY} to set the factory.
*
- * @param factory
+ * @param factory the custom factory
*/
+ @SuppressWarnings("unused")
public static void setDefaultFactory(DrawFactory factory) {
defaultFactory.set(factory);
}
@@ -170,6 +168,7 @@ public class DrawFactory {
return new DrawBackground(shape);
}
+ @SuppressWarnings("WeakerAccess")
public DrawTextFragment getTextFragment(TextLayout layout, AttributedString str) {
return new DrawTextFragment(layout, str);
}
@@ -214,35 +213,6 @@ public class DrawFactory {
/**
- * Replace font families for Windows JVM 6, which contains a font rendering error.
- * This is likely to be removed, when POI upgrades to JDK 7
- *
- * @param graphics the graphics context which will contain the font mapping
- */
- public void fixFonts(Graphics2D graphics) {
- if (!JvmBugs.hasLineBreakMeasurerBug()) return;
- @SuppressWarnings("unchecked")
- Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
- if (fontMap == null) {
- fontMap = new HashMap<>();
- graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
- }
-
- String fonts[][] = {
- { "Calibri", "Lucida Sans" },
- { "Cambria", "Lucida Bright" },
- { "Times New Roman", "Lucida Bright" },
- { "serif", "Lucida Bright" }
- };
-
- for (String f[] : fonts) {
- if (!fontMap.containsKey(f[0])) {
- fontMap.put(f[0], f[1]);
- }
- }
- }
-
- /**
* Return a FontManager, either registered beforehand or a default implementation
*
* @param graphics the graphics context holding potentially a font manager
diff --git a/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java b/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java
index c439fc926f..a428943699 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.java
@@ -22,6 +22,8 @@ package org.apache.poi.sl.draw;
import java.awt.Font;
import java.awt.Graphics2D;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.sl.draw.Drawable.DrawableHint;
@@ -33,6 +35,13 @@ import org.apache.poi.sl.draw.Drawable.DrawableHint;
*/
public class DrawFontManagerDefault implements DrawFontManager {
+ protected final Set<String> knownSymbolFonts = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+
+ public DrawFontManagerDefault() {
+ knownSymbolFonts.add("Wingdings");
+ knownSymbolFonts.add("Symbol");
+ }
+
@Override
public FontInfo getMappedFont(Graphics2D graphics, FontInfo fontInfo) {
return getFontWithFallback(graphics, Drawable.FONT_MAP, fontInfo);
@@ -49,25 +58,35 @@ public class DrawFontManagerDefault implements DrawFontManager {
public String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text) {
// TODO: find a real charset mapping solution instead of hard coding for Wingdings
- String attStr = text;
- if (fontInfo != null && "Wingdings".equalsIgnoreCase(fontInfo.getTypeface())) {
- // wingdings doesn't contain high-surrogates, so chars are ok
- boolean changed = false;
- char chrs[] = attStr.toCharArray();
- for (int i=0; i<chrs.length; i++) {
- // only change valid chars
- if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
- (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
- chrs[i] |= 0xf000;
- changed = true;
- }
- }
+ return (fontInfo != null && knownSymbolFonts.contains(fontInfo.getTypeface()))
+ ? mapSymbolChars(text)
+ : text;
+ }
- if (changed) {
- attStr = new String(chrs);
+ /**
+ * Symbol fonts like "Wingdings" or "Symbol" have glyphs mapped to a Unicode private use range via the Java font loader,
+ * although a system font viewer might show you the glyphs in the ASCII range.
+ * This helper function maps the chars of the text string to the corresponding private use range chars.
+ *
+ * @param text the input string, typically consists of ASCII chars
+ * @return the mapped string, typically consists of chars in the range of 0xf000 to 0xf0ff
+ *
+ * @since POI 4.0.0
+ */
+ public static String mapSymbolChars(String text) {
+ // wingdings doesn't contain high-surrogates, so chars are ok
+ boolean changed = false;
+ char chrs[] = text.toCharArray();
+ for (int i=0; i<chrs.length; i++) {
+ // only change valid chars
+ if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
+ (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
+ chrs[i] |= 0xf000;
+ changed = true;
}
}
- return attStr;
+
+ return changed ? new String(chrs) : text;
}
@Override
diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
index fcca6d07a7..3ccbc9f219 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
@@ -22,19 +22,22 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
+import java.util.ServiceLoader;
import org.apache.poi.sl.usermodel.PictureData;
-import org.apache.poi.sl.usermodel.PictureData.PictureType;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
import org.apache.poi.sl.usermodel.PictureShape;
import org.apache.poi.sl.usermodel.RectAlign;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
public class DrawPictureShape extends DrawSimpleShape {
private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
- private static final String WMF_IMAGE_RENDERER = "org.apache.poi.hwmf.draw.HwmfSLImageRenderer";
-
+ private static final String[] KNOWN_RENDERER = {
+ "org.apache.poi.hwmf.draw.HwmfImageRenderer",
+ "org.apache.poi.hemf.draw.HemfImageRenderer"
+ };
+
public DrawPictureShape(PictureShape<?,?> shape) {
super(shape);
}
@@ -59,29 +62,47 @@ public class DrawPictureShape extends DrawSimpleShape {
/**
* Returns an ImageRenderer for the PictureData
*
- * @param graphics
+ * @param graphics the graphics context
* @return the image renderer
*/
+ @SuppressWarnings({"WeakerAccess", "unchecked"})
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
- ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
- if (renderer != null) {
+ final ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
+ if (renderer != null && renderer.canRender(contentType)) {
return renderer;
}
-
- if (PictureType.WMF.contentType.equals(contentType)) {
+
+ // first try with our default image renderer
+ final BitmapImageRenderer bir = new BitmapImageRenderer();
+ if (bir.canRender(contentType)) {
+ return bir;
+ }
+
+ // then iterate through the scratchpad renderers
+ //
+ // this could be nicely implemented via a j.u.ServiceLoader, but OSGi makes things complicated ...
+ // https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
+ // ... therefore falling back to classloading attempts
+ ClassLoader cl = ImageRenderer.class.getClassLoader();
+ for (String kr : KNOWN_RENDERER) {
+ final ImageRenderer ir;
try {
- @SuppressWarnings("unchecked")
- Class<? extends ImageRenderer> irc = (Class<? extends ImageRenderer>)
- DrawPictureShape.class.getClassLoader().loadClass(WMF_IMAGE_RENDERER);
- return irc.newInstance();
- } catch (Exception e) {
- // WMF image renderer is not on the classpath, continuing with BitmapRenderer
- // although this doesn't make much sense ...
- LOG.log(POILogger.ERROR, "WMF image renderer is not on the classpath - include poi-scratchpad jar!", e);
+ ir = ((Class<? extends ImageRenderer>)cl.loadClass(kr)).newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ // scratchpad was not on the path, ignore and continue
+ LOG.log(POILogger.INFO, "Known image renderer '"+kr+" not found/loaded - include poi-scratchpad jar!", e);
+ continue;
+ }
+ if (ir.canRender(contentType)) {
+ return ir;
}
}
-
- return new BitmapImageRenderer();
+
+ LOG.log(POILogger.WARN, "No suiteable image renderer found for content-type '"+
+ contentType+"' - include poi-scratchpad jar!");
+
+ // falling back to BitmapImageRenderer, at least it gracefully handles invalid images
+ return bir;
}
@Override
diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
index 938d46230a..1ab4af3a1e 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
@@ -254,7 +254,6 @@ public class DrawTextParagraph implements Drawable {
lines.clear();
DrawFactory fact = DrawFactory.getInstance(graphics);
- fact.fixFonts(graphics);
StringBuilder text = new StringBuilder();
AttributedString at = getAttributedString(graphics, text);
boolean emptyParagraph = text.toString().trim().isEmpty();
@@ -635,13 +634,6 @@ public class DrawTextParagraph implements Drawable {
* <li>determine the font group - a text run can have different font groups. Depending on the chars,
* the correct font group needs to be used
*
- * @param graphics
- * @param dfm
- * @param attList
- * @param beginIndex
- * @param run
- * @param runText
- *
* @see <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
*/
private void processGlyphs(Graphics2D graphics, DrawFontManager dfm, List<AttributedStringData> attList, final int beginIndex, TextRun run, String runText) {
diff --git a/src/java/org/apache/poi/sl/draw/DrawTextShape.java b/src/java/org/apache/poi/sl/draw/DrawTextShape.java
index 413ab218c9..c4dd65bb75 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTextShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTextShape.java
@@ -40,8 +40,6 @@ public class DrawTextShape extends DrawSimpleShape {
@Override
public void drawContent(Graphics2D graphics) {
- DrawFactory.getInstance(graphics).fixFonts(graphics);
-
TextShape<?,?> s = getShape();
Rectangle2D anchor = DrawShape.getAnchor(graphics, s);
@@ -219,10 +217,9 @@ public class DrawTextShape extends DrawSimpleShape {
graphics.addRenderingHints(oldGraphics.getRenderingHints());
graphics.setTransform(oldGraphics.getTransform());
}
- DrawFactory.getInstance(graphics).fixFonts(graphics);
return drawParagraphs(graphics, 0, 0);
}
-
+
@Override
protected TextShape<?,? extends TextParagraph<?,?,? extends TextRun>> getShape() {
return (TextShape<?,? extends TextParagraph<?,?,? extends TextRun>>)shape;
diff --git a/src/java/org/apache/poi/sl/draw/ImageRenderer.java b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
index 7ecc96a967..b2355b3012 100644
--- a/src/java/org/apache/poi/sl/draw/ImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
@@ -76,6 +76,13 @@ import java.io.InputStream;
*/
public interface ImageRenderer {
/**
+ * Determines if this image renderer implementation supports the given contentType
+ * @param contentType the image content type
+ * @return if the content type is supported
+ */
+ boolean canRender(String contentType);
+
+ /**
* Load and buffer the image
*
* @param data the raw image stream
diff --git a/src/java/org/apache/poi/util/Dimension2DDouble.java b/src/java/org/apache/poi/util/Dimension2DDouble.java
new file mode 100644
index 0000000000..d08906433c
--- /dev/null
+++ b/src/java/org/apache/poi/util/Dimension2DDouble.java
@@ -0,0 +1,73 @@
+/* ====================================================================
+ 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.
+==================================================================== */
+
+package org.apache.poi.util;
+
+import java.awt.geom.Dimension2D;
+
+public class Dimension2DDouble extends Dimension2D {
+
+ double width;
+ double height;
+
+ public Dimension2DDouble() {
+ width = 0d;
+ height = 0d;
+ }
+
+ public Dimension2DDouble(double width, double height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public double getWidth() {
+ return width;
+ }
+
+ @Override
+ public double getHeight() {
+ return height;
+ }
+
+ @Override
+ public void setSize(double width, double height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Dimension2DDouble) {
+ Dimension2DDouble other = (Dimension2DDouble) obj;
+ return width == other.width && height == other.height;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ double sum = width + height;
+ return (int) Math.ceil(sum * (sum + 1) / 2 + width);
+ }
+
+ @Override
+ public String toString() {
+ return "Dimension2DDouble[" + width + ", " + height + "]";
+ }
+}
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
index 009510ffc6..3dd4cb0d57 100644
--- a/src/java/org/apache/poi/util/IOUtils.java
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -50,6 +50,7 @@ public final class IOUtils {
* @param maxOverride The number of bytes that should be possible to be allocated in one step.
* @since 4.0.0
*/
+ @SuppressWarnings("unused")
public static void setByteArrayMaxOverride(int maxOverride) {
BYTE_ARRAY_MAX_OVERRIDE = maxOverride;
}
@@ -395,13 +396,35 @@ public final class IOUtils {
* @throws IOException If copying the data fails.
*/
public static long copy(InputStream inp, OutputStream out) throws IOException {
+ return copy(inp, out, -1);
+ }
+
+ /**
+ * Copies all the data from the given InputStream to the OutputStream. It
+ * leaves both streams open, so you will still need to close them once done.
+ *
+ * @param inp The {@link InputStream} which provides the data
+ * @param out The {@link OutputStream} to write the data to
+ * @param limit limit the copied bytes - use {@code -1} for no limit
+ * @return the amount of bytes copied
+ *
+ * @throws IOException If copying the data fails.
+ */
+ public static long copy(InputStream inp, OutputStream out, long limit) throws IOException {
final byte[] buff = new byte[4096];
long totalCount = 0;
- for (int count; (count = inp.read(buff)) != -1; totalCount += count) {
- if (count > 0) {
- out.write(buff, 0, count);
+ int readBytes = -1;
+ do {
+ int todoBytes = (int)((limit < 0) ? buff.length : Math.min(limit-totalCount, buff.length));
+ if (todoBytes > 0) {
+ readBytes = inp.read(buff, 0, todoBytes);
+ if (readBytes > 0) {
+ out.write(buff, 0, readBytes);
+ totalCount += readBytes;
+ }
}
- }
+ } while (readBytes >= 0 && (limit == -1 || totalCount < limit));
+
return totalCount;
}
diff --git a/src/java/org/apache/poi/util/LittleEndianInputStream.java b/src/java/org/apache/poi/util/LittleEndianInputStream.java
index 886720f3f1..6199c4dd12 100644
--- a/src/java/org/apache/poi/util/LittleEndianInputStream.java
+++ b/src/java/org/apache/poi/util/LittleEndianInputStream.java
@@ -17,6 +17,7 @@
package org.apache.poi.util;
+import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -29,12 +30,16 @@ import java.io.InputStream;
*/
public class LittleEndianInputStream extends FilterInputStream implements LittleEndianInput {
+ private static final int BUFFERED_SIZE = 8096;
+
private static final int EOF = -1;
+ private int readIndex = 0;
+ private int markIndex = -1;
public LittleEndianInputStream(InputStream is) {
- super(is);
+ super(is.markSupported() ? is : new BufferedInputStream(is, BUFFERED_SIZE));
}
-
+
@Override
@SuppressForbidden("just delegating")
public int available() {
@@ -60,7 +65,18 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
}
return LittleEndian.getUByte(buf);
}
-
+
+ /**
+ * get a float value, reads it in little endian format
+ * then converts the resulting revolting IEEE 754 (curse them) floating
+ * point number to a happy java float
+ *
+ * @return the float (32-bit) value
+ */
+ public float readFloat() {
+ return Float.intBitsToFloat( readInt() );
+ }
+
@Override
public double readDouble() {
return Double.longBitsToDouble(readLong());
@@ -137,14 +153,42 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
}
}
- //Makes repeated calls to super.read() until length is read or EOF is reached
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int readBytes = super.read(b, off, len);
+ readIndex += readBytes;
+ return readBytes;
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ super.mark(readlimit);
+ markIndex = readIndex;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ super.reset();
+ if (markIndex > -1) {
+ readIndex = markIndex;
+ markIndex = -1;
+ }
+ }
+
+ public int getReadIndex() {
+ return readIndex;
+ }
+
+
+
+ //Makes repeated calls to super.read() until length is read or EOF is reached
private int _read(byte[] buffer, int offset, int length) throws IOException {
//lifted directly from org.apache.commons.io.IOUtils 2.4
int remaining = length;
while (remaining > 0) {
int location = length - remaining;
int count = read(buffer, offset + location, remaining);
- if (EOF == count) { // EOF
+ if (EOF == count) {
break;
}
remaining -= count;
@@ -157,4 +201,9 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
public void readPlain(byte[] buf, int off, int len) {
readFully(buf, off, len);
}
+
+
+ public void skipFully(int len) throws IOException {
+ IOUtils.skipFully(this, len);
+ }
}