| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| /* | ||
| This file is part of the iText (R) project. | ||
| Copyright (c) 1998-2019 iText Group NV | ||
| Authors: iText Software. | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Affero General Public License version 3 | ||
| as published by the Free Software Foundation with the addition of the | ||
| following permission added to Section 15 as permitted in Section 7(a): | ||
| FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY | ||
| ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT | ||
| OF THIRD PARTY RIGHTS | ||
| This program is distributed in the hope that it will be useful, but | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Affero General Public License for more details. | ||
| You should have received a copy of the GNU Affero General Public License | ||
| along with this program; if not, see http://www.gnu.org/licenses or write to | ||
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| Boston, MA, 02110-1301 USA, or download the license from the following URL: | ||
| http://itextpdf.com/terms-of-use/ | ||
| The interactive user interfaces in modified source and object code versions | ||
| of this program must display Appropriate Legal Notices, as required under | ||
| Section 5 of the GNU Affero General Public License. | ||
| In accordance with Section 7(b) of the GNU Affero General Public License, | ||
| a covered work must retain the producer line in every PDF that is created | ||
| or manipulated using iText. | ||
| You can be released from the requirements of the license by purchasing | ||
| a commercial license. Buying such a license is mandatory as soon as you | ||
| develop commercial activities involving the iText software without | ||
| disclosing the source code of your own applications. | ||
| These activities include: offering paid services to customers as an ASP, | ||
| serving PDFs on the fly in a web application, shipping iText with a closed | ||
| source product. | ||
| For more information, please contact iText Software Corp. at this | ||
| address: sales@itextpdf.com | ||
| */ | ||
| package com.itextpdf.kernel.pdf; | ||
|
|
||
| import com.itextpdf.kernel.PdfException; | ||
|
|
||
| import java.io.ByteArrayOutputStream; | ||
| import java.util.Arrays; | ||
|
|
||
| /** | ||
| * This class implements an output stream which can be used for memory limits aware decompression of pdf streams. | ||
| */ | ||
| class MemoryLimitsAwareOutputStream extends ByteArrayOutputStream { | ||
|
|
||
| /** | ||
| * The maximum size of array to allocate. | ||
| * Attempts to allocate larger arrays will result in an exception. | ||
| */ | ||
| private static final int DEFAULT_MAX_STREAM_SIZE = Integer.MAX_VALUE - 8; | ||
|
|
||
| /** | ||
| * The maximum size of array to allocate. | ||
| * Attempts to allocate larger arrays will result in an exception. | ||
| */ | ||
| private int maxStreamSize = DEFAULT_MAX_STREAM_SIZE; | ||
|
|
||
| /** | ||
| * Creates a new byte array output stream. The buffer capacity is | ||
| * initially 32 bytes, though its size increases if necessary. | ||
| */ | ||
| public MemoryLimitsAwareOutputStream() { | ||
| super(); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new byte array output stream, with a buffer capacity of | ||
| * the specified size, in bytes. | ||
| * | ||
| * @param size the initial size. | ||
| * @throws IllegalArgumentException if size is negative. | ||
| */ | ||
| public MemoryLimitsAwareOutputStream(int size) { | ||
| super(size); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the maximum size which can be occupied by this output stream. | ||
| * | ||
| * @return the maximum size which can be occupied by this output stream. | ||
| */ | ||
| public long getMaxStreamSize() { | ||
| return maxStreamSize; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the maximum size which can be occupied by this output stream. | ||
| * | ||
| * @param maxStreamSize the maximum size which can be occupied by this output stream. | ||
| * @return this {@link MemoryLimitsAwareOutputStream} | ||
| */ | ||
| public MemoryLimitsAwareOutputStream setMaxStreamSize(int maxStreamSize) { | ||
| this.maxStreamSize = maxStreamSize; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| @Override | ||
| public synchronized void write(byte[] b, int off, int len) { | ||
| if ((off < 0) || (off > b.length) || (len < 0) || | ||
| ((off + len) - b.length > 0)) { | ||
| throw new IndexOutOfBoundsException(); | ||
| } | ||
|
|
||
| int minCapacity = count + len; | ||
| if (minCapacity < 0) { // overflow | ||
| throw new MemoryLimitsAwareException(PdfException.DuringDecompressionSingleStreamOccupiedMoreThanMaxIntegerValue); | ||
| } | ||
| if (minCapacity > maxStreamSize) { | ||
| throw new MemoryLimitsAwareException(PdfException.DuringDecompressionSingleStreamOccupiedMoreMemoryThanAllowed); | ||
| } | ||
|
|
||
| // calculate new capacity | ||
| int oldCapacity = buf.length; | ||
| int newCapacity = oldCapacity << 1; | ||
| if (newCapacity < 0 || newCapacity - minCapacity < 0) { // overflow | ||
| newCapacity = minCapacity; | ||
| } | ||
|
|
||
| if (newCapacity - maxStreamSize > 0) { | ||
| newCapacity = maxStreamSize; | ||
| buf = Arrays.copyOf(buf, newCapacity); | ||
| } | ||
| super.write(b, off, len); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| This file is part of the iText (R) project. | ||
| Copyright (c) 1998-2019 iText Group NV | ||
| Authors: iText Software. | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Affero General Public License version 3 | ||
| as published by the Free Software Foundation with the addition of the | ||
| following permission added to Section 15 as permitted in Section 7(a): | ||
| FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY | ||
| ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT | ||
| OF THIRD PARTY RIGHTS | ||
| This program is distributed in the hope that it will be useful, but | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Affero General Public License for more details. | ||
| You should have received a copy of the GNU Affero General Public License | ||
| along with this program; if not, see http://www.gnu.org/licenses or write to | ||
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| Boston, MA, 02110-1301 USA, or download the license from the following URL: | ||
| http://itextpdf.com/terms-of-use/ | ||
| The interactive user interfaces in modified source and object code versions | ||
| of this program must display Appropriate Legal Notices, as required under | ||
| Section 5 of the GNU Affero General Public License. | ||
| In accordance with Section 7(b) of the GNU Affero General Public License, | ||
| a covered work must retain the producer line in every PDF that is created | ||
| or manipulated using iText. | ||
| You can be released from the requirements of the license by purchasing | ||
| a commercial license. Buying such a license is mandatory as soon as you | ||
| develop commercial activities involving the iText software without | ||
| disclosing the source code of your own applications. | ||
| These activities include: offering paid services to customers as an ASP, | ||
| serving PDFs on the fly in a web application, shipping iText with a closed | ||
| source product. | ||
| For more information, please contact iText Software Corp. at this | ||
| address: sales@itextpdf.com | ||
| */ | ||
| package com.itextpdf.kernel.pdf.filters; | ||
|
|
||
| import com.itextpdf.kernel.pdf.MemoryLimitsAwareException; | ||
| import com.itextpdf.kernel.pdf.PdfDictionary; | ||
| import com.itextpdf.kernel.pdf.PdfName; | ||
| import com.itextpdf.kernel.pdf.PdfObject; | ||
|
|
||
| import java.io.ByteArrayInputStream; | ||
| import java.io.ByteArrayOutputStream; | ||
| import java.util.zip.InflaterInputStream; | ||
|
|
||
| /** | ||
| * Handles strict FlateDecode filter. | ||
| */ | ||
| public class FlateDecodeStrictFilter extends FlateDecodeFilter { | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| @Override | ||
| public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) { | ||
| ByteArrayOutputStream outputStream = enableMemoryLimitsAwareHandler(streamDictionary); | ||
| byte[] res = flateDecode(b, outputStream); | ||
| b = decodePredictor(res, decodeParams); | ||
| return b; | ||
| } | ||
|
|
||
| /** | ||
| * A helper to flateDecode. | ||
| * | ||
| * @param in the input data | ||
| * @param out the out stream which will be used to write the bytes. | ||
| * @return the decoded data | ||
| */ | ||
| private static byte[] flateDecode(byte[] in, ByteArrayOutputStream out) { | ||
| ByteArrayInputStream stream = new ByteArrayInputStream(in); | ||
| InflaterInputStream zip = new InflaterInputStream(stream); | ||
| byte[] b = new byte[4092]; | ||
| try { | ||
| int n; | ||
| while ((n = zip.read(b)) >= 0) { | ||
| out.write(b, 0, n); | ||
| } | ||
| zip.close(); | ||
| out.close(); | ||
| return out.toByteArray(); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| return null; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| /* | ||
| This file is part of the iText (R) project. | ||
| Copyright (c) 1998-2019 iText Group NV | ||
| Authors: iText Software. | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Affero General Public License version 3 | ||
| as published by the Free Software Foundation with the addition of the | ||
| following permission added to Section 15 as permitted in Section 7(a): | ||
| FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY | ||
| ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT | ||
| OF THIRD PARTY RIGHTS | ||
| This program is distributed in the hope that it will be useful, but | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Affero General Public License for more details. | ||
| You should have received a copy of the GNU Affero General Public License | ||
| along with this program; if not, see http://www.gnu.org/licenses or write to | ||
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| Boston, MA, 02110-1301 USA, or download the license from the following URL: | ||
| http://itextpdf.com/terms-of-use/ | ||
| The interactive user interfaces in modified source and object code versions | ||
| of this program must display Appropriate Legal Notices, as required under | ||
| Section 5 of the GNU Affero General Public License. | ||
| In accordance with Section 7(b) of the GNU Affero General Public License, | ||
| a covered work must retain the producer line in every PDF that is created | ||
| or manipulated using iText. | ||
| You can be released from the requirements of the license by purchasing | ||
| a commercial license. Buying such a license is mandatory as soon as you | ||
| develop commercial activities involving the iText software without | ||
| disclosing the source code of your own applications. | ||
| These activities include: offering paid services to customers as an ASP, | ||
| serving PDFs on the fly in a web application, shipping iText with a closed | ||
| source product. | ||
| For more information, please contact iText Software Corp. at this | ||
| address: sales@itextpdf.com | ||
| */ | ||
| package com.itextpdf.kernel.pdf; | ||
|
|
||
|
|
||
| import com.itextpdf.kernel.PdfException; | ||
| import com.itextpdf.test.annotations.type.UnitTest; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.junit.experimental.categories.Category; | ||
|
|
||
| @Category(UnitTest.class) | ||
| public class MemoryLimitsAwareHandlerTest { | ||
|
|
||
| @Test | ||
| public void defaultMemoryHandler() { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
|
|
||
| Assert.assertEquals(Integer.MAX_VALUE / 100, handler.getMaxSizeOfSingleDecompressedPdfStream()); | ||
| Assert.assertEquals(Integer.MAX_VALUE / 20, handler.getMaxSizeOfDecompressedPdfStreamsSum()); | ||
| } | ||
|
|
||
| @Test | ||
| public void customMemoryHandler() { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(1000000); | ||
|
|
||
| Assert.assertEquals(100000000, handler.getMaxSizeOfSingleDecompressedPdfStream()); | ||
| Assert.assertEquals(500000000, handler.getMaxSizeOfDecompressedPdfStreamsSum()); | ||
| } | ||
|
|
||
| @Test | ||
| public void defaultSingleMemoryHandler() { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
|
|
||
| testSingleStream(handler); | ||
| } | ||
|
|
||
| @Test | ||
| public void defaultMultipleMemoryHandler() { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
|
|
||
| testMultipleStreams(handler); | ||
| } | ||
|
|
||
| @Test | ||
| public void considerBytesTest() { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
|
|
||
| long state1 = handler.getAllMemoryUsedForDecompression(); | ||
|
|
||
| handler.considerBytesOccupiedByDecompressedPdfStream(100); | ||
| long state2 = handler.getAllMemoryUsedForDecompression(); | ||
|
|
||
| Assert.assertEquals(state1, state2); | ||
|
|
||
| handler.beginDecompressedPdfStreamProcessing(); | ||
| handler.considerBytesOccupiedByDecompressedPdfStream(100); | ||
| long state3 = handler.getAllMemoryUsedForDecompression(); | ||
| Assert.assertEquals(state1, state3); | ||
|
|
||
| handler.considerBytesOccupiedByDecompressedPdfStream(80); | ||
| long state4 = handler.getAllMemoryUsedForDecompression(); | ||
| Assert.assertEquals(state1, state4); | ||
|
|
||
| handler.endDecompressedPdfStreamProcessing(); | ||
| long state5 = handler.getAllMemoryUsedForDecompression(); | ||
| Assert.assertEquals(state1 + 100, state5); | ||
| } | ||
|
|
||
| private static void testSingleStream(MemoryLimitsAwareHandler handler) { | ||
| String expectedExceptionMessage = PdfException.DuringDecompressionSingleStreamOccupiedMoreMemoryThanAllowed; | ||
| int expectedFailureIndex = 10; | ||
| String occuredExceptionMessage = null; | ||
|
|
||
| int limit = handler.getMaxSizeOfSingleDecompressedPdfStream(); | ||
|
|
||
| long step = limit / 10; | ||
|
|
||
| int i = 0; | ||
| try { | ||
| handler.beginDecompressedPdfStreamProcessing(); | ||
| for (i = 0; i < 11; i++) { | ||
| handler.considerBytesOccupiedByDecompressedPdfStream(step * (1 + i)); | ||
| } | ||
| handler.endDecompressedPdfStreamProcessing(); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| occuredExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedFailureIndex, i); | ||
| Assert.assertEquals(expectedExceptionMessage, occuredExceptionMessage); | ||
| } | ||
|
|
||
| private static void testMultipleStreams(MemoryLimitsAwareHandler handler) { | ||
| String expectedExceptionMessage = PdfException.DuringDecompressionMultipleStreamsInSumOccupiedMoreMemoryThanAllowed; | ||
| int expectedFailureIndex = 10; | ||
| String occuredExceptionMessage = null; | ||
|
|
||
| int i = 0; | ||
| try { | ||
| long limit = handler.getMaxSizeOfDecompressedPdfStreamsSum(); | ||
| long step = limit / 10; | ||
|
|
||
| for (i = 0; i < 11; i++) { | ||
| handler.beginDecompressedPdfStreamProcessing(); | ||
| handler.considerBytesOccupiedByDecompressedPdfStream(step); | ||
| handler.endDecompressedPdfStreamProcessing(); | ||
| } | ||
| } catch (MemoryLimitsAwareException e) { | ||
| occuredExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedFailureIndex, i); | ||
| Assert.assertEquals(expectedExceptionMessage, occuredExceptionMessage); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| This file is part of the iText (R) project. | ||
| Copyright (c) 1998-2019 iText Group NV | ||
| Authors: iText Software. | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Affero General Public License version 3 | ||
| as published by the Free Software Foundation with the addition of the | ||
| following permission added to Section 15 as permitted in Section 7(a): | ||
| FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY | ||
| ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT | ||
| OF THIRD PARTY RIGHTS | ||
| This program is distributed in the hope that it will be useful, but | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Affero General Public License for more details. | ||
| You should have received a copy of the GNU Affero General Public License | ||
| along with this program; if not, see http://www.gnu.org/licenses or write to | ||
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| Boston, MA, 02110-1301 USA, or download the license from the following URL: | ||
| http://itextpdf.com/terms-of-use/ | ||
| The interactive user interfaces in modified source and object code versions | ||
| of this program must display Appropriate Legal Notices, as required under | ||
| Section 5 of the GNU Affero General Public License. | ||
| In accordance with Section 7(b) of the GNU Affero General Public License, | ||
| a covered work must retain the producer line in every PDF that is created | ||
| or manipulated using iText. | ||
| You can be released from the requirements of the license by purchasing | ||
| a commercial license. Buying such a license is mandatory as soon as you | ||
| develop commercial activities involving the iText software without | ||
| disclosing the source code of your own applications. | ||
| These activities include: offering paid services to customers as an ASP, | ||
| serving PDFs on the fly in a web application, shipping iText with a closed | ||
| source product. | ||
| For more information, please contact iText Software Corp. at this | ||
| address: sales@itextpdf.com | ||
| */ | ||
| package com.itextpdf.kernel.pdf; | ||
|
|
||
| import com.itextpdf.kernel.PdfException; | ||
| import com.itextpdf.test.annotations.type.UnitTest; | ||
| import org.junit.Assert; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.experimental.categories.Category; | ||
| import org.junit.rules.ExpectedException; | ||
|
|
||
| @Category(UnitTest.class) | ||
| public class MemoryLimitsAwareOutputStreamTest { | ||
|
|
||
| @Rule | ||
| public ExpectedException junitExpectedException = ExpectedException.none(); | ||
|
|
||
| @Test | ||
| public void testMaxSize() { | ||
| junitExpectedException.expect(MemoryLimitsAwareException.class); | ||
| byte[] bigArray = new byte[70]; | ||
| byte[] smallArray = new byte[31]; | ||
|
|
||
| MemoryLimitsAwareOutputStream stream = new MemoryLimitsAwareOutputStream(); | ||
|
|
||
| stream.setMaxStreamSize(100); | ||
| Assert.assertEquals(100, stream.getMaxStreamSize()); | ||
|
|
||
| stream.write(bigArray, 0, bigArray.length); | ||
| Assert.assertEquals(bigArray.length, stream.size()); | ||
|
|
||
| stream.write(smallArray, 0, smallArray.length); | ||
| } | ||
|
|
||
| @Test | ||
| public void testNegativeSize() { | ||
| junitExpectedException.expect(MemoryLimitsAwareException.class); | ||
| byte[] zeroArray = new byte[0]; | ||
|
|
||
| MemoryLimitsAwareOutputStream stream = new MemoryLimitsAwareOutputStream(); | ||
|
|
||
| stream.setMaxStreamSize(-100); | ||
| Assert.assertEquals(-100, stream.getMaxStreamSize()); | ||
|
|
||
| stream.write(zeroArray, 0, zeroArray.length); | ||
| } | ||
|
|
||
| @Test | ||
| public void testIncorrectLength() { | ||
| junitExpectedException.expect(IndexOutOfBoundsException.class); | ||
| MemoryLimitsAwareOutputStream stream = new MemoryLimitsAwareOutputStream(); | ||
| stream.write(new byte[1],0, -1); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,286 @@ | ||
| /* | ||
| This file is part of the iText (R) project. | ||
| Copyright (c) 1998-2019 iText Group NV | ||
| Authors: iText Software. | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Affero General Public License version 3 | ||
| as published by the Free Software Foundation with the addition of the | ||
| following permission added to Section 15 as permitted in Section 7(a): | ||
| FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY | ||
| ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT | ||
| OF THIRD PARTY RIGHTS | ||
| This program is distributed in the hope that it will be useful, but | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Affero General Public License for more details. | ||
| You should have received a copy of the GNU Affero General Public License | ||
| along with this program; if not, see http://www.gnu.org/licenses or write to | ||
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| Boston, MA, 02110-1301 USA, or download the license from the following URL: | ||
| http://itextpdf.com/terms-of-use/ | ||
| The interactive user interfaces in modified source and object code versions | ||
| of this program must display Appropriate Legal Notices, as required under | ||
| Section 5 of the GNU Affero General Public License. | ||
| In accordance with Section 7(b) of the GNU Affero General Public License, | ||
| a covered work must retain the producer line in every PDF that is created | ||
| or manipulated using iText. | ||
| You can be released from the requirements of the license by purchasing | ||
| a commercial license. Buying such a license is mandatory as soon as you | ||
| develop commercial activities involving the iText software without | ||
| disclosing the source code of your own applications. | ||
| These activities include: offering paid services to customers as an ASP, | ||
| serving PDFs on the fly in a web application, shipping iText with a closed | ||
| source product. | ||
| For more information, please contact iText Software Corp. at this | ||
| address: sales@itextpdf.com | ||
| */ | ||
| package com.itextpdf.kernel.pdf; | ||
|
|
||
| import com.itextpdf.io.LogMessageConstant; | ||
| import com.itextpdf.kernel.PdfException; | ||
| import com.itextpdf.test.ExtendedITextTest; | ||
| import com.itextpdf.test.annotations.LogMessage; | ||
| import com.itextpdf.test.annotations.LogMessages; | ||
| import com.itextpdf.test.annotations.type.IntegrationTest; | ||
| import org.junit.Assert; | ||
| import org.junit.BeforeClass; | ||
| import org.junit.Test; | ||
| import org.junit.experimental.categories.Category; | ||
|
|
||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.FileInputStream; | ||
| import java.io.FileOutputStream; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @Category(IntegrationTest.class) | ||
| public class PdfReaderDecodeTest extends ExtendedITextTest { | ||
|
|
||
| public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfReaderDecodeTest/"; | ||
| public static final String destinationFolder = "./target/test/com/itextpdf/kernel/pdf/PdfReaderDecodeTest/"; | ||
|
|
||
|
|
||
| @BeforeClass | ||
| public static void beforeClass() { | ||
| createDestinationFolder(destinationFolder); | ||
| } | ||
|
|
||
| @Test | ||
| public void noMemoryHandlerTest() throws IOException { | ||
| PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
| FileInputStream is = new FileInputStream(sourceFolder + "stream"); | ||
| byte[] b = new byte[51]; | ||
| is.read(b); | ||
|
|
||
| PdfArray array = new PdfArray(); | ||
|
|
||
| PdfStream stream = new PdfStream(b); | ||
| stream.put(PdfName.Filter, array); | ||
| stream.makeIndirect(pdfDocument); | ||
|
|
||
| Assert.assertEquals(51, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(40, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(992, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(1000000, PdfReader.decodeBytes(b, stream).length); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void defaultMemoryHandlerTest() throws IOException { | ||
| PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "timing.pdf"), new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
| PdfStream stream = pdfDocument.getFirstPage().getContentStream(0); | ||
| byte[] b = stream.getBytes(false); | ||
|
|
||
| PdfArray array = new PdfArray(); | ||
| stream.put(PdfName.Filter, array); | ||
|
|
||
| Assert.assertEquals(51, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(40, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(992, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(1000000, PdfReader.decodeBytes(b, stream).length); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void customMemoryHandlerSingleTest() throws IOException { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
| handler.setMaxSizeOfSingleDecompressedPdfStream(1000); | ||
|
|
||
| PdfDocument pdfDocument = new PdfDocument( | ||
| new PdfReader(sourceFolder + "timing.pdf", | ||
| new ReaderProperties().setMemoryLimitsAwareHandler(handler)), | ||
| new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
| PdfStream stream = pdfDocument.getFirstPage().getContentStream(0); | ||
| byte[] b = stream.getBytes(false); | ||
|
|
||
| PdfArray array = new PdfArray(); | ||
| stream.put(PdfName.Filter, array); | ||
|
|
||
| Assert.assertEquals(51, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(40, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(992, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| array.add(PdfName.Fl); | ||
| String expectedExceptionMessage = PdfException.DuringDecompressionSingleStreamOccupiedMoreMemoryThanAllowed; | ||
| String thrownExceptionMessage = null; | ||
| try { | ||
| PdfReader.decodeBytes(b, stream); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| thrownExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedExceptionMessage, thrownExceptionMessage); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void oneFilterCustomMemoryHandlerSingleTest() throws IOException { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
| handler.setMaxSizeOfSingleDecompressedPdfStream(20); | ||
|
|
||
| PdfDocument pdfDocument = new PdfDocument( | ||
| new PdfReader(sourceFolder + "timing.pdf", | ||
| new ReaderProperties().setMemoryLimitsAwareHandler(handler)), | ||
| new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
| PdfStream stream = pdfDocument.getFirstPage().getContentStream(0); | ||
| byte[] b = stream.getBytes(false); | ||
|
|
||
| PdfArray array = new PdfArray(); | ||
| stream.put(PdfName.Filter, array); | ||
|
|
||
| // Limit is reached, but the stream has no filters. Therefore we don't consider ot to be suspicious | ||
| Assert.assertEquals(51, PdfReader.decodeBytes(b, stream).length); | ||
|
|
||
| // Limit is reached, but the stream has only one filter. Therefore we don't consider ot to be suspicious | ||
| array.add(PdfName.Fl); | ||
| Assert.assertEquals(40, PdfReader.decodeBytes(b, stream).length); | ||
| } | ||
|
|
||
| @Test | ||
| public void differentFiltersEmptyTest() { | ||
| byte[] b = new byte[1000]; | ||
|
|
||
| PdfArray array = new PdfArray(); | ||
| array.add(PdfName.Fl); | ||
| array.add(PdfName.AHx); | ||
| array.add(PdfName.A85); | ||
| array.add(PdfName.RunLengthDecode); | ||
|
|
||
| PdfStream stream = new PdfStream(b); | ||
| stream.put(PdfName.Filter, array); | ||
|
|
||
| Assert.assertEquals(0, PdfReader.decodeBytes(b, stream).length); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void customMemoryHandlerSumTest() throws IOException { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
| handler.setMaxSizeOfDecompressedPdfStreamsSum(100000); | ||
|
|
||
| PdfDocument pdfDocument = new PdfDocument( | ||
| new PdfReader(sourceFolder + "timing.pdf", | ||
| new ReaderProperties().setMemoryLimitsAwareHandler(handler)), | ||
| new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
| PdfStream stream = pdfDocument.getFirstPage().getContentStream(0); | ||
| byte[] b = stream.getBytes(false); | ||
|
|
||
| String expectedExceptionMessage = PdfException.DuringDecompressionMultipleStreamsInSumOccupiedMoreMemoryThanAllowed; | ||
| String thrownExceptionMessage = null; | ||
| try { | ||
| PdfReader.decodeBytes(b, stream); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| thrownExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedExceptionMessage, thrownExceptionMessage); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void pageSumTest() throws IOException { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
| handler.setMaxSizeOfDecompressedPdfStreamsSum(1500000); | ||
|
|
||
| PdfDocument pdfDocument = new PdfDocument( | ||
| new PdfReader(sourceFolder + "timing.pdf", | ||
| new ReaderProperties().setMemoryLimitsAwareHandler(handler)), | ||
| new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
|
|
||
| String expectedExceptionMessage = PdfException.DuringDecompressionMultipleStreamsInSumOccupiedMoreMemoryThanAllowed; | ||
| String thrownExceptionMessage = null; | ||
| try { | ||
| pdfDocument.getFirstPage().getContentBytes(); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| thrownExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedExceptionMessage, thrownExceptionMessage); | ||
| } | ||
|
|
||
| @Test | ||
| @LogMessages(messages = { | ||
| @LogMessage(messageTemplate = LogMessageConstant.INVALID_INDIRECT_REFERENCE), | ||
| @LogMessage(messageTemplate = LogMessageConstant.XREF_ERROR) | ||
| }) | ||
| public void pageAsSingleStreamTest() throws IOException { | ||
| MemoryLimitsAwareHandler handler = new MemoryLimitsAwareHandler(); | ||
| handler.setMaxSizeOfSingleDecompressedPdfStream(1500000); | ||
|
|
||
| PdfDocument pdfDocument = new PdfDocument( | ||
| new PdfReader(sourceFolder + "timing.pdf", | ||
| new ReaderProperties().setMemoryLimitsAwareHandler(handler)), | ||
| new PdfWriter(new ByteArrayOutputStream())); | ||
|
|
||
|
|
||
| String expectedExceptionMessage = PdfException.DuringDecompressionSingleStreamOccupiedMoreMemoryThanAllowed; | ||
| String thrownExceptionMessage = null; | ||
| try { | ||
| pdfDocument.getFirstPage().getContentBytes(); | ||
| } catch (MemoryLimitsAwareException e) { | ||
| thrownExceptionMessage = e.getMessage(); | ||
| } | ||
| Assert.assertEquals(expectedExceptionMessage, thrownExceptionMessage); | ||
| } | ||
| } |