@@ -42,6 +42,7 @@ source product.
42424343*/
4444using System ;
45+ using System . Text . RegularExpressions ;
4546using iText . IO ;
4647
4748namespace iText . IO . Util {
@@ -67,6 +68,10 @@ public class ImageMagickHelper {
6768
6869 private const String TEMP_FILE_PREFIX = "itext_im_io_temp" ;
6970
71+ private const String DIFF_PIXELS_OUTPUT_REGEXP = "^\\ d+\\ .*\\ d*(e\\ +\\ d+)?" ;
72+
73+ private static readonly Regex pattern = iText . IO . Util . StringUtil . RegexCompile ( DIFF_PIXELS_OUTPUT_REGEXP ) ;
74+
7075 private String compareExec ;
7176
7277 /// <summary>
@@ -127,6 +132,59 @@ public virtual bool RunImageMagickImageCompare(String outImageFilePath, String c
127132 /// <returns>boolean result of comparing: true - images are visually equal</returns>
128133 public virtual bool RunImageMagickImageCompare ( String outImageFilePath , String cmpImageFilePath , String diffImageName
129134 , String fuzzValue ) {
135+ ImageMagickCompareResult compareResult = RunImageMagickImageCompareAndGetResult ( outImageFilePath , cmpImageFilePath
136+ , diffImageName , fuzzValue ) ;
137+ return compareResult . IsComparingResultSuccessful ( ) ;
138+ }
139+
140+ /// <summary>
141+ /// Runs imageMagick to visually compare images with the specified fuzziness value and given threshold
142+ /// and generate difference output.
143+ /// </summary>
144+ /// <param name="outImageFilePath">Path to the output image file</param>
145+ /// <param name="cmpImageFilePath">Path to the cmp image file</param>
146+ /// <param name="diffImageName">Path to the difference output image file</param>
147+ /// <param name="fuzzValue">
148+ /// String fuzziness value to compare images. Should be formatted as string with integer
149+ /// or decimal number. Can be null, if it is not required to use fuzziness
150+ /// </param>
151+ /// <param name="threshold">Long value of accepted threshold.</param>
152+ /// <returns>boolean result of comparing: true - images are visually equal</returns>
153+ public virtual bool RunImageMagickImageCompareWithThreshold ( String outImageFilePath , String cmpImageFilePath
154+ , String diffImageName , String fuzzValue , long threshold ) {
155+ ImageMagickCompareResult compareResult = RunImageMagickImageCompareAndGetResult ( outImageFilePath , cmpImageFilePath
156+ , diffImageName , fuzzValue ) ;
157+ if ( compareResult . IsComparingResultSuccessful ( ) ) {
158+ return true ;
159+ }
160+ else {
161+ return compareResult . GetDiffPixels ( ) <= threshold ;
162+ }
163+ }
164+
165+ /// <summary>Runs imageMagick to visually compare images with the specified fuzziness value and generate difference output.
166+ /// </summary>
167+ /// <remarks>
168+ /// Runs imageMagick to visually compare images with the specified fuzziness value and generate difference output.
169+ /// This method returns an object of
170+ /// <see cref="ImageMagickCompareResult"/>
171+ /// , containing comparing result information,
172+ /// such as boolean result value and the number of different pixels.
173+ /// </remarks>
174+ /// <param name="outImageFilePath">Path to the output image file</param>
175+ /// <param name="cmpImageFilePath">Path to the cmp image file</param>
176+ /// <param name="diffImageName">Path to the difference output image file</param>
177+ /// <param name="fuzzValue">
178+ /// String fuzziness value to compare images. Should be formatted as string with integer
179+ /// or decimal number. Can be null, if it is not required to use fuzziness
180+ /// </param>
181+ /// <returns>
182+ /// an object of
183+ /// <see cref="ImageMagickCompareResult"/>
184+ /// . containing comparing result information.
185+ /// </returns>
186+ public virtual ImageMagickCompareResult RunImageMagickImageCompareAndGetResult ( String outImageFilePath , String
187+ cmpImageFilePath , String diffImageName , String fuzzValue ) {
130188 if ( ! ValidateFuzziness ( fuzzValue ) ) {
131189 throw new ArgumentException ( "Invalid fuzziness value: " + fuzzValue ) ;
132190 }
@@ -137,14 +195,19 @@ public virtual bool RunImageMagickImageCompare(String outImageFilePath, String c
137195 try {
138196 replacementOutFile = FileUtil . CreateTempCopy ( outImageFilePath , TEMP_FILE_PREFIX , null ) ;
139197 replacementCmpFile = FileUtil . CreateTempCopy ( cmpImageFilePath , TEMP_FILE_PREFIX , null ) ;
140- replacementDiff = FileUtil . CreateTempFile ( TEMP_FILE_PREFIX , null ) . ToString ( ) ;
198+ // ImageMagick generates difference images in .png format, therefore we can specify it.
199+ // For some reason .webp comparison fails if the extension of diff image is not specified.
200+ replacementDiff = FileUtil . CreateTempFile ( TEMP_FILE_PREFIX , ".png" ) . FullName ;
141201 String currCompareParams = fuzzValue + " '" + replacementOutFile + "' '" + replacementCmpFile + "' '" + replacementDiff
142202 + "'" ;
143- bool result = SystemUtil . RunProcessAndWait ( compareExec , currCompareParams ) ;
203+ ProcessInfo processInfo = SystemUtil . RunProcessAndGetProcessInfo ( compareExec , currCompareParams ) ;
204+ bool comparingResult = processInfo . GetExitCode ( ) == 0 ;
205+ long diffPixels = ParseImageMagickProcessOutput ( processInfo . GetProcessErrOutput ( ) ) ;
206+ ImageMagickCompareResult resultInfo = new ImageMagickCompareResult ( comparingResult , diffPixels ) ;
144207 if ( FileUtil . FileExists ( replacementDiff ) ) {
145208 FileUtil . Copy ( replacementDiff , diffImageName ) ;
146209 }
147- return result ;
210+ return resultInfo ;
148211 }
149212 finally {
150213 FileUtil . RemoveFiles ( new String [ ] { replacementOutFile , replacementCmpFile , replacementDiff } ) ;
@@ -166,5 +229,27 @@ internal static bool ValidateFuzziness(String fuzziness) {
166229 }
167230 }
168231 }
232+
233+ private static long ParseImageMagickProcessOutput ( String processOutput ) {
234+ if ( null == processOutput ) {
235+ throw new ArgumentException ( IoExceptionMessage . IMAGE_MAGICK_OUTPUT_IS_NULL ) ;
236+ }
237+ if ( String . IsNullOrEmpty ( processOutput ) ) {
238+ return 0L ;
239+ }
240+ String [ ] processOutputLines = iText . IO . Util . StringUtil . Split ( processOutput , "\n " ) ;
241+ foreach ( String line in processOutputLines ) {
242+ try {
243+ Matcher matcher = iText . IO . Util . Matcher . Match ( pattern , line ) ;
244+ if ( matcher . Find ( ) ) {
245+ return ( long ) Double . Parse ( matcher . Group ( ) , System . Globalization . CultureInfo . InvariantCulture ) ;
246+ }
247+ }
248+ catch ( FormatException ) {
249+ }
250+ }
251+ // Nothing should be done here because of the exception, that will be thrown later.
252+ throw new System . IO . IOException ( IoExceptionMessage . IMAGE_MAGICK_PROCESS_EXECUTION_FAILED + processOutput ) ;
253+ }
169254 }
170255}
0 commit comments