Skip to content

Commit 7eb454d

Browse files
author
Eugene Bochilo
committed
Improve memoryFirstWriter
DEVSIX-8582
1 parent 409d265 commit 7eb454d

File tree

4 files changed

+114
-80
lines changed

4 files changed

+114
-80
lines changed

kernel/src/main/java/com/itextpdf/kernel/utils/CompareTool.java

+41-25
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This file is part of the iText (R) project.
2525
import com.itextpdf.commons.actions.contexts.IMetaInfo;
2626
import com.itextpdf.commons.utils.FileUtil;
2727
import com.itextpdf.commons.utils.MessageFormatUtil;
28+
import com.itextpdf.commons.utils.SystemUtil;
2829
import com.itextpdf.io.font.PdfEncodings;
2930
import com.itextpdf.io.logs.IoLogMessageConstant;
3031
import com.itextpdf.io.util.GhostscriptHelper;
@@ -73,6 +74,7 @@ This file is part of the iText (R) project.
7374
import java.nio.charset.StandardCharsets;
7475
import java.util.ArrayList;
7576
import java.util.Arrays;
77+
import java.util.Collections;
7678
import java.util.Comparator;
7779
import java.util.HashSet;
7880
import java.util.LinkedHashMap;
@@ -119,6 +121,7 @@ public class CompareTool {
119121
private static final String VERSION_REPLACEMENT = "<version>";
120122
private static final String COPYRIGHT_REGEXP = "\u00a9\\d+-\\d+ (?:iText Group NV|Apryse Group NV)";
121123
private static final String COPYRIGHT_REPLACEMENT = "\u00a9<copyright years> Apryse Group NV";
124+
private static final boolean MEMORY_FIRST_WRITER_DISABLED;
122125

123126
private static final String NEW_LINES = "\\r|\\n";
124127

@@ -147,6 +150,11 @@ public class CompareTool {
147150
private String gsExec;
148151
private String compareExec;
149152

153+
static {
154+
MEMORY_FIRST_WRITER_DISABLED = "true".equalsIgnoreCase(
155+
SystemUtil.getPropertyOrEnvironmentVariable("DISABLE_MEMORY_FIRST_WRITER"));
156+
}
157+
150158
/**
151159
* Create new {@link CompareTool} instance.
152160
*/
@@ -182,35 +190,39 @@ public static PdfWriter createTestPdfWriter(String filename) throws IOException
182190
* be created, or cannot be opened for any other reason.
183191
*/
184192
public static PdfWriter createTestPdfWriter(String filename, WriterProperties properties) throws IOException {
185-
return new MemoryFirstPdfWriter(filename, properties); // Android-Conversion-Replace return new PdfWriter(filename, properties);
193+
if (MEMORY_FIRST_WRITER_DISABLED) {
194+
return new PdfWriter(filename, properties);
195+
} else {
196+
return new MemoryFirstPdfWriter(filename, properties); // Android-Conversion-Replace return new PdfWriter(filename, properties);
197+
}
186198
}
187199

188200
/**
189201
* Create {@link PdfReader} out of the data created recently or read from disk.
190202
*
191203
* @param filename File to read the data from when necessary.
204+
* @param properties {@link ReaderProperties} to use.
192205
* @return {@link PdfReader} to be used in tests.
193206
* @throws IOException on error
194207
*/
195-
public static PdfReader createOutputReader(String filename) throws IOException {
196-
return CompareTool.createOutputReader(filename, new ReaderProperties());
208+
public static PdfReader createOutputReader(String filename, ReaderProperties properties) throws IOException {
209+
MemoryFirstPdfWriter outWriter = MemoryFirstPdfWriter.get(filename);
210+
if (outWriter != null) {
211+
return new PdfReader(new ByteArrayInputStream(outWriter.getBAOutputStream().toByteArray()), properties);
212+
} else {
213+
return new PdfReader(filename, properties);
214+
}
197215
}
198216

199217
/**
200218
* Create {@link PdfReader} out of the data created recently or read from disk.
201219
*
202220
* @param filename File to read the data from when necessary.
203-
* @param properties {@link ReaderProperties} to use.
204221
* @return {@link PdfReader} to be used in tests.
205222
* @throws IOException on error
206223
*/
207-
public static PdfReader createOutputReader(String filename, ReaderProperties properties) throws IOException {
208-
MemoryFirstPdfWriter outWriter = MemoryFirstPdfWriter.get(filename);
209-
if (outWriter != null) {
210-
return new PdfReader(new ByteArrayInputStream(outWriter.getBAOutputStream().toByteArray()), properties);
211-
} else {
212-
return new PdfReader(filename, properties);
213-
}
224+
public static PdfReader createOutputReader(String filename) throws IOException {
225+
return CompareTool.createOutputReader(filename, new ReaderProperties());
214226
}
215227

216228
/**
@@ -360,39 +372,39 @@ public CompareTool enableEncryptionCompare(boolean kdfSaltCompareEnabled) {
360372
}
361373

362374
/**
363-
* Gets {@link ReaderProperties} to be passed later to the {@link PdfReader} of the output document.
375+
* Gets {@link ReaderProperties} to be passed later to the {@link PdfReader} of the cmp document.
364376
* <p>
365377
* Documents for comparison are opened in reader mode. This method is intended to alter {@link ReaderProperties}
366-
* which are used to open the output document. This is particularly useful for comparison of encrypted documents.
378+
* which are used to open the cmp document. This is particularly useful for comparison of encrypted documents.
367379
* <p>
368380
* For more explanations about what outDoc and cmpDoc are see last paragraph of the {@link CompareTool}
369381
* class description.
370382
*
371-
* @return {@link ReaderProperties} instance to be passed later to the {@link PdfReader} of the output document.
383+
* @return {@link ReaderProperties} instance to be passed later to the {@link PdfReader} of the cmp document.
372384
*/
373-
public ReaderProperties getOutReaderProperties() {
374-
if (outProps == null) {
375-
outProps = new ReaderProperties();
385+
public ReaderProperties getCmpReaderProperties() {
386+
if (cmpProps == null) {
387+
cmpProps = new ReaderProperties();
376388
}
377-
return outProps;
389+
return cmpProps;
378390
}
379391

380392
/**
381-
* Gets {@link ReaderProperties} to be passed later to the {@link PdfReader} of the cmp document.
393+
* Gets {@link ReaderProperties} to be passed later to the {@link PdfReader} of the output document.
382394
* <p>
383395
* Documents for comparison are opened in reader mode. This method is intended to alter {@link ReaderProperties}
384-
* which are used to open the cmp document. This is particularly useful for comparison of encrypted documents.
396+
* which are used to open the output document. This is particularly useful for comparison of encrypted documents.
385397
* <p>
386398
* For more explanations about what outDoc and cmpDoc are see last paragraph of the {@link CompareTool}
387399
* class description.
388400
*
389-
* @return {@link ReaderProperties} instance to be passed later to the {@link PdfReader} of the cmp document.
401+
* @return {@link ReaderProperties} instance to be passed later to the {@link PdfReader} of the output document.
390402
*/
391-
public ReaderProperties getCmpReaderProperties() {
392-
if (cmpProps == null) {
393-
cmpProps = new ReaderProperties();
403+
public ReaderProperties getOutReaderProperties() {
404+
if (outProps == null) {
405+
outProps = new ReaderProperties();
394406
}
395-
return cmpProps;
407+
return outProps;
396408
}
397409

398410
/**
@@ -1313,6 +1325,10 @@ private String compareByContent(String outPath, String differenceImagePrefix, Ma
13131325
CompareTool.writeOnDiskIfNotExists(cmpPdf);
13141326
return compareVisuallyAndCombineReports(compareResult.getReport(), outPath, differenceImagePrefix, ignoredAreas, equalPages);
13151327
}
1328+
} catch (Exception e) {
1329+
CompareTool.writeOnDisk(outPdf);
1330+
CompareTool.writeOnDiskIfNotExists(cmpPdf);
1331+
throw e;
13161332
}
13171333
}
13181334

kernel/src/main/java/com/itextpdf/kernel/utils/MemoryFirstPdfWriter.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ This file is part of the iText (R) project.
3636

3737
/**
3838
* PdfWriter implementation which allows to create documents in memory and dump them on disk on purpose.
39-
* Currently it's private and used in automated tests only.
39+
* Currently, it's private and used in automated tests only.
4040
*/
4141
class MemoryFirstPdfWriter extends PdfWriter {
4242
private static final int MAX_ALLOWED_STREAMS = 100;
4343

44-
private static Map<String, MemoryFirstPdfWriter> waitingStreams = new ConcurrentHashMap<>();
44+
private static final Map<String, MemoryFirstPdfWriter> waitingStreams = new ConcurrentHashMap<>();
4545

46-
private String filePath;
47-
private ByteArrayOutputStream outStream;
46+
private final String filePath;
47+
private final ByteArrayOutputStream outStream;
4848

49-
MemoryFirstPdfWriter(String filename, WriterProperties properties) throws FileNotFoundException {
49+
MemoryFirstPdfWriter(String filename, WriterProperties properties) {
5050
this(filename, MemoryFirstPdfWriter.createBAOutputStream(), properties);
5151
}
5252

@@ -81,9 +81,9 @@ static void cleanup(String path) {
8181
}
8282

8383
void dump() throws IOException {
84-
OutputStream fos = FileUtil.getFileOutputStream(filePath);
85-
outStream.writeTo(fos);
86-
fos.close();
84+
try (OutputStream fos = FileUtil.getFileOutputStream(filePath)) {
85+
outStream.writeTo(fos);
86+
}
8787
}
8888

8989
ByteArrayOutputStream getBAOutputStream() {

kernel/src/main/java/com/itextpdf/kernel/utils/PdfSplitter.java

+48-47
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,6 @@ protected PdfWriter getNextPdfWriter(PageRange documentPageRange) {
244244
return new PdfWriter(new ByteArrayOutputStream());
245245
}
246246

247-
private PdfDocument createPdfDocument(PageRange currentPageRange) {
248-
PdfDocument newDocument = new PdfDocument(getNextPdfWriter(currentPageRange), new DocumentProperties().setEventCountingMetaInfo(metaInfo));
249-
if (pdfDocument.isTagged() && preserveTagged)
250-
newDocument.setTagged();
251-
if (pdfDocument.hasOutlines() && preserveOutlines)
252-
newDocument.initializeOutlines();
253-
return newDocument;
254-
}
255-
256247
/**
257248
* The event listener which is called when another document is ready.
258249
*/
@@ -290,8 +281,18 @@ public List<PdfDocument> splitByOutlines(List<String> outlineTitles) {
290281
return documentList;
291282
}
292283

293-
private PdfDocument splitByOutline(String outlineTitle) {
284+
private PdfDocument createPdfDocument(PageRange currentPageRange) {
285+
PdfDocument newDocument = new PdfDocument(getNextPdfWriter(currentPageRange), new DocumentProperties().setEventCountingMetaInfo(metaInfo));
286+
if (pdfDocument.isTagged() && preserveTagged) {
287+
newDocument.setTagged();
288+
}
289+
if (pdfDocument.hasOutlines() && preserveOutlines) {
290+
newDocument.initializeOutlines();
291+
}
292+
return newDocument;
293+
}
294294

295+
private PdfDocument splitByOutline(String outlineTitle) {
295296
int startPage = -1;
296297
int endPage = -1;
297298

@@ -329,20 +330,33 @@ private PdfDocument splitByOutline(String outlineTitle) {
329330
return toDocument;
330331
}
331332

332-
private PdfPage getPageByOutline(int fromPage, PdfOutline outline) {
333-
int size = pdfDocument.getNumberOfPages();
334-
for (int i = fromPage; i <= size; i++) {
335-
PdfPage pdfPage = pdfDocument.getPage(i);
336-
List<PdfOutline> outlineList = pdfPage.getOutlines(false);
337-
if (outlineList != null) {
338-
for (PdfOutline pdfOutline : outlineList) {
339-
if (pdfOutline.equals(outline)) {
340-
return pdfPage;
341-
}
342-
}
333+
private PageRange getNextRange(int startPage, int endPage, long size) {
334+
PdfResourceCounter counter = new PdfResourceCounter(pdfDocument.getTrailer());
335+
Map<Integer, PdfObject> resources = counter.getResources();
336+
// initialize with trailer length
337+
long lengthWithoutXref = counter.getLength(null);
338+
int currentPage = startPage;
339+
boolean oversized = false;
340+
341+
do {
342+
PdfPage page = pdfDocument.getPage(currentPage++);
343+
counter = new PdfResourceCounter(page.getPdfObject());
344+
lengthWithoutXref += counter.getLength(resources);
345+
resources.putAll(counter.getResources());
346+
347+
if (lengthWithoutXref + xrefLength(resources.size()) > size) {
348+
oversized = true;
343349
}
350+
} while (currentPage <= endPage && !oversized);
351+
352+
// true if at least the first page to be copied didn't cause the oversize
353+
if (oversized && (currentPage - 1) != startPage) {
354+
// we shouldn't copy previous page because it caused
355+
// the oversize and it isn't the first page to be copied
356+
--currentPage;
344357
}
345-
return null;
358+
359+
return new PageRange().addPageSequence(startPage, currentPage - 1);
346360
}
347361

348362
/**
@@ -369,33 +383,20 @@ private PdfOutline getAbsoluteTreeNextOutline(PdfOutline outline) {
369383
return nextPdfOutline;
370384
}
371385

372-
private PageRange getNextRange(int startPage, int endPage, long size) {
373-
PdfResourceCounter counter = new PdfResourceCounter(pdfDocument.getTrailer());
374-
Map<Integer, PdfObject> resources = counter.getResources();
375-
// initialize with trailer length
376-
long lengthWithoutXref = counter.getLength(null);
377-
int currentPage = startPage;
378-
boolean oversized = false;
379-
380-
do {
381-
PdfPage page = pdfDocument.getPage(currentPage++);
382-
counter = new PdfResourceCounter(page.getPdfObject());
383-
lengthWithoutXref += counter.getLength(resources);
384-
resources.putAll(counter.getResources());
385-
386-
if (lengthWithoutXref + xrefLength(resources.size()) > size) {
387-
oversized = true;
386+
private PdfPage getPageByOutline(int fromPage, PdfOutline outline) {
387+
int size = pdfDocument.getNumberOfPages();
388+
for (int i = fromPage; i <= size; i++) {
389+
PdfPage pdfPage = pdfDocument.getPage(i);
390+
List<PdfOutline> outlineList = pdfPage.getOutlines(false);
391+
if (outlineList != null) {
392+
for (PdfOutline pdfOutline : outlineList) {
393+
if (pdfOutline.equals(outline)) {
394+
return pdfPage;
395+
}
396+
}
388397
}
389-
} while (currentPage <= endPage && !oversized);
390-
391-
// true if at least the first page to be copied didn't cause the oversize
392-
if (oversized && (currentPage - 1) != startPage) {
393-
// we shouldn't copy previous page because it caused
394-
// the oversize and it isn't the first page to be copied
395-
--currentPage;
396398
}
397-
398-
return new PageRange().addPageSequence(startPage, currentPage - 1);
399+
return null;
399400
}
400401

401402
private long xrefLength(int size) {

kernel/src/test/java/com/itextpdf/kernel/utils/CompareToolTest.java

+17
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,23 @@ public void memoryFirstWriterNoFileTest() throws InterruptedException, IOExcepti
334334
Assertions.assertFalse(new File(secondPdf).exists());
335335
}
336336

337+
@Test
338+
public void memoryFirstWriterCmpMissingTest() throws IOException {
339+
String firstPdf = destinationFolder + "memoryFirstWriterCmpMissingTest.pdf";
340+
String secondPdf = destinationFolder + "cmp_memoryFirstWriterCmpMissingTest.pdf";
341+
PdfDocument firstDocument = new PdfDocument(CompareTool.createTestPdfWriter(firstPdf));
342+
343+
PdfPage page1FirstDocument = firstDocument.addNewPage();
344+
page1FirstDocument.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 560, 400, 50)).setDestination(
345+
PdfExplicitDestination.createFit(page1FirstDocument)).setBorder(new PdfArray(new float[] {0, 0, 1})));
346+
page1FirstDocument.flush();
347+
firstDocument.close();
348+
349+
Assertions.assertThrows(IOException.class,
350+
() -> new CompareTool().compareByContent(firstPdf, secondPdf, destinationFolder));
351+
Assertions.assertTrue(new File(firstPdf).exists());
352+
}
353+
337354
@Test
338355
public void dumpMemoryFirstWriterOnDiskTest() throws InterruptedException, IOException {
339356
String firstPdf = destinationFolder + "dumpMemoryFirstWriterOnDiskTest.pdf";

0 commit comments

Comments
 (0)