2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 import helper
.OSHelper
;
22 import helper
.ProcessHandler
;
26 * Helper class to interpret a jpg filename
35 private NameDPIPage(String _sName
, int _nDPI
, int _nPage
)
42 public static NameDPIPage
interpret(String _sFilename
)
44 String sBasename
= FileHelper
.getBasename(_sFilename
); // if exist a path, remove it
45 String sNameNoSuffix
= FileHelper
.getNameNoSuffix(sBasename
); // remove extension (.jpg)
47 // check if there exist a 'DPI_' at specific position
48 String sDPICheck
= sNameNoSuffix
.substring(sNameNoSuffix
.length() - 8, sNameNoSuffix
.length() - 4);
52 if (sDPICheck
.equals("DPI_"))
54 // seems to be a generated filename by us.
55 int nDPIStart
= sNameNoSuffix
.lastIndexOf("_", sNameNoSuffix
.length() - 8);
56 sName
= sNameNoSuffix
.substring(0, nDPIStart
);
59 String sDPI
= sNameNoSuffix
.substring(nDPIStart
+ 1, sNameNoSuffix
.length() - 8);
62 nDPI
= Integer
.valueOf(sDPI
).intValue();
64 catch (java
.lang
.NumberFormatException e
)
66 GlobalLogWriter
.println("DPI: Number format exception");
68 String sPage
= sNameNoSuffix
.substring(sNameNoSuffix
.length() - 4);
71 nPage
= Integer
.valueOf(sPage
).intValue();
73 catch (java
.lang
.NumberFormatException e
)
75 GlobalLogWriter
.println("Page: Number format exception");
81 sName
= sNameNoSuffix
;
84 return new NameDPIPage(sName
, nDPI
, nPage
);
88 class CountNotXXXPixelsFromImage
extends Thread
91 private String m_sFilename
;
92 protected int m_nValue
;
94 CountNotXXXPixelsFromImage(String _sFilename
)
96 m_sFilename
= _sFilename
;
104 protected void setValue(int _nValue
)
109 protected String
getFilename()
115 class CountNotWhitePixelsFromImage
extends CountNotXXXPixelsFromImage
118 CountNotWhitePixelsFromImage(String _sFilename
)
127 final int nNotWhiteCount
= PixelCounter
.countNotWhitePixelsFromImage(getFilename());
128 setValue(nNotWhiteCount
);
130 catch (java
.io
.IOException e
)
137 class CountNotBlackPixelsFromImage
extends CountNotXXXPixelsFromImage
140 CountNotBlackPixelsFromImage(String _sFilename
)
149 final int nNotBlackCount
= PixelCounter
.countNotBlackPixelsFromImage(getFilename());
150 setValue(nNotBlackCount
);
152 catch (java
.io
.IOException e
)
159 public class JPEGComparator
extends EnhancedComplexTestCase
162 public String
[] getTestMethodNames()
164 return new String
[]{"CompareJPEGvsJPEG"};
166 private Tolerance m_aTolerance
;
171 public void CompareJPEGvsJPEG()
173 GlobalLogWriter
.set(log
);
174 ParameterHelper aParam
= new ParameterHelper(param
);
176 // run through all documents found in Inputpath
177 foreachJPEGcompareWithJPEG(aParam
);
180 public void checkOneFile(String _sDocumentName
, String _sResult
, ParameterHelper _aParams
) throws OfficeException
182 String sPath
= FileHelper
.getPath(_sDocumentName
);
183 String sSectionName
= FileHelper
.getBasename(_sDocumentName
);
185 // take the build id out of the ini file in the reference file and put it into the current parameter helper
186 String sIniFileForRefBuildID
= FileHelper
.appendPath(sPath
, sSectionName
+ ".ini");
187 IniFile aIniFileForRefBuildID
= new IniFile(sIniFileForRefBuildID
);
188 String sRefBuildID
= aIniFileForRefBuildID
.getValue("global", "buildid");
189 aIniFileForRefBuildID
.close();
191 _aParams
.getTestParameters().put("RefBuildId", sRefBuildID
);
193 String sIniFile
= FileHelper
.appendPath(sPath
, "index.ini");
194 IniFile aIniFile
= new IniFile(sIniFile
);
195 if (aIniFile
.hasValue(sSectionName
, "pages"))
197 // only which has 'pages' has also pictures
198 int nPages
= aIniFile
.getIntValue(sSectionName
, "pages", 0);
199 String sJPEGSchema
= aIniFile
.getValue(sSectionName
, "jpegschema");
200 int nTolerance
= aIniFile
.getIntValue(sSectionName
, "tolerance", 0);
201 m_aTolerance
= new Tolerance(nTolerance
);
202 for (int i
= 1; i
<= nPages
; i
++)
204 String sJPEGFilename
= JPEGCreator
.getFilenameForJPEGSchema(sJPEGSchema
, i
);
205 String sJPEGPath
= FileHelper
.getPath(sJPEGFilename
);
206 if (!sPath
.equals(sJPEGPath
))
208 GlobalLogWriter
.println("Path where to find the index and where to file the JPEG pictures are not the same.");
210 File aFile
= new File(sJPEGFilename
);
211 assure("File '" + sJPEGFilename
+ "' doesn't exists.", aFile
.exists(), true);
214 GlobalLogWriter
.println("Page: " + i
);
215 checkOnePicture(sJPEGFilename
, _sResult
, _aParams
);
221 GlobalLogWriter
.println("The document '" + sSectionName
+ "' seems to have no picture representation.");
224 String sResultIniFile
= FileHelper
.appendPath(_sResult
, sSectionName
);
225 evaluateResult(sResultIniFile
, _aParams
);
228 private void evaluateResult(String _sDocument
, ParameterHelper _aParams
)
230 String sResultIniFile
= _sDocument
+ ".ini";
231 File aFile
= new File(sResultIniFile
);
232 assure("Result file doesn't exists " + sResultIniFile
, aFile
.exists());
237 int ok_status
= 1; // 1=ok 2=bad 3=ugly
239 IniFile aResultIniFile
= new IniFile(sResultIniFile
);
240 int nPages
= aResultIniFile
.getIntValue("global", "pages", 0);
241 for (int i
= 0; i
< nPages
; i
++)
243 String sCurrentPage
= "page" + String
.valueOf(i
+ 1);
244 int nPercent
= aResultIniFile
.getIntValue(sCurrentPage
, "percent", -1);
249 else if (nPercent
<= 5)
261 assure("Error: document doesn't contains pages", nPages
> 0);
263 // TODO: this information has to come out of the ini files
264 String sStatusRunThrough
= "PASSED, ";
265 String sPassed
= "OK";
267 String sStatusMessage
= "From " + nPages
+ " page(s) are: ";
274 sGood
= " good:=" + good
;
275 sStatusMessage
+= sGood
;
279 sBad
= " bad:=" + bad
;
280 sStatusMessage
+= sBad
;
284 sUgly
= " ugly:=" + ugly
;
285 sStatusMessage
+= sUgly
;
302 sPassed
= "NEED A LOOK";
309 sStatusRunThrough
+= sPassed
;
310 aResultIniFile
.insertValue("global", "state", sStatusRunThrough
);
311 aResultIniFile
.insertValue("global", "info", sStatusMessage
);
312 aResultIniFile
.close();
314 _aParams
.getTestParameters().put("current_state", sStatusRunThrough
);
315 _aParams
.getTestParameters().put("current_info", sStatusMessage
);
316 _aParams
.getTestParameters().put("current_ok_status", ok_status
);
318 // if we have a ugly page, we must return this as a FAILED STATUS in Log file!
319 assure("There exist pages marked as ugly.", ugly
== 0);
322 private void checkOnePicture(String _sDocumentName
, String _sResult
, ParameterHelper _aParams
)
324 GlobalLogWriter
.println("JPEG: Compare difference between '" + _sDocumentName
+ "' and '" + _sResult
+ "'");
325 File aResultFile
= new File(_sResult
);
326 if (aResultFile
.isDirectory())
328 // result is just a directory, so we search for the basename of the source and take this.
329 String sBasename
= FileHelper
.getBasename(_sDocumentName
);
330 String sResultFilename
= FileHelper
.appendPath(_sResult
, sBasename
);
331 aResultFile
= new File(sResultFilename
);
332 if (aResultFile
.exists())
334 // Original and Result exists
335 String sInputPath
= _aParams
.getInputPath();
336 if (sInputPath
.toLowerCase().endsWith("index.ini"))
339 // we want to get the buildid from the info file.
342 compareJPEG(_sDocumentName
, sResultFilename
, _aParams
);
347 String sResultFilenamePDF
= util
.utils
.replaceAll13(sResultFilename
, ".ps_", ".pdf_");
348 File aResultPDFFile
= new File(sResultFilenamePDF
);
349 if (aResultPDFFile
.exists())
351 // Original and Result exists
352 String sInputPath
= _aParams
.getInputPath();
353 if (sInputPath
.toLowerCase().endsWith("index.ini"))
356 // we want to get the buildid from the info file.
359 compareJPEG(_sDocumentName
, sResultFilenamePDF
, _aParams
);
363 GlobalLogWriter
.println("Warning: Result JPEG doesn't exists '" + sResultFilename
+ "'");
369 // result is also a file
370 if (aResultFile
.exists())
372 compareJPEG(_sDocumentName
, _sResult
, _aParams
);
376 GlobalLogWriter
.println("Warning: Result JPEG doesn't exists '" + _sResult
+ "'");
382 * compare 2 JPEGs, it is a need, that both _sDocumentName and _sResultFilename exist.
383 * @param _sDocumentName
387 private void compareJPEG(String _sDocumentName
, String _sResult
, ParameterHelper _aParams
)
389 NameDPIPage aNameDPIPage
= NameDPIPage
.interpret(_sDocumentName
);
391 String sSourceBasename
= FileHelper
.getBasename(_sDocumentName
);
392 String sSourcePath
= FileHelper
.getPath(_sDocumentName
);
393 String sDestinationBasename
= FileHelper
.getBasename(_sResult
);
394 String sDestinationPath
= FileHelper
.getPath(_sResult
);
396 if (!sSourcePath
.equals(sDestinationPath
))
398 // we want to have all in one Directory, Original, Reference and the Difference result.
399 // copy the original file to the reference path
400 String sNewSourceBasename
= "Original_" + sSourceBasename
;
401 String sSource
= _sDocumentName
;
402 String sDestination
= FileHelper
.appendPath(sDestinationPath
, sNewSourceBasename
);
403 FileHelper
.copy(sSource
, sDestination
);
404 sSourceBasename
= sNewSourceBasename
;
406 JPEGCreator
.convertToNearSameFileWithWidth340(sDestination
);
408 String sDifferenceBasename
= "Difference_between_" + FileHelper
.getNameNoSuffix(sSourceBasename
) + "_and_" + FileHelper
.getNameNoSuffix(sDestinationBasename
) + ".jpg";
410 String sSource
= FileHelper
.appendPath(sDestinationPath
, sSourceBasename
);
411 String sDestination
= FileHelper
.appendPath(sDestinationPath
, sDestinationBasename
);
412 String sDifference
= FileHelper
.appendPath(sDestinationPath
, sDifferenceBasename
);
413 int nErr
= compareJPEG(sSource
, sDestination
, sDifference
);
414 if (nErr
== 0 && FileHelper
.exists(sDifference
))
416 // check the difference, returns the count of different colors
417 // this means, 1=only one color, no differences found.
418 int nResult
= identify(sDifference
);
419 int nPercentColorDiffer
= 0;
421 String sResult
= "YES";
423 if (m_aTolerance
!= null)
425 final int nAcceptedTolerance
= m_aTolerance
.getAccept();
426 if (nResult
<= nAcceptedTolerance
)
429 sResult
= "IN TOLERANCE";
430 GlobalLogWriter
.println("The differences are in tolerance.");
439 nPercentColorDiffer
= estimateGfx(sSource
, sDestination
, sDifference
);
441 catch (java
.io
.IOException e
)
443 GlobalLogWriter
.println("Can't estimate the different colors. " + e
.getMessage());
447 // store the result in a result.ini file
448 String sResultFile
= FileHelper
.appendPath(sDestinationPath
, aNameDPIPage
.Name
+ ".ini");
449 int nPage
= aNameDPIPage
.Page
;
454 IniFile aResultIni
= new IniFile(sResultFile
);
458 "; This file is automatically created by a graphical.JPEGComparator run",
460 "; If you see this file in a browser you may have forgotten to set the follows in the property file",
461 "; " + PropertyName
.DOC_COMPARATOR_HTML_OUTPUT_PREFIX
+ "=http://<computer>/gfxcmp_ui/cw.php?inifile=",
462 "; Please check the documentation if you got confused.",
466 aResultIni
.insertFirstComment(aComment
);
468 // write down the global flags
469 int nMaxPage
= Math
.max(nPage
, aResultIni
.getIntValue("global", "pages", 0));
470 aResultIni
.insertValue("global", "pages", nMaxPage
);
472 String sRefBuildId
= (String
) _aParams
.getTestParameters().get("RefBuildId");
473 if (sRefBuildId
== null)
477 aResultIni
.insertValue("global", "refbuildid", sRefBuildId
);
479 aResultIni
.insertValue("global", "diffdiff", "no");
480 aResultIni
.insertValue("global", "basename", aNameDPIPage
.Name
);
481 aResultIni
.insertValue("global", "dpi", aNameDPIPage
.DPI
);
483 // write down flags for each page
484 String sSection
= "page" + String
.valueOf(nPage
);
486 aResultIni
.insertValue(sSection
, "oldgfx", sSource
);
487 aResultIni
.insertValue(sSection
, "newgfx", sDestination
);
488 aResultIni
.insertValue(sSection
, "diffgfx", sDifference
);
489 aResultIni
.insertValue(sSection
, "percent", nPercentColorDiffer
);
490 aResultIni
.insertValue(sSection
, "BM", "false");
491 aResultIni
.insertValue(sSection
, "result", sResult
);
498 * count how much pixel differ and between Old or New and the Difference graphics
500 * First, count the old graphics, then the new graphics due to the fact both should be equal
501 * it should be legal to take result from old or new. We take the graphics with less values.
503 * Second, count the difference graphics, now take the percent algorithm and
504 * build a percent value, which contain the number of different pixels as a percent value
507 * 0% there is no difference
509 * <100% Take a look into the difference graphics, maybe the difference graphics shows
510 * text like outlined or the text is little bit move left, right up or down.
512 * >>100% Yes it's possible that there is a difference more then 100%, maybe a font problem
513 * between old and new graphics. The font of the new graphics is little bit bigger,
514 * so the pixel count between old graphics and new graphics is twice the more.
516 * @param _sOldGfx path & name to the jpeg file (1)
517 * @param _sNewGfx path & name to the other jpeg file (2)
518 * @param _sDiffGfx path & name to the new difference file which shows the difference between (1) and (2)
519 * @return the count of different pixels
520 * @throws java.io.IOException if file access is not possible
522 public static int estimateGfx(String _sOldGfx
, String _sNewGfx
, String _sDiffGfx
)
523 throws java
.io
.IOException
525 TimeHelper a
= new TimeHelper();
528 final int nNotWhiteCount_OldGraphic
= PixelCounter
.countNotWhitePixelsFromImage(_sOldGfx
);
529 final int nNotWhiteCount_NewGraphic
= PixelCounter
.countNotWhitePixelsFromImage(_sNewGfx
);
530 final int nNotBlackCount_DiffGraphic
= PixelCounter
.countNotBlackPixelsFromImage(_sDiffGfx
);
533 GlobalLogWriter
.println("Thread Time is: " + a
.getTime());
535 int nMinNotWhiteCount
= Math
.min(nNotWhiteCount_NewGraphic
, nNotWhiteCount_OldGraphic
);
538 if (nMinNotWhiteCount
== 0)
540 nMinNotWhiteCount
= Math
.max(nNotWhiteCount_NewGraphic
, nNotWhiteCount_OldGraphic
);
541 if (nMinNotWhiteCount
== 0)
543 nMinNotWhiteCount
= 1;
547 int nPercent
= Math
.abs(nNotBlackCount_DiffGraphic
* 100 / nMinNotWhiteCount
);
548 GlobalLogWriter
.println("Graphics check, pixel based:" + String
.valueOf(nPercent
) + "% pixel differ ");
552 private static int compareJPEG(String _sOldGfx
, String _sNewGfx
, String _sDiffGfx
)
554 String sComposite
= "composite";
555 if (OSHelper
.isWindows())
557 sComposite
= "composite.exe";
558 String sIMPath
= (String
) param
.get("imagemagick.path");
561 sComposite
= FileHelper
.appendPath(sIMPath
, sComposite
);
565 String
[] sCommandArray
=
575 ProcessHandler aHandler
= new ProcessHandler(sCommandArray
);
576 aHandler
.executeSynchronously();
577 int nExitCode
= aHandler
.getExitCode();
580 GlobalLogWriter
.println("'" + sComposite
+ "' return with ");
581 String sBack
= aHandler
.getOutputText();
582 GlobalLogWriter
.println("'" + sBack
+ "'");
586 // creates an extra smaller difference picture
587 File aDiffFile
= new File(_sDiffGfx
);
588 if (aDiffFile
.exists())
590 JPEGCreator
.convertToNearSameFileWithWidth340(_sDiffGfx
);
597 * wrapper for ImageMagick identify,
598 * function checks how many different colors a picture contains.
599 * if it's only one color (nResult==1), like background color, there is no difference.
601 int identify(String _sDiffGfx
)
604 // would like to know what the meaning of %k is for ImageMagick's 'identify'
605 String sIM_Format
= "%k";
607 String sIdentify
= "identify";
608 if (OSHelper
.isWindows())
610 sIdentify
= "identify.exe";
611 String sIMPath
= (String
) param
.get("imagemagick.path");
614 sIdentify
= FileHelper
.appendPath(sIMPath
, sIdentify
);
618 String
[] sCommandArray
=
625 ProcessHandler aHandler
= new ProcessHandler(sCommandArray
);
626 aHandler
.executeSynchronously();
627 aHandler
.getExitCode();
629 String sBack
= aHandler
.getOutputText();
630 GlobalLogWriter
.println("'" + sBack
+ "'");
632 // try to interpret the result, which we get as a String
635 int nIdx
= sBack
.indexOf("\n");
638 sBack
= sBack
.substring(0, nIdx
);
641 nResult
= Integer
.valueOf(sBack
).intValue();
643 catch (java
.lang
.NumberFormatException e
)
645 GlobalLogWriter
.println("identify(): Number format exception");