Skip to content

Commit bfc6d3b

Browse files
dmitry.radchukUbuntu
authored andcommitted
Ignore FlateDecode and Crypt filters when they're flushed
DEVSIX-1193
1 parent 7be0674 commit bfc6d3b

18 files changed

+421
-75
lines changed

io/src/main/java/com/itextpdf/io/logs/IoLogMessageConstant.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ public final class IoLogMessageConstant {
334334

335335
public static final String INVALID_KEY_VALUE_KEY_0_HAS_NULL_VALUE = "Invalid key value: key {0} has null value.";
336336

337+
public static final String FILTER_WAS_ALREADY_FLUSHED =
338+
"{0} {1} R stream object filter was already flushed, stream compression will remain as in original file. "
339+
+ "Try to flush object containing filter before the PdfDocument#close() method so that stream "
340+
+ "compression would be processed as intended.";
341+
337342
public static final String LAST_ROW_IS_NOT_COMPLETE =
338343
"Last row is not completed. Table bottom border may collapse as you do not expect it";
339344

kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ public final class KernelExceptionMessageConstant {
177177
public static final String FLUSHED_PAGE_CANNOT_BE_ADDED_OR_INSERTED = "Flushed page cannot be added or inserted.";
178178
public static final String FLUSHED_PAGE_CANNOT_BE_REMOVED = "Flushed page cannot be removed from a document which "
179179
+ "is tagged or has an AcroForm";
180+
public static final String FLUSHED_STREAM_FILTER_EXCEPTION =
181+
"Stream {0} {1} R contains flushed indirect filter object in encrypted document, try to use "
182+
+ "PdfStream#flush() stream before PdfDocument#close() to prevent this exception";
180183
public static final String FLUSHING_HELPER_FLUSHING_MODE_IS_NOT_FOR_DOC_READING_MODE = "Flushing writes the object "
181184
+ "to the output stream and releases it from memory. It is only possible for documents that have a "
182185
+ "PdfWriter associated with them. Use PageFlushingHelper#releaseDeep method instead.";
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.itextpdf.kernel.pdf;
2+
3+
import com.itextpdf.commons.utils.MessageFormatUtil;
4+
import com.itextpdf.io.logs.IoLogMessageConstant;
5+
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
6+
import com.itextpdf.kernel.exceptions.PdfException;
7+
8+
import org.slf4j.Logger;
9+
10+
final class IndirectFilterUtils {
11+
private IndirectFilterUtils() {}
12+
13+
static void throwFlushedFilterException(PdfStream stream) {
14+
throw new PdfException(
15+
MessageFormatUtil.format(
16+
KernelExceptionMessageConstant.FLUSHED_STREAM_FILTER_EXCEPTION,
17+
stream.getIndirectReference().getObjNumber(),
18+
stream.getIndirectReference().getGenNumber()));
19+
}
20+
21+
static void logFilterWasAlreadyFlushed(Logger logger, PdfStream stream) {
22+
logger.info(MessageFormatUtil.format(IoLogMessageConstant.FILTER_WAS_ALREADY_FLUSHED,
23+
stream.getIndirectReference().getObjNumber(), stream.getIndirectReference().getGenNumber()));
24+
}
25+
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfOutputStream.java

Lines changed: 102 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ This file is part of the iText (R) project.
5353
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
5454
import com.itextpdf.kernel.pdf.filters.FlateDecodeFilter;
5555
import com.itextpdf.commons.utils.MessageFormatUtil;
56-
5756
import org.slf4j.Logger;
5857
import org.slf4j.LoggerFactory;
5958
import java.io.IOException;
@@ -67,6 +66,7 @@ public class PdfOutputStream extends OutputStream<PdfOutputStream> {
6766
private static final byte[] closeDict = ByteUtils.getIsoBytes(">>");
6867
private static final byte[] endIndirect = ByteUtils.getIsoBytes(" R");
6968
private static final byte[] endIndirectWithZeroGenNr = ByteUtils.getIsoBytes(" 0 R");
69+
private static final Logger LOGGER = LoggerFactory.getLogger(PdfOutputStream.class);
7070

7171
/**
7272
* Document associated with PdfOutputStream.
@@ -181,8 +181,7 @@ private void write(PdfDictionary pdfDictionary) {
181181
write(key);
182182
PdfObject value = pdfDictionary.get(key, false);
183183
if (value == null) {
184-
Logger logger = LoggerFactory.getLogger(PdfOutputStream.class);
185-
logger.warn(MessageFormatUtil.format(IoLogMessageConstant.INVALID_KEY_VALUE_KEY_0_HAS_NULL_VALUE, key));
184+
LOGGER.warn(MessageFormatUtil.format(IoLogMessageConstant.INVALID_KEY_VALUE_KEY_0_HAS_NULL_VALUE, key));
186185
value = PdfNull.PDF_NULL;
187186
}
188187
if ((value.getType() == PdfObject.NUMBER
@@ -213,14 +212,12 @@ private void write(PdfIndirectReference indirectReference) {
213212
throw new PdfException(KernelExceptionMessageConstant.PDF_INDIRECT_OBJECT_BELONGS_TO_OTHER_PDF_DOCUMENT);
214213
}
215214
if (indirectReference.isFree()) {
216-
Logger logger = LoggerFactory.getLogger(PdfOutputStream.class);
217-
logger.error(IoLogMessageConstant.FLUSHED_OBJECT_CONTAINS_FREE_REFERENCE);
215+
LOGGER.error(IoLogMessageConstant.FLUSHED_OBJECT_CONTAINS_FREE_REFERENCE);
218216
write(PdfNull.PDF_NULL);
219217
} else if (indirectReference.refersTo == null
220218
&& (indirectReference.checkState(PdfObject.MODIFIED) || indirectReference.getReader() == null
221219
|| !(indirectReference.getOffset() > 0 || indirectReference.getIndex() >= 0))) {
222-
Logger logger = LoggerFactory.getLogger(PdfOutputStream.class);
223-
logger.error(IoLogMessageConstant.FLUSHED_OBJECT_CONTAINS_REFERENCE_WHICH_NOT_REFER_TO_ANY_OBJECT);
220+
LOGGER.error(IoLogMessageConstant.FLUSHED_OBJECT_CONTAINS_REFERENCE_WHICH_NOT_REFER_TO_ANY_OBJECT);
224221
write(PdfNull.PDF_NULL);
225222
} else if (indirectReference.getGenNumber() == 0) {
226223
writeInteger(indirectReference.getObjNumber()).
@@ -339,7 +336,8 @@ private void write(PdfStream pdfStream) {
339336
assert pdfStream.getOutputStream() != null : "PdfStream lost OutputStream";
340337
ByteArrayOutputStream byteArrayStream;
341338
try {
342-
if (toCompress && !containsFlateFilter(pdfStream) && (allowCompression || userDefinedCompression)) {
339+
if (toCompress && !containsFlateFilter(pdfStream) && decodeParamsArrayNotFlushed(pdfStream)
340+
&& (allowCompression || userDefinedCompression)) {
343341
// compress
344342
updateCompressionFilter(pdfStream);
345343
byteArrayStream = new ByteArrayOutputStream();
@@ -390,70 +388,86 @@ private void write(PdfStream pdfStream) {
390388
protected boolean checkEncryption(PdfStream pdfStream) {
391389
if (crypto == null || (crypto.isEmbeddedFilesOnly() && !document.doesStreamBelongToEmbeddedFile(pdfStream))) {
392390
return false;
393-
} else if (isXRefStream(pdfStream)) {
391+
}
392+
if (isXRefStream(pdfStream)) {
394393
// The cross-reference stream shall not be encrypted
395394
return false;
396-
} else {
397-
PdfObject filter = pdfStream.get(PdfName.Filter, true);
398-
if (filter != null) {
399-
if (PdfName.Crypt.equals(filter)) {
400-
return false;
401-
} else if (filter.getType() == PdfObject.ARRAY) {
402-
PdfArray filters = (PdfArray) filter;
403-
if (!filters.isEmpty() && PdfName.Crypt.equals(filters.get(0, true))) {
404-
return false;
405-
}
406-
}
407-
}
395+
}
396+
PdfObject filter = pdfStream.get(PdfName.Filter, true);
397+
if (filter == null) {
408398
return true;
409399
}
400+
if (filter.isFlushed()) {
401+
IndirectFilterUtils.throwFlushedFilterException(pdfStream);
402+
}
403+
if (PdfName.Crypt.equals(filter)) {
404+
return false;
405+
}
406+
if (filter.getType() == PdfObject.ARRAY) {
407+
PdfArray filters = (PdfArray) filter;
408+
if (filters.isEmpty()) {
409+
return true;
410+
}
411+
if (filters.get(0).isFlushed()) {
412+
IndirectFilterUtils.throwFlushedFilterException(pdfStream);
413+
}
414+
return !PdfName.Crypt.equals(filters.get(0, true));
415+
}
416+
return true;
410417
}
411418

412419
protected boolean containsFlateFilter(PdfStream pdfStream) {
413420
PdfObject filter = pdfStream.get(PdfName.Filter);
414-
if (filter != null) {
415-
if (filter.getType() == PdfObject.NAME) {
416-
if (PdfName.FlateDecode.equals(filter)) {
417-
return true;
418-
}
419-
} else if (filter.getType() == PdfObject.ARRAY) {
420-
if (((PdfArray) filter).contains(PdfName.FlateDecode))
421-
return true;
422-
} else {
423-
throw new PdfException(KernelExceptionMessageConstant.FILTER_IS_NOT_A_NAME_OR_ARRAY);
421+
if (filter == null) {
422+
return false;
423+
}
424+
if (filter.isFlushed()) {
425+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, pdfStream);
426+
return true;
427+
}
428+
if (filter.getType() != PdfObject.NAME && filter.getType() != PdfObject.ARRAY) {
429+
throw new PdfException(KernelExceptionMessageConstant.FILTER_IS_NOT_A_NAME_OR_ARRAY);
430+
}
431+
if (filter.getType() == PdfObject.NAME) {
432+
return PdfName.FlateDecode.equals(filter);
433+
}
434+
for (PdfObject obj : (PdfArray) filter) {
435+
if (obj.isFlushed()) {
436+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, pdfStream);
437+
return true;
424438
}
425439
}
426-
return false;
440+
return ((PdfArray) filter).contains(PdfName.FlateDecode);
427441
}
428442

429443
protected void updateCompressionFilter(PdfStream pdfStream) {
430444
PdfObject filter = pdfStream.get(PdfName.Filter);
431445
if (filter == null) {
432446
pdfStream.put(PdfName.Filter, PdfName.FlateDecode);
447+
return;
448+
}
449+
PdfArray filters = new PdfArray();
450+
filters.add(PdfName.FlateDecode);
451+
if (filter instanceof PdfArray) {
452+
filters.addAll((PdfArray) filter);
433453
} else {
434-
PdfArray filters = new PdfArray();
435-
filters.add(PdfName.FlateDecode);
436-
if (filter instanceof PdfArray) {
437-
filters.addAll((PdfArray) filter);
454+
filters.add(filter);
455+
}
456+
PdfObject decodeParms = pdfStream.get(PdfName.DecodeParms);
457+
if (decodeParms != null) {
458+
if (decodeParms instanceof PdfDictionary) {
459+
PdfArray array = new PdfArray();
460+
array.add(new PdfNull());
461+
array.add(decodeParms);
462+
pdfStream.put(PdfName.DecodeParms, array);
463+
} else if (decodeParms instanceof PdfArray) {
464+
((PdfArray) decodeParms).add(0, new PdfNull());
438465
} else {
439-
filters.add(filter);
440-
}
441-
PdfObject decodeParms = pdfStream.get(PdfName.DecodeParms);
442-
if (decodeParms != null) {
443-
if (decodeParms instanceof PdfDictionary) {
444-
PdfArray array = new PdfArray();
445-
array.add(new PdfNull());
446-
array.add(decodeParms);
447-
pdfStream.put(PdfName.DecodeParms, array);
448-
} else if (decodeParms instanceof PdfArray) {
449-
((PdfArray) decodeParms).add(0, new PdfNull());
450-
} else {
451-
throw new PdfException(KernelExceptionMessageConstant.THIS_DECODE_PARAMETER_TYPE_IS_NOT_SUPPORTED)
452-
.setMessageParams(decodeParms.getClass().toString());
453-
}
466+
throw new PdfException(KernelExceptionMessageConstant.THIS_DECODE_PARAMETER_TYPE_IS_NOT_SUPPORTED)
467+
.setMessageParams(decodeParms.getClass().toString());
454468
}
455-
pdfStream.put(PdfName.Filter, filters);
456469
}
470+
pdfStream.put(PdfName.Filter, filters);
457471
}
458472

459473
protected byte[] decodeFlateBytes(PdfStream stream, byte[] bytes) {
@@ -468,11 +482,20 @@ protected byte[] decodeFlateBytes(PdfStream stream, byte[] bytes) {
468482
filterName = (PdfName) filterObject;
469483
} else if (filterObject instanceof PdfArray) {
470484
filtersArray = (PdfArray) filterObject;
485+
if (filtersArray.isFlushed()) {
486+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, stream);
487+
return bytes;
488+
}
471489
filterName = filtersArray.getAsName(0);
472490
} else {
473491
throw new PdfException(KernelExceptionMessageConstant.FILTER_IS_NOT_A_NAME_OR_ARRAY);
474492
}
475493

494+
if (filterName.isFlushed()) {
495+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, stream);
496+
return bytes;
497+
}
498+
476499
if (!PdfName.FlateDecode.equals(filterName)) {
477500
return bytes;
478501
}
@@ -483,6 +506,9 @@ protected byte[] decodeFlateBytes(PdfStream stream, byte[] bytes) {
483506
PdfObject decodeParamsObject = stream.get(PdfName.DecodeParms);
484507
if (decodeParamsObject == null) {
485508
decodeParams = null;
509+
} else if (decodeParamsObject.isFlushed()) {
510+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, stream);
511+
return bytes;
486512
} else if (decodeParamsObject.getType() == PdfObject.DICTIONARY) {
487513
decodeParams = (PdfDictionary) decodeParamsObject;
488514
} else if (decodeParamsObject.getType() == PdfObject.ARRAY) {
@@ -493,6 +519,13 @@ protected byte[] decodeFlateBytes(PdfStream stream, byte[] bytes) {
493519
.setMessageParams(decodeParamsObject.getClass().toString());
494520
}
495521

522+
if (decodeParams != null && (decodeParams.isFlushed() || isFlushed(decodeParams, PdfName.Predictor)
523+
|| isFlushed(decodeParams, PdfName.Columns) || isFlushed(decodeParams, PdfName.Colors) || isFlushed(
524+
decodeParams, PdfName.BitsPerComponent))) {
525+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, stream);
526+
return bytes;
527+
}
528+
496529
// decode
497530
byte[] res = FlateDecodeFilter.flateDecode(bytes, true);
498531
if (res == null)
@@ -535,4 +568,21 @@ protected byte[] decodeFlateBytes(PdfStream stream, byte[] bytes) {
535568

536569
return bytes;
537570
}
571+
572+
private static boolean isFlushed(PdfDictionary dict, PdfName name) {
573+
PdfObject obj = dict.get(name);
574+
return obj != null && obj.isFlushed();
575+
}
576+
577+
private static boolean decodeParamsArrayNotFlushed(PdfStream pdfStream) {
578+
PdfArray decodeParams = pdfStream.getAsArray(PdfName.DecodeParms);
579+
if (decodeParams == null) {
580+
return true;
581+
}
582+
if (decodeParams.isFlushed()) {
583+
IndirectFilterUtils.logFilterWasAlreadyFlushed(LOGGER, pdfStream);
584+
return false;
585+
}
586+
return true;
587+
}
538588
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfReader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,11 +407,17 @@ public byte[] readStreamBytesRaw(PdfStream stream) throws IOException {
407407
PdfObject filter = stream.get(PdfName.Filter, true);
408408
boolean skip = false;
409409
if (filter != null) {
410+
if (filter.isFlushed()) {
411+
IndirectFilterUtils.throwFlushedFilterException(stream);
412+
}
410413
if (PdfName.Crypt.equals(filter)) {
411414
skip = true;
412415
} else if (filter.getType() == PdfObject.ARRAY) {
413416
PdfArray filters = (PdfArray) filter;
414417
for (int k = 0; k < filters.size(); k++) {
418+
if (filters.get(k).isFlushed()) {
419+
IndirectFilterUtils.throwFlushedFilterException(stream);
420+
}
415421
if (!filters.isEmpty() && PdfName.Crypt.equals(filters.get(k, true))) {
416422
skip = true;
417423
break;

0 commit comments

Comments
 (0)