You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2015/06/06 18:39:48 UTC
svn commit: r1683929 - in
/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox: cos/COSDocument.java
cos/COSStream.java io/ScratchFile.java io/ScratchFileBuffer.java
Author: lehmi
Date: Sat Jun 6 16:39:48 2015
New Revision: 1683929
URL: http://svn.apache.org/r1683929
Log:
PDFBOX-2301: use one scratch file instead of many as proposed by Jesse Long
Added:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java (with props)
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java (with props)
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java?rev=1683929&r1=1683928&r2=1683929&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java Sat Jun 6 16:39:48 2015
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.io.ScratchFile;
import org.apache.pdfbox.pdfparser.PDFObjectStreamParser;
/**
@@ -74,10 +75,8 @@ public class COSDocument extends COSBase
private boolean closed = false;
private boolean isXRefStream;
-
- private final File scratchDirectory;
-
- private final boolean useScratchFile;
+
+ private ScratchFile scratchFile;
/**
* Constructor.
@@ -102,8 +101,17 @@ public class COSDocument extends COSBase
*/
public COSDocument(File scratchDir, boolean useScratchFiles)
{
- scratchDirectory = scratchDir;
- useScratchFile = useScratchFiles;
+ if (useScratchFiles)
+ {
+ try
+ {
+ scratchFile = new ScratchFile(scratchDir);
+ }
+ catch (IOException e)
+ {
+ LOG.error("Can't create temp file, using memory buffer instead", e);
+ }
+ }
}
/**
@@ -121,7 +129,7 @@ public class COSDocument extends COSBase
*/
public COSStream createCOSStream()
{
- return new COSStream( useScratchFile, scratchDirectory);
+ return new COSStream(scratchFile);
}
/**
@@ -133,7 +141,7 @@ public class COSDocument extends COSBase
*/
public COSStream createCOSStream(COSDictionary dictionary)
{
- return new COSStream( dictionary, useScratchFile, scratchDirectory );
+ return new COSStream( dictionary, scratchFile );
}
/**
@@ -424,6 +432,11 @@ public class COSDocument extends COSBase
}
}
}
+
+ if (scratchFile != null)
+ {
+ scratchFile.close();
+ }
closed = true;
}
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java?rev=1683929&r1=1683928&r2=1683929&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java Sat Jun 6 16:39:48 2015
@@ -21,22 +21,19 @@ import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.filter.DecodeResult;
import org.apache.pdfbox.filter.Filter;
import org.apache.pdfbox.filter.FilterFactory;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.io.RandomAccess;
import org.apache.pdfbox.io.RandomAccessBuffer;
-import org.apache.pdfbox.io.RandomAccessFile;
import org.apache.pdfbox.io.RandomAccessFileInputStream;
import org.apache.pdfbox.io.RandomAccessFileOutputStream;
+import org.apache.pdfbox.io.ScratchFile;
/**
* This class represents a stream object in a PDF document.
@@ -45,11 +42,6 @@ import org.apache.pdfbox.io.RandomAccess
*/
public class COSStream extends COSDictionary implements Closeable
{
- /**
- * Log instance.
- */
- private static final Log LOG = LogFactory.getLog(COSStream.class);
-
private static final int BUFFER_SIZE=16384;
/**
@@ -70,11 +62,7 @@ public class COSStream extends COSDictio
private RandomAccessFileOutputStream unFilteredStream;
private DecodeResult decodeResult;
- private File scratchFileFiltered;
- private File scratchFileUnfiltered;
-
- private final boolean scratchFiles;
- private final File scratchFileDirectory;
+ private final ScratchFile scratchFile;
/**
* Constructor. Creates a new stream with an empty dictionary.
@@ -82,7 +70,7 @@ public class COSStream extends COSDictio
*/
public COSStream( )
{
- this(false, null);
+ this((ScratchFile)null);
}
/**
@@ -93,43 +81,39 @@ public class COSStream extends COSDictio
*/
public COSStream( COSDictionary dictionary )
{
- this(dictionary, false, null);
+ this(dictionary, null);
}
/**
* Constructor. Creates a new stream with an empty dictionary.
*
- * @param useScratchFiles enables the usage of a scratch file if set to true
- * @param scratchDirectory directory to be used to create the scratch file. If null java.io.temp is used instead.
+ * @param scratchFile scratch file to use.
*
*/
- public COSStream( boolean useScratchFiles, File scratchDirectory )
+ public COSStream( ScratchFile scratchFile )
{
super();
- scratchFiles= useScratchFiles;
- scratchFileDirectory = scratchDirectory;
+ this.scratchFile = scratchFile;
}
/**
* Constructor.
*
* @param dictionary The dictionary that is associated with this stream.
- * @param useScratchFiles enables the usage of a scratch file if set to true
- * @param scratchDirectory directory to be used to create the scratch file. If null java.io.temp is used instead.
+ * @param scratchFile The scratch file to use.
*
*/
- public COSStream( COSDictionary dictionary, boolean useScratchFiles, File scratchDirectory )
+ public COSStream( COSDictionary dictionary, ScratchFile scratchFile )
{
super( dictionary );
- scratchFiles= useScratchFiles;
- scratchFileDirectory = scratchDirectory;
+ this.scratchFile = scratchFile;
}
- private RandomAccess createBuffer(boolean filtered) throws IOException
+ private RandomAccess createBuffer() throws IOException
{
- if (scratchFiles)
+ if (scratchFile != null)
{
- return createScratchFile(filtered);
+ return scratchFile.createBuffer();
}
else
{
@@ -138,33 +122,6 @@ public class COSStream extends COSDictio
}
/**
- * Create a scratch file to be used as buffer.
- */
- private RandomAccess createScratchFile(boolean filtered)
- {
- try
- {
- if (filtered)
- {
- deleteFile(scratchFileFiltered);
- scratchFileFiltered = File.createTempFile("PDFBox_streamf_", null, scratchFileDirectory);
- return new RandomAccessFile(scratchFileFiltered, "rw");
- }
- else
- {
- deleteFile(scratchFileUnfiltered);
- scratchFileUnfiltered = File.createTempFile("PDFBox_streamu_", null, scratchFileDirectory);
- return new RandomAccessFile(scratchFileUnfiltered, "rw");
- }
- }
- catch (IOException exception)
- {
- LOG.error("Can't create temp file, using memory buffer instead", exception);
- return new RandomAccessBuffer();
- }
- }
-
- /**
* This will get the stream with all of the filters applied.
*
* @return the bytes of the physical (encoded) stream
@@ -374,7 +331,7 @@ public class COSStream extends COSDictio
{
if (result == null)
{
- result = createBuffer(false);
+ result = createBuffer();
}
}
else
@@ -396,7 +353,7 @@ public class COSStream extends COSDictio
IOUtils.closeQuietly(unFilteredStream);
if (destBuffer == null)
{
- result = createBuffer(false);
+ result = createBuffer();
}
else
{
@@ -468,7 +425,7 @@ public class COSStream extends COSDictio
IOUtils.closeQuietly(filteredStream);
if (destBuffer == null)
{
- result = createBuffer(true);
+ result = createBuffer();
}
else
{
@@ -599,7 +556,7 @@ public class COSStream extends COSDictio
{
if (filteredBuffer == null)
{
- filteredBuffer = createBuffer(true);
+ filteredBuffer = createBuffer();
}
else if (clear)
{
@@ -617,7 +574,7 @@ public class COSStream extends COSDictio
{
if (unfilteredBuffer == null)
{
- unfilteredBuffer = createBuffer(false);
+ unfilteredBuffer = createBuffer();
}
else if (clear)
{
@@ -647,18 +604,5 @@ public class COSStream extends COSDictio
{
filteredBuffer.close();
}
- deleteFile(scratchFileFiltered);
- deleteFile(scratchFileUnfiltered);
- }
-
- private void deleteFile(File file) throws IOException
- {
- if (file != null && file.exists())
- {
- if (!file.delete())
- {
- throw new IOException("Can't delete the temporary scratch file "+file.getAbsolutePath());
- }
- }
}
}
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java?rev=1683929&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java Sat Jun 6 16:39:48 2015
@@ -0,0 +1,130 @@
+/*
+ * 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.pdfbox.io;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.cos.COSStream;
+
+/**
+ * A temporary file which can hold multiple buffers of temporary data. A new temporary file is created for each new
+ * {@link ScratchFile} instance, and is deleted when the {@link ScratchFile} is closed.
+ * <p>
+ * Multiple buffers can be creating by calling the {@link #createBuffer()} method.
+ * <p>
+ * The file is split into pages, each page containing a pointer to the previous and next pages. This allows for
+ * multiple, separate streams in the same file.
+ *
+ * @author Jesse Long
+ */
+public class ScratchFile implements Closeable
+{
+ private static final Log LOG = LogFactory.getLog(COSStream.class);
+ private File file;
+ private java.io.RandomAccessFile raf;
+
+ /**
+ * Creates a new scratch file. If a {code scratchFileDirectory} is supplied, then the scratch file is created in
+ * that directory.
+ *
+ * @param scratchFileDirectory The directory in which to create the scratch file, or {code null} if the scratch
+ * should be created in the default temporary directory.
+ * @throws IOException If there was a problem creating a temporary file.
+ */
+ public ScratchFile(File scratchFileDirectory) throws IOException
+ {
+ file = File.createTempFile("PDFBox", ".tmp", scratchFileDirectory);
+ try
+ {
+ raf = new java.io.RandomAccessFile(file, "rw");
+ }
+ catch (IOException e)
+ {
+ if (!file.delete())
+ {
+ LOG.warn("Error deleting scratch file: " + file.getAbsolutePath());
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Returns the underlying {@link java.io.RandomAccessFile}.
+ *
+ * @return The underlying {@link java.io.RandomAccessFile}.
+ */
+ java.io.RandomAccessFile getRandomAccessFile()
+ {
+ return raf;
+ }
+
+ /**
+ * Checks if this scratch file has already been closed. If the file has been closed, an {@link IOException} is
+ * thrown.
+ *
+ * @throws IOException If the file has already been closed.
+ */
+ void checkClosed() throws IOException
+ {
+ if (raf == null)
+ {
+ throw new IOException("Scratch file already closed");
+ }
+ }
+
+ /**
+ * Creates a new buffer in the scratch file.
+ *
+ * @return A new buffer.
+ * @throws IOException If an error occurred.
+ */
+ public RandomAccess createBuffer() throws IOException
+ {
+ return new ScratchFileBuffer(this);
+ }
+
+ /**
+ * Closes and deletes the temporary file. No further interaction with the scratch file or associated buffers can
+ * happen after this method is called.
+ *
+ * @throws IOException If there was a problem closing or deleting the temporary file.
+ */
+ @Override
+ public void close() throws IOException
+ {
+ if (raf != null)
+ {
+ raf.close();
+ raf = null;
+ }
+
+ if (file != null)
+ {
+ if (file.delete())
+ {
+ file = null;
+ }
+ else
+ {
+ throw new IOException("Error deleting scratch file: " + file.getAbsolutePath());
+ }
+ }
+ }
+}
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFile.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java?rev=1683929&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java Sat Jun 6 16:39:48 2015
@@ -0,0 +1,515 @@
+/*
+ * 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.pdfbox.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * A {@link RandomAccess} implemented as a doubly linked list over multiple pages in a {@link java.io.RandomAccessFile}.
+ * <p>
+ * Each page is {@link #PAGE_SIZE} bytes, with the first 8 bytes being a pointer to page index (
+ * {@code pageOffset / PAGE_SIZE}) of the previous page in the buffer, and the last 8 bytes being a pointer to the page
+ * index of the next page in the buffer.
+ *
+ * @author Jesse Long
+ */
+class ScratchFileBuffer implements RandomAccess
+{
+ /**
+ * The size of each page.
+ */
+ private static final int PAGE_SIZE = 4096;
+ /**
+ * The underlying scratch file.
+ */
+ private ScratchFile scratchFile;
+ /**
+ * The random access file of the scratch file.
+ */
+ private RandomAccessFile raFile;
+ /**
+ * The first page in this buffer.
+ */
+ private final long firstPage;
+ /**
+ * The number of bytes of content in this buffer.
+ */
+ private long length = 0;
+ /**
+ * The index of the page in which the current position of this buffer is in.
+ */
+ private long currentPage;
+ /**
+ * The current position of the buffer as an offset in the current page.
+ */
+ private int positionInPage;
+ /**
+ * The current position in the space of the whole buffer.
+ */
+ private long positionInBuffer;
+
+ /**
+ * Creates a new buffer in the provided {@link ScratchFile}.
+ *
+ * @param scratchFile The {@link ScratchFile} in which to create the new buffer.
+ * @throws IOException If there was an error writing to the file.
+ */
+ ScratchFileBuffer(ScratchFile scratchFile) throws IOException
+ {
+ scratchFile.checkClosed();
+
+ this.scratchFile = scratchFile;
+
+ raFile = scratchFile.getRandomAccessFile();
+
+ /*
+ * We must allocate a new first page for each new buffer, in case multiple buffers are created at the same time,
+ * and use the same space.
+ */
+ firstPage = createNewPage();
+
+ /*
+ * Mark the first page back pointer to -1 to indicate start of buffer.
+ */
+ raFile.seek(firstPage * PAGE_SIZE);
+ raFile.writeLong(-1L);
+
+ /*
+ * Reset variables to beginning of empty buffer.
+ */
+ clear();
+ }
+
+ /**
+ * Checks if this buffer, or the underlying {@link ScratchFile} have been closed, throwing {@link IOException} if
+ * so.
+ *
+ * @throws IOException If either this buffer, or the underlying {@link ScratchFile} have been closed.
+ */
+ private void checkClosed() throws IOException
+ {
+ if (scratchFile == null)
+ {
+ throw new IOException("Scratch file buffer already closed");
+ }
+ scratchFile.checkClosed();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long length() throws IOException
+ {
+ checkClosed();
+ return length;
+ }
+
+ /**
+ * Allocates a new page, and links the current and the new page.
+ *
+ * @throws IOException If there was an error writing to the file.
+ */
+ private void growToNewPage() throws IOException
+ {
+ long newPage = createNewPage();
+
+ /*
+ * We should only grow to a new page when previous pages are full. If not, links won't work.
+ */
+ if (positionInPage != PAGE_SIZE - 8)
+ {
+ throw new IOException("Corruption detected in scratch file");
+ }
+ seekToCurrentPositionInFile();
+ raFile.writeLong(newPage);
+
+ long previousPage = currentPage;
+ currentPage = newPage;
+ positionInPage = 0;
+ /*
+ * write back link to previous page.
+ */
+ seekToCurrentPositionInFile();
+ raFile.writeLong(previousPage);
+ positionInPage = 8;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void write(int b) throws IOException
+ {
+ checkClosed();
+ seekToCurrentPositionInFile();
+ if (positionInPage == PAGE_SIZE - 8)
+ {
+ growToNewPage();
+ }
+
+ raFile.write(b);
+
+ positionInPage++;
+ positionInBuffer++;
+ if (positionInBuffer > length)
+ {
+ length = positionInBuffer;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ checkClosed();
+
+ seekToCurrentPositionInFile();
+
+ while (len > 0)
+ {
+ if (positionInPage == PAGE_SIZE - 8)
+ {
+ growToNewPage();
+ }
+
+ int availableSpaceInCurrentPage = (PAGE_SIZE - 8) - positionInPage;
+
+ int bytesToWrite = Math.min(len, availableSpaceInCurrentPage);
+
+ raFile.write(b, off, bytesToWrite);
+
+ off += bytesToWrite;
+ len -= bytesToWrite;
+ positionInPage += bytesToWrite;
+ positionInBuffer += bytesToWrite;
+ if (positionInBuffer > length)
+ {
+ length = positionInBuffer;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void clear() throws IOException
+ {
+ checkClosed();
+ length = 0;
+ currentPage = firstPage;
+ positionInBuffer = 0;
+ positionInPage = 8;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getPosition() throws IOException
+ {
+ checkClosed();
+ return positionInBuffer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void seek(long seekToPosition) throws IOException
+ {
+ checkClosed();
+
+ /*
+ * Can't seek past end of file. If you want to change implementation, seek to end of file, write zero bytes for
+ * remaining seek distance.
+ */
+ if (seekToPosition > length)
+ {
+ throw new EOFException();
+ }
+
+ if (seekToPosition < positionInBuffer)
+ {
+ if (currentPage != firstPage && seekToPosition < (positionInBuffer / 2))
+ {
+ /*
+ * If we are seeking backwards, and the seek to position is closer to the beginning of the buffer than
+ * our current position, just go to the start of the buffer and seek forward from there. Recurse exactly
+ * once.
+ */
+ currentPage = firstPage;
+ positionInPage = 8;
+ positionInBuffer = 0;
+ seek(seekToPosition);
+ }
+ else
+ {
+ while (positionInBuffer - seekToPosition > positionInPage - 8)
+ {
+ raFile.seek(currentPage * PAGE_SIZE);
+ long previousPage = raFile.readLong();
+ currentPage = previousPage;
+ positionInBuffer -= (positionInPage - 8);
+ positionInPage = PAGE_SIZE - 8;
+ }
+
+ positionInPage -= (positionInBuffer - seekToPosition);
+ positionInBuffer = seekToPosition;
+ }
+ }
+ else
+ {
+ while (seekToPosition - positionInBuffer > (PAGE_SIZE - 8) - positionInPage)
+ {
+ // seek to 8 bytes from end of current page, to read next page pointer.
+ raFile.seek(((currentPage + 1) * PAGE_SIZE) - 8);
+ long nextPage = raFile.readLong();
+ positionInBuffer += (PAGE_SIZE - 8) - positionInPage;
+ currentPage = nextPage;
+ positionInPage = 8;
+ }
+
+ positionInPage += seekToPosition - positionInBuffer;
+ positionInBuffer = seekToPosition;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isClosed()
+ {
+ return scratchFile == null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int peek() throws IOException
+ {
+ int result = read();
+ if (result != -1)
+ {
+ rewind(1);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void rewind(int bytes) throws IOException
+ {
+ seek(positionInBuffer - bytes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] readFully(int len) throws IOException
+ {
+ byte[] b = new byte[len];
+
+ int n = 0;
+ do
+ {
+ int count = read(b, n, len - n);
+ if (count < 0)
+ {
+ throw new EOFException();
+ }
+ n += count;
+ } while (n < len);
+
+ return b;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEOF() throws IOException
+ {
+ checkClosed();
+ return positionInBuffer == length;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int available() throws IOException
+ {
+ checkClosed();
+ return (int) Math.min(length - positionInBuffer, Integer.MAX_VALUE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read() throws IOException
+ {
+ checkClosed();
+
+ if (positionInBuffer >= length)
+ {
+ return -1;
+ }
+
+ seekToCurrentPositionInFile();
+
+ if (positionInPage == PAGE_SIZE - 8)
+ {
+ currentPage = raFile.readLong();
+ positionInPage = 8;
+ seekToCurrentPositionInFile();
+ }
+
+ int retv = raFile.read();
+
+ if (retv >= 0)
+ {
+ positionInPage++;
+ positionInBuffer++;
+ }
+
+ return retv;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ checkClosed();
+
+ if (positionInBuffer >= length)
+ {
+ return -1;
+ }
+
+ seekToCurrentPositionInFile();
+
+ if (positionInPage == PAGE_SIZE - 8)
+ {
+ currentPage = raFile.readLong();
+ positionInPage = 8;
+ seekToCurrentPositionInFile();
+ }
+
+ len = (int) Math.min(len, length - positionInBuffer);
+
+ int totalBytesRead = 0;
+
+ while (len > 0)
+ {
+ int availableInThisPage = (PAGE_SIZE - 8) - positionInPage;
+
+ int rdbytes = raFile.read(b, off, Math.min(len, availableInThisPage));
+
+ if (rdbytes < 0)
+ {
+ throw new IOException("EOF reached before end of scratch file stream");
+ }
+
+ if (rdbytes == availableInThisPage)
+ {
+ currentPage = raFile.readLong();
+ positionInPage = 8;
+ seekToCurrentPositionInFile();
+ }
+ else
+ {
+ positionInPage += rdbytes;
+ }
+
+ totalBytesRead += rdbytes;
+ positionInBuffer += rdbytes;
+ off += rdbytes;
+ len -= rdbytes;
+ }
+
+ return totalBytesRead;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException
+ {
+ scratchFile = null;
+ raFile = null;
+ }
+
+ /**
+ * Positions the underlying {@link java.io.RandomAccessFile} to the correct position for use by this buffer.
+ *
+ * @throws IOException If there was a problem seeking in the {@link java.io.RandomAccessFile}.
+ */
+ private void seekToCurrentPositionInFile() throws IOException
+ {
+ long positionInFile = (currentPage * PAGE_SIZE) + positionInPage;
+ if (raFile.getFilePointer() != positionInFile)
+ {
+ raFile.seek(positionInFile);
+ }
+ }
+
+ /**
+ * Allocates a new page in the temporary file by growing the file, returning the page index of the new page.
+ *
+ * @return The index of the new page.
+ * @throws IOException If there was an error growing the file.
+ */
+ private long createNewPage() throws IOException
+ {
+ long fileLen = raFile.length();
+
+ fileLen += PAGE_SIZE;
+
+ if (fileLen % PAGE_SIZE > 0)
+ {
+ fileLen += PAGE_SIZE - (fileLen % PAGE_SIZE);
+ }
+
+ raFile.setLength(fileLen);
+
+ return (fileLen / PAGE_SIZE) - 1;
+ }
+}
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/ScratchFileBuffer.java
------------------------------------------------------------------------------
svn:eol-style = native