From: Markus Koschany <apo@debian.org>
Date: Mon, 16 Jan 2023 08:39:42 +0100
Subject: CVE-2021-43113
Bug-Debian: https://bugs.debian.org/1014597
Origin: https://github.com/itext/itextpdf/commit/ce8bbacd631e13717a91f02e9cbd9814b9dc2cca
---
.../java/com/itextpdf/testutils/CompareTool.java | 150 ++++++++++++--------
.../com/itextpdf/testutils/CompareToolUtil.java | 155 +++++++++++++++++++++
.../com/itextpdf/testutils/CompareToolTest.java | 50 +++++--
.../testutils/CompareToolTest/EqualPdfsTest.pdf | Bin 0 -> 182207 bytes
.../itextpdf/testutils/CompareToolTest/TestPdf.pdf | Bin 0 -> 2799 bytes
.../CompareToolTest/cmp_EqualPdfsTest.pdf | Bin 0 -> 182207 bytes
.../CompareToolTest/cmp_EqualPdfsTest.xml | 4 +
.../testutils/CompareToolTest/cmp_TestPdf.pdf | Bin 0 -> 2778 bytes
.../testutils/CompareToolTest/cmp_TestPdf.xml | 24 ++++
.../cmp_comparePdfsWithDifferentPagesExceptOne.pdf | Bin 0 -> 5044 bytes
.../cmp_comparePdfsWithDifferentPagesExceptOne.xml | 47 +++++++
.../cmp_comparePdfsWithOneDifferentPage.pdf | Bin 0 -> 4772 bytes
.../cmp_comparePdfsWithOneDifferentPage.xml | 39 ++++++
.../comparePdfsWithDifferentPagesExceptOne.pdf | Bin 0 -> 4773 bytes
.../comparePdfsWithOneDifferentPage.pdf | Bin 0 -> 4734 bytes
15 files changed, 403 insertions(+), 66 deletions(-)
create mode 100644 itext/src/main/java/com/itextpdf/testutils/CompareToolUtil.java
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/EqualPdfsTest.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/TestPdf.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_EqualPdfsTest.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_EqualPdfsTest.xml
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_TestPdf.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_TestPdf.xml
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_comparePdfsWithDifferentPagesExceptOne.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_comparePdfsWithDifferentPagesExceptOne.xml
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_comparePdfsWithOneDifferentPage.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/cmp_comparePdfsWithOneDifferentPage.xml
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/comparePdfsWithDifferentPagesExceptOne.pdf
create mode 100644 itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/comparePdfsWithOneDifferentPage.pdf
diff --git a/itext/src/main/java/com/itextpdf/testutils/CompareTool.java b/itext/src/main/java/com/itextpdf/testutils/CompareTool.java
index ca688a8..fd7c328 100644
--- a/itext/src/main/java/com/itextpdf/testutils/CompareTool.java
+++ b/itext/src/main/java/com/itextpdf/testutils/CompareTool.java
@@ -48,7 +48,25 @@ import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Meta;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.io.RandomAccessSourceFactory;
-import com.itextpdf.text.pdf.*;
+import com.itextpdf.text.pdf.PRIndirectReference;
+import com.itextpdf.text.pdf.PRStream;
+import com.itextpdf.text.pdf.PRTokeniser;
+import com.itextpdf.text.pdf.PdfAnnotation;
+import com.itextpdf.text.pdf.PdfArray;
+import com.itextpdf.text.pdf.PdfBoolean;
+import com.itextpdf.text.pdf.PdfContentByte;
+import com.itextpdf.text.pdf.PdfContentParser;
+import com.itextpdf.text.pdf.PdfDictionary;
+import com.itextpdf.text.pdf.PdfIndirectReference;
+import com.itextpdf.text.pdf.PdfLiteral;
+import com.itextpdf.text.pdf.PdfName;
+import com.itextpdf.text.pdf.PdfNumber;
+import com.itextpdf.text.pdf.PdfObject;
+import com.itextpdf.text.pdf.PdfReader;
+import com.itextpdf.text.pdf.PdfStamper;
+import com.itextpdf.text.pdf.PdfString;
+import com.itextpdf.text.pdf.RandomAccessFileOrArray;
+import com.itextpdf.text.pdf.RefKey;
import com.itextpdf.text.pdf.parser.ContentByteUtils;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.InlineImageInfo;
@@ -89,9 +107,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
-import java.util.StringTokenizer;
import java.util.TreeSet;
-
+import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -102,7 +119,6 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -392,9 +408,17 @@ public class CompareTool {
private String gsExec;
private String compareExec;
- private final String gsParams = " -dNOPAUSE -dBATCH -sDEVICE=png16m -r150 -sOutputFile=<outputfile> <inputfile>";
+ private static final String renderedImageExtension = "png";
+ private static final String pageNumberPattern = "%03d";
+ private static final Pattern pageListRegexp = Pattern.compile("^(\\d+,)*\\d+$");
+ private static final String tempFilePrefix = "itext_gs_io_temp";
+
+
+ private final String gsParams = " -dNOPAUSE -dBATCH -dSAFER -sDEVICE=" +
+ renderedImageExtension + "16m -r150 -sOutputFile=<outputfile> <inputfile>";
private final String compareParams = " \"<image1>\" \"<image2>\" \"<difference>\"";
+
static private final String cannotOpenTargetDirectory = "Cannot open target directory for <filename>.";
static private final String gsFailed = "GhostScript failed for <filename>.";
static private final String unexpectedNumberOfPages = "Unexpected number of pages for <filename>.";
@@ -462,7 +486,6 @@ public class CompareTool {
file.delete();
}
}
-
File diffFile = new File(outPath + differenceImagePrefix);
if (diffFile.exists()) {
diffFile.delete();
@@ -499,38 +522,23 @@ public class CompareTool {
init(outPath + ignoredAreasPrefix + outPdfName, outPath + ignoredAreasPrefix + cmpPdfName);
}
+ String cmpPdfTempCopy = null;
+ String replacementImagesDirectory = null;
+ String outPdfTempCopy = null;
if (targetDir.exists()) {
- String gsParams = this.gsParams.replace("<outputfile>", outPath + cmpImage).replace("<inputfile>", cmpPdf);
- Process p = runProcess(gsExec , gsParams);
- BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream()));
- BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ replacementImagesDirectory = CompareToolUtil.createTempDirectory(tempFilePrefix);
+ cmpPdfTempCopy = CompareToolUtil.createTempCopy(cmpPdf, tempFilePrefix, null);
+ outPdfTempCopy = CompareToolUtil.createTempCopy(outPdf, tempFilePrefix, null);
+ int exitValue = runGhostscriptAndGetExitCode(cmpPdfTempCopy, CompareToolUtil.buildPath(replacementImagesDirectory,
+ new String[]{"cmp_" + tempFilePrefix + pageNumberPattern + "." + renderedImageExtension}));
String line;
- while ((line = bri.readLine()) != null) {
- System.out.println(line);
- }
- bri.close();
- while ((line = bre.readLine()) != null) {
- System.out.println(line);
- }
- bre.close();
- if (p.waitFor() == 0) {
- gsParams = this.gsParams.replace("<outputfile>", outPath + outImage).replace("<inputfile>", outPdf);
- p = runProcess(gsExec , gsParams);
- bri = new BufferedReader(new InputStreamReader(p.getInputStream()));
- bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
- while ((line = bri.readLine()) != null) {
- System.out.println(line);
- }
- bri.close();
- while ((line = bre.readLine()) != null) {
- System.out.println(line);
- }
- bre.close();
- int exitValue = p.waitFor();
-
+ if (exitValue == 0) {
+ exitValue = runGhostscriptAndGetExitCode(outPdfTempCopy, CompareToolUtil.buildPath(replacementImagesDirectory,
+ new String[]{tempFilePrefix + pageNumberPattern + "." + renderedImageExtension}));
if (exitValue == 0) {
- imageFiles = targetDir.listFiles(new PngFileFilter());
- cmpImageFiles = targetDir.listFiles(new CmpPngFileFilter());
+ File tempTargetDir = new File(replacementImagesDirectory);
+ imageFiles = tempTargetDir.listFiles(new PngFileFilter());
+ cmpImageFiles = tempTargetDir.listFiles(new CmpPngFileFilter());
boolean bUnexpectedNumberOfPages = false;
if (imageFiles.length != cmpImageFiles.length) {
bUnexpectedNumberOfPages = true;
@@ -543,6 +551,10 @@ public class CompareTool {
Arrays.sort(cmpImageFiles, new ImageNameComparator());
String differentPagesFail = null;
for (int i = 0; i < cnt; i++) {
+ CompareToolUtil.copy(imageFiles[i].getAbsolutePath(), CompareToolUtil.buildPath(targetDir.getAbsolutePath(),
+ new String[]{outPdfName + "-" + (i + 1) + "." + renderedImageExtension}));
+ CompareToolUtil.copy(cmpImageFiles[i].getAbsolutePath(), CompareToolUtil.buildPath(targetDir.getAbsolutePath(),
+ new String[]{"cmp_" + outPdfName + "-" + (i + 1) + "." + renderedImageExtension}));
if (equalPages != null && equalPages.contains(i))
continue;
System.out.print("Comparing page " + Integer.toString(i + 1) + " (" + imageFiles[i].getAbsolutePath() + ")...");
@@ -552,15 +564,20 @@ public class CompareTool {
is1.close();
is2.close();
if (!cmpResult) {
- if (compareExec != null && new File(compareExec).exists()) {
- String compareParams = this.compareParams.replace("<image1>", imageFiles[i].getAbsolutePath()).replace("<image2>", cmpImageFiles[i].getAbsolutePath()).replace("<difference>", outPath + differenceImagePrefix + Integer.toString(i + 1) + ".png");
- p = runProcess(compareExec , compareParams);
- bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ if (compareExec != null) {
+ String compareParams = this.compareParams.replace("<image1>", imageFiles[i].getAbsolutePath()).replace("<image2>",
+ cmpImageFiles[i].getAbsolutePath()).replace("<difference>",
+ CompareToolUtil.buildPath(replacementImagesDirectory, new String[]{ "diff" +
+ (i + 1) + "." + renderedImageExtension}));
+
+ Process p = CompareToolUtil.runProcess(compareExec , compareParams);
+ BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((line = bre.readLine()) != null) {
System.out.println(line);
}
bre.close();
int cmpExitValue = p.waitFor();
+
if (cmpExitValue == 0) {
if (differentPagesFail == null) {
differentPagesFail = differentPages.replace("<filename>", outPdf).replace("<pagenumber>", Integer.toString(i + 1));
@@ -581,8 +598,17 @@ public class CompareTool {
} else {
System.out.println("done.");
}
+ CompareToolUtil.removeFiles(new String[] {imageFiles[i].getAbsolutePath(), cmpImageFiles[i].getAbsolutePath()});
+ }
+ File[] diffFiles = tempTargetDir.listFiles();
+ for (int i = 0; i < diffFiles.length; i++) {
+ System.out.println(targetDir.getAbsolutePath());
+ CompareToolUtil.copy(diffFiles[i].getAbsolutePath(), CompareToolUtil.buildPath(targetDir.getAbsolutePath(),
+ new String[]{differenceImagePrefix + "-" + (i + 1) + "." + renderedImageExtension}));
+ diffFiles[i].delete();
}
- if (differentPagesFail != null) {
+ tempTargetDir.delete();
+ if (differentPagesFail != null) {
return differentPagesFail;
} else {
if (bUnexpectedNumberOfPages)
@@ -597,20 +623,27 @@ public class CompareTool {
} else {
return cannotOpenTargetDirectory.replace("<filename>", outPdf);
}
-
return null;
}
- private Process runProcess(String execPath, String params) throws IOException, InterruptedException {
- StringTokenizer st = new StringTokenizer(params);
- String[] cmdArray = new String[st.countTokens() + 1];
- cmdArray[0] = execPath;
- for (int i = 1; st.hasMoreTokens(); ++i)
- cmdArray[i] = st.nextToken();
-
- Process p = Runtime.getRuntime().exec(cmdArray);
-
- return p;
+ private int runGhostscriptAndGetExitCode(String replacementPdf, String replacementImagesDirectory)
+ throws IOException, InterruptedException {
+ String gsParams = this.gsParams.replace("<outputfile>", replacementImagesDirectory).
+ replace("<inputfile>", replacementPdf);
+ Process p = CompareToolUtil.runProcess(gsExec , gsParams);
+ BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ String line;
+ while ((line = bri.readLine()) != null) {
+ System.out.println(line);
+ }
+ bri.close();
+ while ((line = bre.readLine()) != null) {
+ System.out.println(line);
+ }
+ bre.close();
+ int exitValue = p.waitFor();
+ return exitValue;
}
public String compare(String outPdf, String cmpPdf, String outPath, String differenceImagePrefix, Map<Integer, List<Rectangle>> ignoredAreas) throws IOException, InterruptedException, DocumentException {
@@ -1367,9 +1400,12 @@ public class CompareTool {
this.cmpPdf = cmpPdf;
outPdfName = new File(outPdf).getName();
cmpPdfName = new File(cmpPdf).getName();
- outImage = outPdfName + "-%03d.png";
- if (cmpPdfName.startsWith("cmp_")) cmpImage = cmpPdfName + "-%03d.png";
- else cmpImage = "cmp_" + cmpPdfName + "-%03d.png";
+ outImage = outPdfName + pageNumberPattern + "." + renderedImageExtension;
+ if (cmpPdfName.startsWith("cmp_")) {
+ cmpImage = cmpPdfName + pageNumberPattern + "." + renderedImageExtension ;
+ } else {
+ cmpImage = "cmp_" + cmpPdfName + pageNumberPattern + "." + renderedImageExtension;
+ }
}
private boolean compareStreams(InputStream is1, InputStream is2) throws IOException {
@@ -1396,7 +1432,7 @@ public class CompareTool {
String ap = pathname.getAbsolutePath();
boolean b1 = ap.endsWith(".png");
boolean b2 = ap.contains("cmp_");
- return b1 && !b2 && ap.contains(outPdfName);
+ return b1 && !b2;
}
}
@@ -1405,7 +1441,7 @@ public class CompareTool {
String ap = pathname.getAbsolutePath();
boolean b1 = ap.endsWith(".png");
boolean b2 = ap.contains("cmp_");
- return b1 && b2 && ap.contains(cmpPdfName);
+ return b1 && b2;
}
}
@@ -1500,6 +1536,4 @@ public class CompareTool {
return new InputSource(new StringReader(""));
}
}
-
-
}
diff --git a/itext/src/main/java/com/itextpdf/testutils/CompareToolUtil.java b/itext/src/main/java/com/itextpdf/testutils/CompareToolUtil.java
new file mode 100644
index 0000000..89a1c84
--- /dev/null
+++ b/itext/src/main/java/com/itextpdf/testutils/CompareToolUtil.java
@@ -0,0 +1,155 @@
+package com.itextpdf.testutils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CompareToolUtil {
+ static private final String SPLIT_REGEX = "((\".+?\"|[^'\\s]|'.+?')+)\\s*";
+
+ /**
+ * Creates a temporary copy of a file.
+ *
+ * @param file the path to the file to be copied
+ * @param tempFilePrefix the prefix of the copied file's name
+ * @param tempFilePostfix the postfix of the copied file's name
+ *
+ * @return the path to the copied file
+ */
+ public static String createTempCopy(String file, String tempFilePrefix, String tempFilePostfix)
+ throws IOException {
+ String replacementFilePath = null;
+ try {
+ replacementFilePath = File.createTempFile(tempFilePrefix, tempFilePostfix).getAbsolutePath();
+ copy(file, replacementFilePath);
+ } catch (IOException e) {
+ if (null != replacementFilePath) {
+ removeFiles(new String[] {replacementFilePath});
+ }
+ throw e;
+ }
+ return replacementFilePath;
+ }
+
+ /**
+ * Creates a copy of a file.
+ *
+ * @param inputFile the path to the file to be copied
+ * @param outputFile the path, to which the passed file should be copied
+ */
+ public static void copy(String inputFile, String outputFile)
+ throws IOException {
+
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = new FileInputStream(inputFile);
+ os = new FileOutputStream(outputFile);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ os.write(buffer, 0, length);
+ }
+ } finally {
+ is.close();
+ os.close();
+ }
+ }
+
+ /**
+ * Creates a temporary directory.
+ *
+ * @param tempFilePrefix the prefix of the temporary directory's name
+ * @return the path to the temporary directory
+ */
+ public static String createTempDirectory(String tempFilePrefix)
+ throws IOException {
+ final File temp;
+
+ temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
+
+ if(!(temp.delete())) {
+ throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
+ }
+
+ if(!(temp.mkdir())) {
+ throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
+ }
+
+ return temp.toString();
+ }
+
+ /**
+ * Removes all of the passed files.
+ *
+ * @param paths paths to files, which should be removed
+ *
+ * @return true if all the files have been successfully removed, false otherwise
+ */
+ public static boolean removeFiles(String[] paths) {
+ boolean allFilesAreRemoved = true;
+ for (String path : paths) {
+ try {
+ if (null != path) {
+ new File(path).delete();
+ }
+ } catch (Exception e) {
+ allFilesAreRemoved = false;
+ }
+ }
+ return allFilesAreRemoved;
+ }
+
+ public static Process runProcess(String execPath, String params) throws IOException, InterruptedException {
+ List<String> cmdList = prepareProcessArguments(execPath, params);
+ String[] cmdArray = cmdList.toArray(new String[0]);
+
+ Process p = Runtime.getRuntime().exec(cmdArray);
+
+ return p;
+ }
+
+ public static List<String> prepareProcessArguments(String exec, String params) {
+ List<String> cmdList;
+ if (new File(exec).exists()) {
+ cmdList = new ArrayList<String>(Collections.singletonList(exec));
+ } else {
+ cmdList = new ArrayList<String>(splitIntoProcessArguments(exec));
+ }
+ cmdList.addAll(splitIntoProcessArguments(params));
+ return cmdList;
+ }
+
+ public static List<String> splitIntoProcessArguments(String line) {
+ List<String> list = new ArrayList<String>();
+ Matcher m = Pattern.compile(SPLIT_REGEX).matcher(line);
+ while (m.find()) {
+ list.add(m.group(1).replace("'", "").replace("\"", "").trim());
+ }
+ return list;
+ }
+
+ public static String buildPath(String path, String[] fragments) {
+ if (path == null) {
+ path = "";
+ }
+
+ if (fragments == null || fragments.length == 0) {
+ return "";
+ }
+
+ for (int i = 0; i < fragments.length; i++) {
+ path = new File(path, fragments[i]).toString();
+ }
+
+ return path;
+ }
+}
diff --git a/itext/src/test/java/com/itextpdf/testutils/CompareToolTest.java b/itext/src/test/java/com/itextpdf/testutils/CompareToolTest.java
index c02b66d..1cbea88 100644
--- a/itext/src/test/java/com/itextpdf/testutils/CompareToolTest.java
+++ b/itext/src/test/java/com/itextpdf/testutils/CompareToolTest.java
@@ -50,10 +50,9 @@ import javax.xml.parsers.ParserConfigurationException;
import org.junit.Before;
import org.junit.Test;
+import org.junit.Assert;
import org.xml.sax.SAXException;
-import junit.framework.Assert;
-
public class CompareToolTest {
private static final String OUT_PATH = "./target/com/itextpdf/testutils/CompareToolTest/";
@@ -82,8 +81,6 @@ public class CompareToolTest {
String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
System.out.println(result);
Assert.assertNotNull("CompareTool must return differences found between the files", result);
- // Comparing the report to the reference one.
- Assert.assertTrue("CompareTool report differs from the reference one", compareTool.compareXmls(RESOURCE_PATH + "cmp_report01.xml", OUT_PATH + "report01.xml"));
}
@Test
@@ -97,8 +94,6 @@ public class CompareToolTest {
String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
System.out.println(result);
Assert.assertNotNull("CompareTool must return differences found between the files", result);
- // Comparing the report to the reference one.
- Assert.assertTrue("CompareTool report differs from the reference one", compareTool.compareXmls(RESOURCE_PATH + "cmp_report02.xml", OUT_PATH + "report02.xml"));
}
@Test
@@ -112,8 +107,47 @@ public class CompareToolTest {
String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
System.out.println(result);
Assert.assertNotNull("CompareTool must return differences found between the files", result);
- // Comparing the report to the reference one.
- Assert.assertTrue("CompareTool report differs from the reference one", compareTool.compareXmls(RESOURCE_PATH + "cmp_report03.xml", OUT_PATH + "report03.xml"));
}
+ @Test
+ public void compareTwoDifferentPdfs()
+ throws DocumentException, IOException, InterruptedException, ParserConfigurationException, SAXException {
+ CompareTool compareTool = new CompareTool();
+ compareTool.setCompareByContentErrorsLimit(10);
+ compareTool.setGenerateCompareByContentXmlReport(true);
+ compareTool.setXmlReportName("TestPdf");
+ String outPdf = RESOURCE_PATH + "TestPdf.pdf";
+ String cmpPdf = RESOURCE_PATH + "cmp_TestPdf.pdf";
+ String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
+ System.out.println(result);
+ Assert.assertNotNull("CompareTool must return differences found between the files", result);
+ }
+
+ @Test
+ public void comparePdfsWithOneDifferentPageTest()
+ throws DocumentException, IOException, InterruptedException, ParserConfigurationException, SAXException {
+ CompareTool compareTool = new CompareTool();
+ compareTool.setCompareByContentErrorsLimit(10);
+ compareTool.setGenerateCompareByContentXmlReport(true);
+ compareTool.setXmlReportName("comparePdfsWithOneDifferentPage");
+ String outPdf = RESOURCE_PATH + "comparePdfsWithOneDifferentPage.pdf";
+ String cmpPdf = RESOURCE_PATH + "cmp_comparePdfsWithOneDifferentPage.pdf";
+ String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
+ System.out.println(result);
+ Assert.assertNotNull("CompareTool must return differences found between the files", result);
+ }
+
+ @Test
+ public void comparePdfsWithDifferentPagesExceptOneTest()
+ throws DocumentException, IOException, InterruptedException, ParserConfigurationException, SAXException {
+ CompareTool compareTool = new CompareTool();
+ compareTool.setCompareByContentErrorsLimit(10);
+ compareTool.setGenerateCompareByContentXmlReport(true);
+ compareTool.setXmlReportName("comparePdfsWithDifferentPagesExceptOne");
+ String outPdf = RESOURCE_PATH + "comparePdfsWithDifferentPagesExceptOne.pdf";
+ String cmpPdf = RESOURCE_PATH + "cmp_comparePdfsWithDifferentPagesExceptOne.pdf";
+ String result = compareTool.compareByContent(outPdf, cmpPdf, OUT_PATH, "difference");
+ System.out.println(result);
+ Assert.assertNotNull("CompareTool must return differences found between the files", result);
+ }
}
diff --git a/itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/EqualPdfsTest.pdf b/itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/EqualPdfsTest.pdf
new file mode 100644
index 0000000..0b61157
--- /dev/null
+++ b/itext/src/test/resources/com/itextpdf/testutils/CompareToolTest/EqualPdfsTest.pdf
@@ -0,0 +1,696 @@
+%PDF-1.5
+%
+1 0 obj
+<</Type/Catalog/Pages 2 0 R/Lang(ru-RU) /StructTreeRoot 8 0 R/MarkInfo<</Marked true>>>>
+endobj
+2 0 obj
+<</Type/Pages/Count 1/Kids[ 3 0 R] >>
+endobj
+3 0 obj
+<</Type/Page/Parent 2 0 R/Resources<</Font<</F1 5 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 595.32 841.92] /Contents 4 0 R/Group<</Type/Group/S/Transparency/CS/DeviceRGB>>/Tabs/S/StructParents 0>>
+endobj
+4 0 obj
+<</Filter/FlateDecode/Length 130>>
+stream
+xSPutQ0IKWH
ִSprqVp
+w3T0430QI2T0 BCS=CP&@!DrEkjkh*xrlh`g9ZCI 4#
+endstream
+endobj
+5 0 obj
+<</Type/Font/Subtype/TrueType/Name/F1/BaseFont/ABCDEE+Calibri/Encoding/WinAnsiEncoding/FontDescriptor 6 0 R/FirstChar 32/LastChar 116/Widths 15 0 R>>
+endobj
+6 0 obj
+<</Type/FontDescriptor/FontName/ABCDEE+Calibri/Flags 32/ItalicAngle 0/Ascent 750/Descent -250/CapHeight 750/AvgWidth 521/MaxWidth 1743/FontWeight 400/XHeight 250/StemV 52/FontBBox[ -503 -250 1240 750] /FontFile2 16 0 R>>
+endobj
+7 0 obj
+<</Author(5=O) /Creator( M i c r o s o f t W o r d 2 0 1 0) /CreationDate(D:20220121114533+03'00') /ModDate(D:20220121114533+03'00') /Producer( M i c r o s o f t W o r d 2 0 1 0) >>
+endobj
+14 0 obj
+<</Type/ObjStm/N 6/First 37/Filter/FlateDecode/Length 284>>
+stream
+xmQ0$XaY]RZa!mM$I[i{ ^{308,31; 0
+/bb 7̚6ۚܟYβ|9oa0LGCjm15͍\OmL'!y碔?I/Hd{^~@O|( j_hsh}ō18 9)j]NM}'+hp'ָm'qx
+endstream
+endobj
+15 0 obj
+[ 226 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 487 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 498 0 0 0 0 0 0 0 0 0 0 0 0 0 391 335]
+endobj
+16 0 obj
+<</Filter/FlateDecode/Length 179309/Length1 542588>>
+stream
+x}XW9;
3AA#M#l ELPkD l&5e5bɚōTfMMOm6?ss{{8c,:VST^:&루zfc)*\xT]qZ*b|
+)[!Ę1S&_\tM-L}|ژr_W$05cA<^11*_ryGc'-_`n͘Ɩel!X<skxW(oۼoʧ~1M
?L\GH҄#iҕ3:},c9aˉHc'N4/
+L_e@x(`
KkoxrƳ~A[};3ZҲp.;ۀ,,nh+չxgLaJF|MLC~^;i0023No;~ضϵN{Dޟ]ͬld=c<|3~>U$V_ffbJ^Q>`h-M,w:
[4']"Mݣ=eS/pK>_E4]t_m=O3}ꑳa2+|U, a5q6jJ<˶.VcWc/eX߱f%,d!MdZ
;l˯_
!YBt3+~*M9m YB,d!YB-sfB,d!YB,d!<OB,d!YB,d!YB,d!YB,d!YB,d!YB,d!Y/݂65_Ӌإ_*^l"`
l)ۖu>ߥ'9z4-U6⃟ gH^ϓxOo72\}ߺe,S5GŊIcзL>zL?dy7\tE-o>yMs̞5sJ_EԲ)'M0~\1%EF>,/w2vr$ڬQsxhTbwIӟVץǎ5~'B%g;kl3sz^ɭlDFPvvJE*'jZ98.J8~^,,oj+)B}BwaCxF:k7C}-( ov,~5?rUi1V7Z]yrg{MV6QﮯQWkQM-nkyE~.7E~ קZζo>`Đj )=LHmh!r\es[*w9 fzJH'S|"UtqT7%[831w*i5s6h**"omY_[NPVtcyZ`1lK3Dm5E@Qr/:Ԟf9J_II+no;jXJU+,~8DvVnYܘjrV*vJ|F \1#d6<%C3ꁣI(Z8rL6S"&zO6rs73*۩>%Lb:$5;1h!1 N?t7XC)ob__V]vpTQz.y~BtB]NnwYɥ2-VT\˫=Un%ڙnbBwZ,ij4
þhsַ+GصO\c?_<;+
+P
+Ҳv/r/^K+*
+W
+k
+{#r1UDTi*߾X_3gu
+Ŭ4A^[:t╹u(Jsb)2ED"Y;
{M0obQ0"@A
lgp{;Ꜫ;xk{Wij0g+rXkw-NϣNW]3~9
+a&4a
}RouUS[M8=X<*G1"] "Oqy<dCƍ;95UTꪨt`?R^TW<xS![jE;R5Ua_
+5 GVF7ZukaU*xh*mZl{ߐFuă2ڢ݃{=<u0WRA2Fun$8ic/"N
`"RS͖p ToęO5VUQ5oc0mѢӆ2X R|oDSEE5el{%Nh&#Zݨw,l9X~E#08:vrf8;O?fߋʪ{2MgG-Zd4^&K7kA%NbiY,^q$\qnAT\tTlJBhgOfei6pG{2:8kVε2e1#6=->cj0INl:g,vTXRV&ua>ɿsF[8k5Jˎv6)ԟ)UM,qJoċ/N }F]p0{[ͯdFivq6+tA7heK\mtDmb7rXbpum>ƃE9pCV7*FrjS]kA(^*a"#mњS{2ZѲ)2X+=r(:ϧVWsJɥ^/V]vhKEQ0* |ͰcL2:\yFy2taʛ̧|F_| U+?|Lr
+@V@CMQX VKk =>;Q#gNe>NHV).bkX-R/*)VJBR,bKX$EX |)8Os'EshAz)ꤘ#E5R̖b3!t)Rs&O
+)ʥ*ES,$)&J1ARTRDb)(@Rxȗb#!p)I'EC"`)rȖbȒ"SRdH.GR}H"UR%K
+)zJ"Ev)H"ARK'E1RDKa*ERX,EaR0JaB/N
+U
+E
+.
+%E'8!q)IqT^VHw)k)K)KJKJ7)>R/a)IJoKoJ)ސKg)HJ/K/JKJ')i)I)KG)q)IJKJW)H%N)RK~)^)bRN.]R)oCۥ&mR*-R,MR(V)nz)Z)j)~#URlJ)b\6).R)6JAK.=\^{py嵇k.=\^{py嵇k.=|.?\py.?\py.?\py.?\py嵇k.o;\vpym^Se}(́qɻ(szFVRFVR
+A+--!ZLEPBe>Q3ybйD5@
!%!M4$otj*Js*ʉM!L4h"DRP)}hQI>TO Ph*%ʧrFÉQ<\DCSe9DT DYTY&
|