@@ -44,11 +44,13 @@ This file is part of the iText (R) project.
44
44
package com .itextpdf .io .util ;
45
45
46
46
import com .itextpdf .commons .utils .FileUtil ;
47
+ import com .itextpdf .commons .utils .ProcessInfo ;
47
48
import com .itextpdf .commons .utils .SystemUtil ;
48
49
import com .itextpdf .io .exceptions .IoExceptionMessage ;
49
50
50
- import java .io .File ;
51
51
import java .io .IOException ;
52
+ import java .util .regex .Matcher ;
53
+ import java .util .regex .Pattern ;
52
54
53
55
/**
54
56
* A utility class that is used as an interface to run 3rd-party tool ImageMagick.
@@ -71,6 +73,9 @@ public class ImageMagickHelper {
71
73
static final String MAGICK_COMPARE_KEYWORD = "ImageMagick Studio LLC" ;
72
74
73
75
private static final String TEMP_FILE_PREFIX = "itext_im_io_temp" ;
76
+ private static final String DIFF_PIXELS_OUTPUT_REGEXP = "^\\ d+\\ .*\\ d*(e\\ +\\ d+)?" ;
77
+
78
+ private static final Pattern pattern = Pattern .compile (DIFF_PIXELS_OUTPUT_REGEXP );
74
79
75
80
private String compareExec ;
76
81
@@ -141,6 +146,58 @@ public boolean runImageMagickImageCompare(String outImageFilePath, String cmpIma
141
146
*/
142
147
public boolean runImageMagickImageCompare (String outImageFilePath , String cmpImageFilePath ,
143
148
String diffImageName , String fuzzValue ) throws IOException , InterruptedException {
149
+ ImageMagickCompareResult compareResult = runImageMagickImageCompareAndGetResult (outImageFilePath ,
150
+ cmpImageFilePath , diffImageName , fuzzValue );
151
+
152
+ return compareResult .isComparingResultSuccessful ();
153
+ }
154
+
155
+ /**
156
+ * Runs imageMagick to visually compare images with the specified fuzziness value and given threshold
157
+ * and generate difference output.
158
+ *
159
+ * @param outImageFilePath Path to the output image file
160
+ * @param cmpImageFilePath Path to the cmp image file
161
+ * @param diffImageName Path to the difference output image file
162
+ * @param fuzzValue String fuzziness value to compare images. Should be formatted as string with integer
163
+ * or decimal number. Can be null, if it is not required to use fuzziness
164
+ * @param threshold Long value of accepted threshold.
165
+ *
166
+ * @return boolean result of comparing: true - images are visually equal
167
+ *
168
+ * @throws IOException if there are file's reading/writing issues
169
+ * @throws InterruptedException if there is thread interruption while executing ImageMagick.
170
+ */
171
+ public boolean runImageMagickImageCompareWithThreshold (String outImageFilePath , String cmpImageFilePath ,
172
+ String diffImageName , String fuzzValue , long threshold ) throws IOException , InterruptedException {
173
+ ImageMagickCompareResult compareResult = runImageMagickImageCompareAndGetResult (outImageFilePath ,
174
+ cmpImageFilePath , diffImageName , fuzzValue );
175
+
176
+ if (compareResult .isComparingResultSuccessful ()) {
177
+ return true ;
178
+ } else {
179
+ return compareResult .getDiffPixels () <= threshold ;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Runs imageMagick to visually compare images with the specified fuzziness value and generate difference output.
185
+ * This method returns an object of {@link ImageMagickCompareResult}, containing comparing result information,
186
+ * such as boolean result value and the number of different pixels.
187
+ *
188
+ * @param outImageFilePath Path to the output image file
189
+ * @param cmpImageFilePath Path to the cmp image file
190
+ * @param diffImageName Path to the difference output image file
191
+ * @param fuzzValue String fuzziness value to compare images. Should be formatted as string with integer
192
+ * or decimal number. Can be null, if it is not required to use fuzziness
193
+ *
194
+ * @return an object of {@link ImageMagickCompareResult}. containing comparing result information.
195
+ *
196
+ * @throws IOException if there are file's reading/writing issues
197
+ * @throws InterruptedException if there is thread interruption while executing ImageMagick.
198
+ */
199
+ public ImageMagickCompareResult runImageMagickImageCompareAndGetResult (String outImageFilePath ,
200
+ String cmpImageFilePath , String diffImageName , String fuzzValue ) throws IOException , InterruptedException {
144
201
if (!validateFuzziness (fuzzValue )) {
145
202
throw new IllegalArgumentException ("Invalid fuzziness value: " + fuzzValue );
146
203
}
@@ -160,12 +217,15 @@ public boolean runImageMagickImageCompare(String outImageFilePath, String cmpIma
160
217
+ replacementOutFile + "' '"
161
218
+ replacementCmpFile + "' '"
162
219
+ replacementDiff + "'" ;
163
- boolean result = SystemUtil .runProcessAndWait (compareExec , currCompareParams );
220
+ ProcessInfo processInfo = SystemUtil .runProcessAndGetProcessInfo (compareExec , currCompareParams );
221
+ boolean comparingResult = processInfo .getExitCode () == 0 ;
222
+ long diffPixels = parseImageMagickProcessOutput (processInfo .getProcessErrOutput ());
223
+ ImageMagickCompareResult resultInfo = new ImageMagickCompareResult (comparingResult , diffPixels );
164
224
165
225
if (FileUtil .fileExists (replacementDiff )) {
166
226
FileUtil .copy (replacementDiff , diffImageName );
167
227
}
168
- return result ;
228
+ return resultInfo ;
169
229
} finally {
170
230
FileUtil .removeFiles (new String [] {replacementOutFile , replacementCmpFile , replacementDiff });
171
231
}
@@ -184,4 +244,29 @@ static boolean validateFuzziness(String fuzziness) {
184
244
}
185
245
}
186
246
}
247
+
248
+ private static long parseImageMagickProcessOutput (String processOutput ) throws IOException {
249
+ if (null == processOutput ) {
250
+ throw new IllegalArgumentException (IoExceptionMessage .IMAGE_MAGICK_OUTPUT_IS_NULL );
251
+ }
252
+
253
+ if (processOutput .isEmpty ()) {
254
+ return 0L ;
255
+ }
256
+
257
+ String [] processOutputLines = processOutput .split ("\n " );
258
+
259
+ for (String line : processOutputLines ) {
260
+ try {
261
+ Matcher matcher = pattern .matcher (line );
262
+ if (matcher .find ()) {
263
+ return (long ) Double .valueOf (matcher .group ()).longValue ();
264
+ }
265
+ } catch (NumberFormatException e ) {
266
+ // Nothing should be done here because of the exception, that will be thrown later.
267
+ }
268
+ }
269
+
270
+ throw new IOException (IoExceptionMessage .IMAGE_MAGICK_PROCESS_EXECUTION_FAILED + processOutput );
271
+ }
187
272
}
0 commit comments