[poppler] Branch 'xpdf303merge' - 11 commits - CMakeLists.txt configure.ac cpp/Doxyfile glib/CMakeLists.txt glib/Makefile.am goo/PNGWriter.cc NEWS poppler/OptionalContent.cc poppler/OptionalContent.h qt4/src regtest/backends regtest/commands regtest/Config.py regtest/main.py regtest/poppler-regtest regtest/TestReferences.py regtest/TestRun.py regtest/Timer.py regtest/Utils.py

Carlos Garcia Campos carlosgc at kemper.freedesktop.org
Sun Sep 18 02:36:54 PDT 2011


 CMakeLists.txt                  |    2 
 NEWS                            |    5 
 configure.ac                    |    2 
 cpp/Doxyfile                    |    2 
 glib/CMakeLists.txt             |   12 +-
 glib/Makefile.am                |   12 +-
 goo/PNGWriter.cc                |    9 +
 poppler/OptionalContent.cc      |  113 ++++++++++++++++----
 poppler/OptionalContent.h       |    3 
 qt4/src/Doxyfile                |    2 
 regtest/Config.py               |   32 +++++
 regtest/TestReferences.py       |   74 +++++++++++++
 regtest/TestRun.py              |  160 +++++++++++++++++++++++++++++
 regtest/Timer.py                |   73 +++++++++++++
 regtest/Utils.py                |   55 ++++++++++
 regtest/backends/__init__.py    |  220 ++++++++++++++++++++++++++++++++++++++++
 regtest/backends/cairo.py       |   39 +++++++
 regtest/backends/postscript.py  |   35 ++++++
 regtest/backends/splash.py      |   39 +++++++
 regtest/backends/text.py        |   48 ++++++++
 regtest/commands/__init__.py    |   93 ++++++++++++++++
 regtest/commands/create-refs.py |   65 +++++++++++
 regtest/commands/run-tests.py   |   69 ++++++++++++
 regtest/main.py                 |   81 ++++++++++++++
 regtest/poppler-regtest         |    6 +
 25 files changed, 1209 insertions(+), 42 deletions(-)

New commits:
commit d737e3098e02e46525c0edf2165462e03ac7a0e6
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Sep 18 11:36:11 2011 +0200

    xpdf303: Add support for visibility expressions in OptContent

diff --git a/poppler/OptionalContent.cc b/poppler/OptionalContent.cc
index b5e19eb..c564d06 100644
--- a/poppler/OptionalContent.cc
+++ b/poppler/OptionalContent.cc
@@ -25,6 +25,10 @@
 // #include "PDFDocEncoding.h"
 #include "OptionalContent.h"
 
+// Max depth of nested visibility expressions.  This is used to catch
+// infinite loops in the visibility expression object structure.
+#define visibilityExprRecursionLimit 50
+
 //------------------------------------------------------------------------
 
 OCGs::OCGs(Object *ocgObject, XRef *xref) :
@@ -174,6 +178,7 @@ bool OCGs::optContentIsVisible( Object *dictRef )
   Object dictType;
   Object ocg;
   Object policy;
+  Object ve;
   bool result = true;
   dictRef->fetch( m_xref, &dictObj );
   if ( ! dictObj.isDict() ) {
@@ -185,32 +190,34 @@ bool OCGs::optContentIsVisible( Object *dictRef )
   // printf("checking if optContent is visible\n");
   dict->lookup("Type", &dictType);
   if (dictType.isName("OCMD")) {
-    // If we supported Visibility Expressions, we'd check
-    // for a VE entry, and then call out to the parser here...
-    // printf("found OCMD dict\n");
-    dict->lookup("P", &policy);
-    dict->lookupNF("OCGs", &ocg);
-    if (ocg.isArray()) {
-      if (policy.isName("AllOn")) {
-	result = allOn( ocg.getArray() );
-      } else if (policy.isName("AllOff")) {
-	result = allOff( ocg.getArray() );
-      } else if (policy.isName("AnyOff")) {
-	result = anyOff( ocg.getArray() );
-      } else if ( (!policy.isName()) || (policy.isName("AnyOn") ) ) {
-	// this is the default
-	result = anyOn( ocg.getArray() );
-      }
-    } else if (ocg.isRef()) {
-      OptionalContentGroup* oc = findOcgByRef( ocg.getRef() );      
-      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
-	result = false;
-      } else {
-	result = true ;
+    if (dict->lookup("VE", &ve)->isArray()) {
+      result = evalOCVisibilityExpr(&ve, 0);
+    } else {
+      dict->lookupNF("OCGs", &ocg);
+      if (ocg.isArray()) {
+        dict->lookup("P", &policy);
+        if (policy.isName("AllOn")) {
+          result = allOn( ocg.getArray() );
+        } else if (policy.isName("AllOff")) {
+          result = allOff( ocg.getArray() );
+        } else if (policy.isName("AnyOff")) {
+          result = anyOff( ocg.getArray() );
+        } else if ( (!policy.isName()) || (policy.isName("AnyOn") ) ) {
+          // this is the default
+          result = anyOn( ocg.getArray() );
+        }
+        policy.free();
+      } else if (ocg.isRef()) {
+        OptionalContentGroup *oc = findOcgByRef( ocg.getRef() );
+        if ( oc && oc->getState() == OptionalContentGroup::Off ) {
+          result = false;
+        } else {
+          result = true ;
+        }
       }
+      ocg.free();
     }
-    ocg.free();
-    policy.free();
+    ve.free();
   } else if ( dictType.isName("OCG") ) {
     OptionalContentGroup* oc = findOcgByRef( dictRef->getRef() );
     if ( oc && oc->getState() == OptionalContentGroup::Off ) {
@@ -223,6 +230,64 @@ bool OCGs::optContentIsVisible( Object *dictRef )
   return result;
 }
 
+GBool OCGs::evalOCVisibilityExpr(Object *expr, int recursion) {
+  OptionalContentGroup *ocg;
+  Object expr2, op, obj;
+  GBool ret;
+  int i;
+
+  if (recursion > visibilityExprRecursionLimit) {
+    error(errSyntaxError, -1,
+	  "Loop detected in optional content visibility expression");
+    return gTrue;
+  }
+  if (expr->isRef()) {
+    if ((ocg = findOcgByRef(expr->getRef()))) {
+      return ocg->getState();
+    }
+  }
+  expr->fetch(m_xref, &expr2);
+  if (!expr2.isArray() || expr2.arrayGetLength() < 1) {
+    error(errSyntaxError, -1,
+	  "Invalid optional content visibility expression");
+    expr2.free();
+    return gTrue;
+  }
+  expr2.arrayGet(0, &op);
+  if (op.isName("Not")) {
+    if (expr2.arrayGetLength() == 2) {
+      expr2.arrayGetNF(1, &obj);
+      ret = !evalOCVisibilityExpr(&obj, recursion + 1);
+      obj.free();
+    } else {
+      error(errSyntaxError, -1,
+	    "Invalid optional content visibility expression");
+      ret = gTrue;
+    }
+  } else if (op.isName("And")) {
+    ret = gTrue;
+    for (i = 1; i < expr2.arrayGetLength() && ret; ++i) {
+      expr2.arrayGetNF(i, &obj);
+      ret = evalOCVisibilityExpr(&obj, recursion + 1);
+      obj.free();
+    }
+  } else if (op.isName("Or")) {
+    ret = gFalse;
+    for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) {
+      expr2.arrayGetNF(i, &obj);
+      ret = evalOCVisibilityExpr(&obj, recursion + 1);
+      obj.free();
+    }
+  } else {
+    error(errSyntaxError, -1,
+	  "Invalid optional content visibility expression");
+    ret = gTrue;
+  }
+  op.free();
+  expr2.free();
+  return ret;
+}
+
 bool OCGs::allOn( Array *ocgArray )
 {
   for (int i = 0; i < ocgArray->getLength(); ++i) {
diff --git a/poppler/OptionalContent.h b/poppler/OptionalContent.h
index 7add4a3..9a5edac 100644
--- a/poppler/OptionalContent.h
+++ b/poppler/OptionalContent.h
@@ -50,7 +50,8 @@ public:
 
 private:
   GBool ok;
-  
+
+  GBool evalOCVisibilityExpr(Object *expr, int recursion);
   bool allOn( Array *ocgArray );
   bool allOff( Array *ocgArray );
   bool anyOn( Array *ocgArray );
commit 258d56ba85902a8ab50eec3fe66dd6425226fa59
Merge: 72a7736... 12c6239...
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Sep 18 11:12:38 2011 +0200

    Merge branch 'master' into xpdf303merge

commit 12c6239ca4f1f6a5de72aebd755f9d1354ea5837
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Sep 18 11:08:06 2011 +0200

    regtest: Fix checksum check for tests containing spaces in filename

diff --git a/regtest/backends/__init__.py b/regtest/backends/__init__.py
index ed728ff..de8b3ec 100644
--- a/regtest/backends/__init__.py
+++ b/regtest/backends/__init__.py
@@ -62,7 +62,7 @@ class Backend:
         tests = os.listdir(out_path)
 
         for line in md5_file.readlines():
-            md5sum, ref_path = line.strip('\n').split(' ')
+            md5sum, ref_path = line.strip('\n').split(' ', 1)
             basename = os.path.basename(ref_path)
             if not basename in tests:
                 retval = False
commit 93ba92db0a3af35f197a4faa5a528af98ea0f624
Author: Albert Astals Cid <aacid at kde.org>
Date:   Wed Sep 14 00:58:44 2011 +0200

    Make it work with Python3

diff --git a/regtest/Config.py b/regtest/Config.py
index 197edce..ff03e3c 100644
--- a/regtest/Config.py
+++ b/regtest/Config.py
@@ -27,6 +27,6 @@ class Config:
 
 if __name__ == '__main__':
     c = Config({'foo' : 25})
-    print c.foo
+    print(c.foo)
     cc = Config()
-    print cc.foo
+    print(cc.foo)
diff --git a/regtest/TestReferences.py b/regtest/TestReferences.py
index 042b0d4..cfd4853 100644
--- a/regtest/TestReferences.py
+++ b/regtest/TestReferences.py
@@ -32,7 +32,7 @@ class TestReferences:
 
         try:
             os.makedirs(self._refsdir)
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
         except:
@@ -46,7 +46,7 @@ class TestReferences:
         refs_path = os.path.join(self._refsdir, filename)
         try:
             os.makedirs(refs_path)
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
         except:
@@ -60,9 +60,9 @@ class TestReferences:
 
         for backend in backends:
             if not self.config.force and backend.has_md5(refs_path):
-                print "Checksum file found, skipping '%s' for %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs)
+                print("Checksum file found, skipping '%s' for %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
                 continue
-            print "Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs)
+            print("Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
             if backend.create_refs(doc_path, refs_path):
                 backend.create_checksums(refs_path, self.config.checksums_only)
 
diff --git a/regtest/TestRun.py b/regtest/TestRun.py
index ba74b1a..f03840b 100644
--- a/regtest/TestRun.py
+++ b/regtest/TestRun.py
@@ -42,7 +42,7 @@ class TestRun:
 
         try:
             os.makedirs(self._outdir);
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
         except:
@@ -54,7 +54,7 @@ class TestRun:
         ref_is_crashed = backend.is_crashed(refs_path)
         ref_is_failed = backend.is_failed(refs_path)
         if not ref_has_md5 and not ref_is_crashed and not ref_is_failed:
-            print "Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name())
+            print("Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name()))
             return
 
         self._n_tests += 1
@@ -68,40 +68,40 @@ class TestRun:
         if ref_has_md5 and test_has_md5:
             if backend.compare_checksums(refs_path, test_path, not self.config.keep_results, self.config.create_diffs):
                 # FIXME: remove dir if it's empty?
-                print "PASS"
+                print("PASS")
                 self._n_passed += 1
             else:
-                print "FAIL"
+                print("FAIL")
                 self._failed.append("%s (%s)" % (doc_path, backend.get_name()))
             return
         elif test_has_md5:
             if ref_is_crashed:
-                print "DOES NOT CRASH"
+                print("DOES NOT CRASH")
             elif ref_is_failed:
-                print "DOES NOT FAIL"
+                print("DOES NOT FAIL")
 
             return
 
         test_is_crashed = backend.is_crashed(test_path)
         if ref_is_crashed and test_is_crashed:
-            print "PASS (Expected crash)"
+            print("PASS (Expected crash)")
             self._n_passed += 1
             return
 
         test_is_failed = backend.is_failed(test_path)
         if ref_is_failed and test_is_failed:
             # FIXME: compare status errors
-            print "PASS (Expected fail with status error %d)" % (test_is_failed)
+            print("PASS (Expected fail with status error %d)" % (test_is_failed))
             self._n_passed += 1
             return
 
         if test_is_crashed:
-            print "CRASH"
+            print("CRASH")
             self._crashed.append("%s (%s)" % (doc_path, backend.get_name()))
             return
 
         if test_is_failed:
-            print "FAIL (status error %d)" % (test_is_failed)
+            print("FAIL (status error %d)" % (test_is_failed))
             self._failed_status_error("%s (%s)" % (doc_path, backend.get_name()))
             return
 
@@ -113,7 +113,7 @@ class TestRun:
         out_path = os.path.join(self._outdir, filename)
         try:
             os.makedirs(out_path)
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
         except:
@@ -122,7 +122,7 @@ class TestRun:
         refs_path = os.path.join(self._refsdir, filename)
 
         if not os.path.isdir(refs_path):
-            print "Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs)
+            print("Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs))
             return
 
         if self.config.backends:
@@ -142,16 +142,16 @@ class TestRun:
 
     def summary(self):
         if not self._n_tests:
-            print "No tests run"
+            print("No tests run")
             return
 
-        print "Total %d tests" % (self._n_tests)
-        print "%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_tests)
+        print("Total %d tests" % (self._n_tests))
+        print("%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_tests))
         def report_tests(test_list, test_type):
             n_tests = len(test_list)
             if not n_tests:
                 return
-            print "%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_tests, ", ".join(test_list))
+            print("%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_tests, ", ".join(test_list)))
         report_tests(self._failed, "failed")
         report_tests(self._crashed, "crashed")
         report_tests(self._failed_status_error, "failed to run")
diff --git a/regtest/Timer.py b/regtest/Timer.py
index 102af96..e86f8c5 100644
--- a/regtest/Timer.py
+++ b/regtest/Timer.py
@@ -61,13 +61,13 @@ if __name__ == '__main__':
 
     t = Timer()
     sleep(5)
-    print "Elapsed: %s" % (t.elapsed_str())
+    print("Elapsed: %s" % (t.elapsed_str()))
     sleep(1)
-    print "Elapsed: %s" % (t.elapsed_str())
+    print("Elapsed: %s" % (t.elapsed_str()))
 
     t.start()
     sleep(2)
     t.stop()
-    print "Elapsed: %s" % (t.elapsed_str())
+    print("Elapsed: %s" % (t.elapsed_str()))
     sleep(2)
-    print "Elapsed: %s" % (t.elapsed_str())
+    print("Elapsed: %s" % (t.elapsed_str()))
diff --git a/regtest/backends/__init__.py b/regtest/backends/__init__.py
index e730eaa..ed728ff 100644
--- a/regtest/backends/__init__.py
+++ b/regtest/backends/__init__.py
@@ -46,7 +46,7 @@ class Backend:
             if not entry.startswith(self._name) or entry.endswith('.md5'):
                 continue
             ref_path = os.path.join(refs_path, entry)
-            f = open(ref_path, 'r')
+            f = open(ref_path, 'rb')
             md5_file.write("%s %s\n" % (md5(f.read()).hexdigest(), ref_path))
             f.close()
             if delete_refs:
@@ -66,21 +66,21 @@ class Backend:
             basename = os.path.basename(ref_path)
             if not basename in tests:
                 retval = False
-                print "%s found in md5 ref file but missing in output dir %s" % (basename, out_path)
+                print("%s found in md5 ref file but missing in output dir %s" % (basename, out_path))
                 continue
 
             result_path = os.path.join(out_path, basename)
-            f = open(result_path, 'r')
+            f = open(result_path, 'rb')
             matched = md5sum == md5(f.read()).hexdigest()
             f.close()
             if matched:
                 if remove_results:
                     os.remove(result_path)
             else:
-                print "Differences found in %s" % (basename)
+                print("Differences found in %s" % (basename))
                 if create_diffs:
                     if not os.path.exists(ref_path):
-                        print "Reference file %s not found, skipping diff for %s" % (ref_path, result_path)
+                        print("Reference file %s not found, skipping diff for %s" % (ref_path, result_path))
                     else:
                         try:
                             self._create_diff(ref_path, result_path)
@@ -115,7 +115,7 @@ class Backend:
     def __create_stderr_file(self, stderr, out_path):
         if not stderr:
             return
-        stderr_file = open(out_path + '.stderr', 'w')
+        stderr_file = open(out_path + '.stderr', 'wb')
         stderr_file.write(stderr)
         stderr_file.close()
 
diff --git a/regtest/commands/__init__.py b/regtest/commands/__init__.py
index 93cec6c..86f58fd 100644
--- a/regtest/commands/__init__.py
+++ b/regtest/commands/__init__.py
@@ -83,11 +83,11 @@ def print_help():
         except ImportError:
             pass
 
-    print "Commands are:"
+    print("Commands are:")
     commands = [(x.name, x.description) for x in _commands.values()]
     commands.sort()
     for name, description in commands:
-        print "  %-15s %s" % (name, description)
+        print("  %-15s %s" % (name, description))
 
     print
-    print "For more information run 'poppler-regtest --help-command <command>'"
+    print("For more information run 'poppler-regtest --help-command <command>'")
diff --git a/regtest/commands/create-refs.py b/regtest/commands/create-refs.py
index 081a7e8..b055703 100644
--- a/regtest/commands/create-refs.py
+++ b/regtest/commands/create-refs.py
@@ -60,6 +60,6 @@ class CreateRefs(Command):
             refs.create_refs()
         else:
             refs.create_refs_for_file(os.path.basename(doc))
-        print "Refs created in %s" % (t.elapsed_str())
+        print("Refs created in %s" % (t.elapsed_str()))
 
 register_command('create-refs', CreateRefs)
diff --git a/regtest/commands/run-tests.py b/regtest/commands/run-tests.py
index 2c564c7..c1a8b6f 100644
--- a/regtest/commands/run-tests.py
+++ b/regtest/commands/run-tests.py
@@ -64,6 +64,6 @@ class RunTests(Command):
         else:
             tests.run_test(os.path.basename(doc))
         tests.summary()
-        print "Tests run in %s" % (t.elapsed_str())
+        print("Tests run in %s" % (t.elapsed_str()))
 
 register_command('run-tests', RunTests)
diff --git a/regtest/main.py b/regtest/main.py
index 4c8a390..a46a64c 100644
--- a/regtest/main.py
+++ b/regtest/main.py
@@ -71,7 +71,7 @@ def main(args):
         sys.stderr.write("Unknown command: %s\n" % (args[0]))
         commands.print_help()
         sys.exit(1)
-    except backends.UnknownBackendError, e:
+    except backends.UnknownBackendError as e:
         sys.stderr.write(str(e) + "\n")
         sys.stdout.write("Backends are: %s\n" % (", ".join([backend.get_name() for backend in backends.get_all_backends()])))
         sys.exit(1)
commit da1b5437148e1e6317246b16f7235c8bc280be97
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Tue Sep 13 20:09:56 2011 +0200

    regtest: Add a way to skip files
    
    A new command line option --skip has been added to give a file with the
    list of test to skip. When --skip is not used, it look for a file named
    Skipped in the tests dir. Lines starting with '#' are considered
    comments and are ignored.

diff --git a/regtest/TestReferences.py b/regtest/TestReferences.py
index 5d95e6f..042b0d4 100644
--- a/regtest/TestReferences.py
+++ b/regtest/TestReferences.py
@@ -20,13 +20,14 @@ import os
 import errno
 from backends import get_backend, get_all_backends
 from Config import Config
-from Utils import get_document_paths_from_dir
+from Utils import get_document_paths_from_dir, get_skipped_tests
 
 class TestReferences:
 
     def __init__(self, docsdir, refsdir):
         self._docsdir = docsdir
         self._refsdir = refsdir
+        self._skipped = get_skipped_tests(docsdir)
         self.config = Config()
 
         try:
@@ -38,6 +39,10 @@ class TestReferences:
             raise
 
     def create_refs_for_file(self, filename, n_doc = 1, total_docs = 1):
+        if filename in self._skipped:
+            print("Skipping test '%s' (%d/%d)" % (os.path.join(self._docsdir, filename), n_doc, total_docs))
+            return
+
         refs_path = os.path.join(self._refsdir, filename)
         try:
             os.makedirs(refs_path)
@@ -55,7 +60,7 @@ class TestReferences:
 
         for backend in backends:
             if not self.config.force and backend.has_md5(refs_path):
-                print "Checksum file found, skipping '%s' for %s backend" % (doc_path, backend.get_name())
+                print "Checksum file found, skipping '%s' for %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs)
                 continue
             print "Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs)
             if backend.create_refs(doc_path, refs_path):
diff --git a/regtest/TestRun.py b/regtest/TestRun.py
index 25e8fb7..ba74b1a 100644
--- a/regtest/TestRun.py
+++ b/regtest/TestRun.py
@@ -18,7 +18,7 @@
 
 from backends import get_backend, get_all_backends
 from Config import Config
-from Utils import get_document_paths_from_dir
+from Utils import get_document_paths_from_dir, get_skipped_tests
 import sys
 import os
 import errno
@@ -29,6 +29,7 @@ class TestRun:
         self._docsdir = docsdir
         self._refsdir = refsdir
         self._outdir = outdir
+        self._skipped = get_skipped_tests(docsdir)
         self.config = Config()
 
         # Results
@@ -105,6 +106,10 @@ class TestRun:
             return
 
     def run_test(self, filename, n_doc = 1, total_docs = 1):
+        if filename in self._skipped:
+            print("Skipping test '%s' (%d/%d)" % (os.path.join(self._docsdir, filename), n_doc, total_docs))
+            return
+
         out_path = os.path.join(self._outdir, filename)
         try:
             os.makedirs(out_path)
@@ -117,7 +122,7 @@ class TestRun:
         refs_path = os.path.join(self._refsdir, filename)
 
         if not os.path.isdir(refs_path):
-            print "Reference dir not found for %s, skipping" % (doc_path)
+            print "Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs)
             return
 
         if self.config.backends:
diff --git a/regtest/Utils.py b/regtest/Utils.py
index 6656fac..90a6eea 100644
--- a/regtest/Utils.py
+++ b/regtest/Utils.py
@@ -32,4 +32,24 @@ def get_document_paths_from_dir(docsdir):
     paths.sort()
     return paths, n_paths
 
+def get_skipped_tests(docsdir):
+    from Config import Config
+    config = Config()
+    if config.skipped_file:
+        skipped_file = config.skipped_file
+    elif os.path.exists(os.path.join(docsdir, 'Skipped')):
+        skipped_file = os.path.join(docsdir, 'Skipped')
+    else:
+        return []
+
+    skipped = []
+    f = open(skipped_file, 'r')
+    for line in f.readlines():
+        line = line.rstrip('\n \t\b\r')
+        if not line or line[0] == '#':
+            continue
+        skipped.append(line)
+    f.close()
+    return skipped
+
 
diff --git a/regtest/main.py b/regtest/main.py
index 1254cae..4c8a390 100644
--- a/regtest/main.py
+++ b/regtest/main.py
@@ -49,11 +49,15 @@ def main(args):
     parser.add_argument('--help-command', metavar = 'COMMAND',
                         action = HelpAction,
                         help = 'Show help for a given command')
-    parser.add_argument('--utils-dir', action = 'store', dest = 'utils_dir', default = os.path.abspath("../utils"),
+    parser.add_argument('--utils-dir',
+                        action = 'store', dest = 'utils_dir', default = os.path.abspath("../utils"),
                         help = 'Directory of poppler utils used for the tests')
     parser.add_argument('-b', '--backends',
                         action = ListAction, dest = 'backends',
                         help = 'List of backends that will be used (separated by comma)')
+    parser.add_argument('--skip', metavar = 'FILE',
+                        action = 'store', dest = 'skipped_file',
+                        help = 'File containing tests to skip')
 
     ns, args = parser.parse_known_args(args)
     if not args:
commit b730b2c1d9666f62f940762663c8318e64049d61
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Tue Sep 13 19:04:04 2011 +0200

    regtest: Limit the number of arguments to 1
    
    It's easier to run poppler-regtest more than once if you need to run
    different tests.

diff --git a/regtest/commands/create-refs.py b/regtest/commands/create-refs.py
index 4c5034b..081a7e8 100644
--- a/regtest/commands/create-refs.py
+++ b/regtest/commands/create-refs.py
@@ -26,7 +26,7 @@ import tempfile
 class CreateRefs(Command):
 
     name = 'create-refs'
-    usage_args = '[ options ... ] documents ... '
+    usage_args = '[ options ... ] tests '
     description = 'Create references for tests'
 
     def __init__(self):
@@ -41,7 +41,7 @@ class CreateRefs(Command):
         parser.add_argument('-c', '--checksums-only',
                             action = 'store_true', dest = 'checksums_only', default = False,
                             help = 'Leave only checksum files in references dir, other files will be deleted')
-        parser.add_argument('documents', nargs='*')
+        parser.add_argument('tests')
 
     def run(self, options):
         config = Config()
@@ -49,17 +49,17 @@ class CreateRefs(Command):
         config.checksums_only = options['checksums_only']
 
         t = Timer()
-        for doc in options['documents']:
-            if os.path.isdir(doc):
-                docs_dir = doc
-            else:
-                docs_dir = os.path.dirname(doc)
+        doc = options['tests']
+        if os.path.isdir(doc):
+            docs_dir = doc
+        else:
+            docs_dir = os.path.dirname(doc)
 
-            refs = TestReferences(docs_dir, options['refs_dir'])
-            if doc == docs_dir:
-                refs.create_refs()
-            else:
-                refs.create_refs_for_file(os.path.basename(doc))
+        refs = TestReferences(docs_dir, options['refs_dir'])
+        if doc == docs_dir:
+            refs.create_refs()
+        else:
+            refs.create_refs_for_file(os.path.basename(doc))
         print "Refs created in %s" % (t.elapsed_str())
 
 register_command('create-refs', CreateRefs)
diff --git a/regtest/commands/run-tests.py b/regtest/commands/run-tests.py
index b97b34b..2c564c7 100644
--- a/regtest/commands/run-tests.py
+++ b/regtest/commands/run-tests.py
@@ -26,7 +26,7 @@ import tempfile
 class RunTests(Command):
 
     name = 'run-tests'
-    usage_args = '[ options ... ] documents ... '
+    usage_args = '[ options ... ] tests '
     description = 'Run tests for documents'
 
     def __init__(self):
@@ -44,7 +44,7 @@ class RunTests(Command):
         parser.add_argument('--create-diffs',
                             action = 'store_true', dest = 'create_diffs', default = False,
                             help = 'Create diff files for failed tests')
-        parser.add_argument('documents', nargs='*')
+        parser.add_argument('tests')
 
     def run(self, options):
         config = Config()
@@ -52,18 +52,18 @@ class RunTests(Command):
         config.create_diffs = options['create_diffs']
 
         t = Timer()
-        for doc in options['documents']:
-            if os.path.isdir(doc):
-                docs_dir = doc
-            else:
-                docs_dir = os.path.dirname(doc)
+        doc = options['tests']
+        if os.path.isdir(doc):
+            docs_dir = doc
+        else:
+            docs_dir = os.path.dirname(doc)
 
-            tests = TestRun(docs_dir, options['refs_dir'], options['out_dir'])
-            if doc == docs_dir:
-                tests.run_tests()
-            else:
-                tests.run_test(os.path.basename(doc))
-            tests.summary()
+        tests = TestRun(docs_dir, options['refs_dir'], options['out_dir'])
+        if doc == docs_dir:
+            tests.run_tests()
+        else:
+            tests.run_test(os.path.basename(doc))
+        tests.summary()
         print "Tests run in %s" % (t.elapsed_str())
 
 register_command('run-tests', RunTests)
diff --git a/regtest/main.py b/regtest/main.py
index 5784f5a..1254cae 100644
--- a/regtest/main.py
+++ b/regtest/main.py
@@ -42,7 +42,7 @@ def main(args):
     parser = argparse.ArgumentParser(
         description = 'Poppler regression tests',
         prog = 'poppler-regtest',
-        usage = '%(prog)s [options ...] command [command-options ...] tests ...',
+        usage = '%(prog)s [options ...] command [command-options ...] tests',
         add_help = False)
     parser.add_argument('-h', '--help',
                         action = HelpAction, nargs = 0)
commit 5ce045d0358318859c844340c639483485b69c58
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Tue Sep 13 18:18:21 2011 +0200

    regtest: Sort tests before create-refs/run-tests and show progress

diff --git a/regtest/TestReferences.py b/regtest/TestReferences.py
index 0af3f8a..5d95e6f 100644
--- a/regtest/TestReferences.py
+++ b/regtest/TestReferences.py
@@ -20,6 +20,7 @@ import os
 import errno
 from backends import get_backend, get_all_backends
 from Config import Config
+from Utils import get_document_paths_from_dir
 
 class TestReferences:
 
@@ -36,7 +37,7 @@ class TestReferences:
         except:
             raise
 
-    def create_refs_for_file(self, filename):
+    def create_refs_for_file(self, filename, n_doc = 1, total_docs = 1):
         refs_path = os.path.join(self._refsdir, filename)
         try:
             os.makedirs(refs_path)
@@ -56,18 +57,13 @@ class TestReferences:
             if not self.config.force and backend.has_md5(refs_path):
                 print "Checksum file found, skipping '%s' for %s backend" % (doc_path, backend.get_name())
                 continue
-            print "Creating refs for '%s' using %s backend" % (doc_path, backend.get_name())
+            print "Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs)
             if backend.create_refs(doc_path, refs_path):
                 backend.create_checksums(refs_path, self.config.checksums_only)
 
     def create_refs(self):
-        for root, dirs, files in os.walk(self._docsdir, False):
-            for entry in files:
-                if not entry.lower().endswith('.pdf'):
-                    continue
-
-                test_path = os.path.join(root[len(self._docsdir):], entry)
-                self.create_refs_for_file(test_path.lstrip(os.path.sep))
-
-
-
+        docs, total_docs = get_document_paths_from_dir(self._docsdir)
+        n_doc = 0
+        for doc in docs:
+            n_doc += 1
+            self.create_refs_for_file(doc, n_doc, total_docs)
diff --git a/regtest/TestRun.py b/regtest/TestRun.py
index ffeb48d..25e8fb7 100644
--- a/regtest/TestRun.py
+++ b/regtest/TestRun.py
@@ -18,6 +18,7 @@
 
 from backends import get_backend, get_all_backends
 from Config import Config
+from Utils import get_document_paths_from_dir
 import sys
 import os
 import errno
@@ -46,7 +47,7 @@ class TestRun:
         except:
             raise
 
-    def test(self, refs_path, doc_path, test_path, backend):
+    def test(self, refs_path, doc_path, test_path, backend, n_doc, total_docs):
         # First check whether there are test results for the backend
         ref_has_md5 = backend.has_md5(refs_path)
         ref_is_crashed = backend.is_crashed(refs_path)
@@ -56,7 +57,7 @@ class TestRun:
             return
 
         self._n_tests += 1
-        sys.stdout.write("Testing '%s' using %s backend: " % (doc_path, backend.get_name()))
+        sys.stdout.write("Testing '%s' using %s backend (%d/%d): " % (doc_path, backend.get_name(), n_doc, total_docs))
         sys.stdout.flush()
         test_has_md5 = backend.create_refs(doc_path, test_path)
 
@@ -103,7 +104,7 @@ class TestRun:
             self._failed_status_error("%s (%s)" % (doc_path, backend.get_name()))
             return
 
-    def run_test(self, filename):
+    def run_test(self, filename, n_doc = 1, total_docs = 1):
         out_path = os.path.join(self._outdir, filename)
         try:
             os.makedirs(out_path)
@@ -125,16 +126,14 @@ class TestRun:
             backends = get_all_backends()
 
         for backend in backends:
-            self.test(refs_path, doc_path, out_path, backend)
+            self.test(refs_path, doc_path, out_path, backend, n_doc, total_docs)
 
     def run_tests(self):
-        for root, dirs, files in os.walk(self._docsdir, False):
-              for entry in files:
-                  if not entry.lower().endswith('.pdf'):
-                      continue
-
-                  test_path = os.path.join(root[len(self._docsdir):], entry)
-                  self.run_test(test_path.lstrip(os.path.sep))
+        docs, total_docs = get_document_paths_from_dir(self._docsdir)
+        n_doc = 0
+        for doc in docs:
+            n_doc += 1
+            self.run_test(doc, n_doc, total_docs)
 
     def summary(self):
         if not self._n_tests:
diff --git a/regtest/Utils.py b/regtest/Utils.py
new file mode 100644
index 0000000..6656fac
--- /dev/null
+++ b/regtest/Utils.py
@@ -0,0 +1,35 @@
+# Utils.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+
+def get_document_paths_from_dir(docsdir):
+    paths = []
+    n_paths = 0
+    for root, dirs, files in os.walk(docsdir, False):
+        for entry in files:
+            if not entry.lower().endswith('.pdf'):
+                continue
+
+            test_path = os.path.join(root[len(docsdir):], entry)
+            paths.append(test_path.lstrip(os.path.sep))
+            n_paths += 1
+    paths.sort()
+    return paths, n_paths
+
+
commit 10801b6faee9037af054fe74cc4a03620ea41d45
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Mon Sep 12 20:13:17 2011 +0200

    Add initial poppler regressions test program

diff --git a/regtest/Config.py b/regtest/Config.py
new file mode 100644
index 0000000..197edce
--- /dev/null
+++ b/regtest/Config.py
@@ -0,0 +1,32 @@
+# Config.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+class Config:
+
+    shared_state = {}
+
+    def __init__(self, config = None):
+        if config is not None:
+            self.__class__.shared_state = config
+        self.__dict__ = self.__class__.shared_state
+
+if __name__ == '__main__':
+    c = Config({'foo' : 25})
+    print c.foo
+    cc = Config()
+    print cc.foo
diff --git a/regtest/TestReferences.py b/regtest/TestReferences.py
new file mode 100644
index 0000000..0af3f8a
--- /dev/null
+++ b/regtest/TestReferences.py
@@ -0,0 +1,73 @@
+# TestReferences.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+import errno
+from backends import get_backend, get_all_backends
+from Config import Config
+
+class TestReferences:
+
+    def __init__(self, docsdir, refsdir):
+        self._docsdir = docsdir
+        self._refsdir = refsdir
+        self.config = Config()
+
+        try:
+            os.makedirs(self._refsdir)
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+        except:
+            raise
+
+    def create_refs_for_file(self, filename):
+        refs_path = os.path.join(self._refsdir, filename)
+        try:
+            os.makedirs(refs_path)
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+        except:
+            raise
+        doc_path = os.path.join(self._docsdir, filename)
+
+        if self.config.backends:
+            backends = [get_backend(name) for name in self.config.backends]
+        else:
+            backends = get_all_backends()
+
+        for backend in backends:
+            if not self.config.force and backend.has_md5(refs_path):
+                print "Checksum file found, skipping '%s' for %s backend" % (doc_path, backend.get_name())
+                continue
+            print "Creating refs for '%s' using %s backend" % (doc_path, backend.get_name())
+            if backend.create_refs(doc_path, refs_path):
+                backend.create_checksums(refs_path, self.config.checksums_only)
+
+    def create_refs(self):
+        for root, dirs, files in os.walk(self._docsdir, False):
+            for entry in files:
+                if not entry.lower().endswith('.pdf'):
+                    continue
+
+                test_path = os.path.join(root[len(self._docsdir):], entry)
+                self.create_refs_for_file(test_path.lstrip(os.path.sep))
+
+
+
diff --git a/regtest/TestRun.py b/regtest/TestRun.py
new file mode 100644
index 0000000..ffeb48d
--- /dev/null
+++ b/regtest/TestRun.py
@@ -0,0 +1,156 @@
+# TestRun.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from backends import get_backend, get_all_backends
+from Config import Config
+import sys
+import os
+import errno
+
+class TestRun:
+
+    def __init__(self, docsdir, refsdir, outdir):
+        self._docsdir = docsdir
+        self._refsdir = refsdir
+        self._outdir = outdir
+        self.config = Config()
+
+        # Results
+        self._n_tests = 0
+        self._n_passed = 0
+        self._failed = []
+        self._crashed = []
+        self._failed_status_error = []
+        self._stderr = []
+
+        try:
+            os.makedirs(self._outdir);
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+        except:
+            raise
+
+    def test(self, refs_path, doc_path, test_path, backend):
+        # First check whether there are test results for the backend
+        ref_has_md5 = backend.has_md5(refs_path)
+        ref_is_crashed = backend.is_crashed(refs_path)
+        ref_is_failed = backend.is_failed(refs_path)
+        if not ref_has_md5 and not ref_is_crashed and not ref_is_failed:
+            print "Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name())
+            return
+
+        self._n_tests += 1
+        sys.stdout.write("Testing '%s' using %s backend: " % (doc_path, backend.get_name()))
+        sys.stdout.flush()
+        test_has_md5 = backend.create_refs(doc_path, test_path)
+
+        if backend.has_stderr(test_path):
+            self._stderr.append("%s (%s)" % (doc_path, backend.get_name()))
+
+        if ref_has_md5 and test_has_md5:
+            if backend.compare_checksums(refs_path, test_path, not self.config.keep_results, self.config.create_diffs):
+                # FIXME: remove dir if it's empty?
+                print "PASS"
+                self._n_passed += 1
+            else:
+                print "FAIL"
+                self._failed.append("%s (%s)" % (doc_path, backend.get_name()))
+            return
+        elif test_has_md5:
+            if ref_is_crashed:
+                print "DOES NOT CRASH"
+            elif ref_is_failed:
+                print "DOES NOT FAIL"
+
+            return
+
+        test_is_crashed = backend.is_crashed(test_path)
+        if ref_is_crashed and test_is_crashed:
+            print "PASS (Expected crash)"
+            self._n_passed += 1
+            return
+
+        test_is_failed = backend.is_failed(test_path)
+        if ref_is_failed and test_is_failed:
+            # FIXME: compare status errors
+            print "PASS (Expected fail with status error %d)" % (test_is_failed)
+            self._n_passed += 1
+            return
+
+        if test_is_crashed:
+            print "CRASH"
+            self._crashed.append("%s (%s)" % (doc_path, backend.get_name()))
+            return
+
+        if test_is_failed:
+            print "FAIL (status error %d)" % (test_is_failed)
+            self._failed_status_error("%s (%s)" % (doc_path, backend.get_name()))
+            return
+
+    def run_test(self, filename):
+        out_path = os.path.join(self._outdir, filename)
+        try:
+            os.makedirs(out_path)
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+        except:
+            raise
+        doc_path = os.path.join(self._docsdir, filename)
+        refs_path = os.path.join(self._refsdir, filename)
+
+        if not os.path.isdir(refs_path):
+            print "Reference dir not found for %s, skipping" % (doc_path)
+            return
+
+        if self.config.backends:
+            backends = [get_backend(name) for name in self.config.backends]
+        else:
+            backends = get_all_backends()
+
+        for backend in backends:
+            self.test(refs_path, doc_path, out_path, backend)
+
+    def run_tests(self):
+        for root, dirs, files in os.walk(self._docsdir, False):
+              for entry in files:
+                  if not entry.lower().endswith('.pdf'):
+                      continue
+
+                  test_path = os.path.join(root[len(self._docsdir):], entry)
+                  self.run_test(test_path.lstrip(os.path.sep))
+
+    def summary(self):
+        if not self._n_tests:
+            print "No tests run"
+            return
+
+        print "Total %d tests" % (self._n_tests)
+        print "%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_tests)
+        def report_tests(test_list, test_type):
+            n_tests = len(test_list)
+            if not n_tests:
+                return
+            print "%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_tests, ", ".join(test_list))
+        report_tests(self._failed, "failed")
+        report_tests(self._crashed, "crashed")
+        report_tests(self._failed_status_error, "failed to run")
+        report_tests(self._stderr, "have stderr output")
+
+
diff --git a/regtest/Timer.py b/regtest/Timer.py
new file mode 100644
index 0000000..102af96
--- /dev/null
+++ b/regtest/Timer.py
@@ -0,0 +1,73 @@
+# Timer.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from time import time, strftime, gmtime
+
+class Timer:
+
+    def __init__(self, start = True):
+        self._stop = None
+        if start:
+            self.start()
+        else:
+            self._start = None
+
+    def start(self):
+        self._start = time()
+
+    def stop(self):
+        self._stop = time()
+
+    def elapsed(self):
+        if self._start is None:
+            return 0
+
+        if self._stop is None:
+            return time() - self._start
+
+        return self._stop - self._start
+
+    def elapsed_str(self):
+        h, m, s = [int(i) for i in strftime('%H:%M:%S', gmtime(self.elapsed())).split(':')]
+        retval = "%d seconds" % (s)
+        if h == 0 and m == 0:
+            return retval
+
+        retval = "%d minutes and %s" % (m, retval)
+        if h == 0:
+            return retval
+
+        retval = "%d hours, %s" % (h, retval)
+        return retval
+
+
+if __name__ == '__main__':
+    from time import sleep
+
+    t = Timer()
+    sleep(5)
+    print "Elapsed: %s" % (t.elapsed_str())
+    sleep(1)
+    print "Elapsed: %s" % (t.elapsed_str())
+
+    t.start()
+    sleep(2)
+    t.stop()
+    print "Elapsed: %s" % (t.elapsed_str())
+    sleep(2)
+    print "Elapsed: %s" % (t.elapsed_str())
diff --git a/regtest/backends/__init__.py b/regtest/backends/__init__.py
new file mode 100644
index 0000000..e730eaa
--- /dev/null
+++ b/regtest/backends/__init__.py
@@ -0,0 +1,220 @@
+# backends
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from hashlib import md5
+import os
+from Config import Config
+
+__all__ = [ 'register_backend',
+            'get_backend',
+            'get_all_backends',
+            'UnknownBackendError',
+            'Backend' ]
+
+class UnknownBackendError(Exception):
+    '''Unknown backend type'''
+
+class Backend:
+
+    def __init__(self, name):
+        self._name = name
+        self._utilsdir = Config().utils_dir
+
+    def get_name(self):
+        return self._name
+
+    def create_checksums(self, refs_path, delete_refs = False):
+        path = os.path.join(refs_path, self._name)
+        md5_file = open(path + '.md5', 'w')
+
+        for entry in os.listdir(refs_path):
+            if not entry.startswith(self._name) or entry.endswith('.md5'):
+                continue
+            ref_path = os.path.join(refs_path, entry)
+            f = open(ref_path, 'r')
+            md5_file.write("%s %s\n" % (md5(f.read()).hexdigest(), ref_path))
+            f.close()
+            if delete_refs:
+                os.remove(ref_path)
+
+        md5_file.close()
+
+    def compare_checksums(self, refs_path, out_path, remove_results = True, create_diffs = True):
+        retval = True
+
+        md5_path = os.path.join(refs_path, self._name)
+        md5_file = open(md5_path + '.md5', 'r')
+        tests = os.listdir(out_path)
+
+        for line in md5_file.readlines():
+            md5sum, ref_path = line.strip('\n').split(' ')
+            basename = os.path.basename(ref_path)
+            if not basename in tests:
+                retval = False
+                print "%s found in md5 ref file but missing in output dir %s" % (basename, out_path)
+                continue
+
+            result_path = os.path.join(out_path, basename)
+            f = open(result_path, 'r')
+            matched = md5sum == md5(f.read()).hexdigest()
+            f.close()
+            if matched:
+                if remove_results:
+                    os.remove(result_path)
+            else:
+                print "Differences found in %s" % (basename)
+                if create_diffs:
+                    if not os.path.exists(ref_path):
+                        print "Reference file %s not found, skipping diff for %s" % (ref_path, result_path)
+                    else:
+                        try:
+                            self._create_diff(ref_path, result_path)
+                        except NotImplementedError:
+                            # Diff not supported by backend
+                            pass
+                retval = False
+        md5_file.close()
+
+        return retval
+
+    def has_md5(self, test_path):
+        return os.path.exists(os.path.join(test_path, self._name + '.md5'))
+
+    def is_crashed(self, test_path):
+        return os.path.exists(os.path.join(test_path, self._name + '.crashed'))
+
+    def is_failed(self, test_path):
+        failed_path = os.path.join(test_path, self._name + '.failed')
+        if not os.path.exists(failed_path):
+            return 0
+
+        f = open(failed_path, 'r')
+        status = int(f.read())
+        f.close()
+
+        return status
+
+    def has_stderr(self, test_path):
+        return os.path.exists(os.path.join(test_path, self._name + '.stderr'))
+
+    def __create_stderr_file(self, stderr, out_path):
+        if not stderr:
+            return
+        stderr_file = open(out_path + '.stderr', 'w')
+        stderr_file.write(stderr)
+        stderr_file.close()
+
+    def __create_failed_file_if_needed(self, status, out_path):
+        if os.WIFEXITED(status) or os.WEXITSTATUS(status) == 0:
+            return False
+
+        failed_file = open(out_path + '.failed', 'w')
+        failed_file.write("%d" % (os.WEXITSTATUS(status)))
+        failed_file.close()
+
+        return True
+
+    def _check_exit_status(self, p, out_path):
+        status = p.wait()
+
+        stderr = p.stderr.read()
+        self.__create_stderr_file(stderr, out_path)
+
+        if not os.WIFEXITED(status):
+            open(out_path + '.crashed', 'w').close()
+            return False
+
+        if self.__create_failed_file_if_needed(status, out_path):
+            return False
+
+        return True
+
+    def _check_exit_status2(self, p1, p2, out_path):
+        status1 = p1.wait()
+        status2 = p2.wait()
+
+        p1_stderr = p1.stderr.read()
+        p2_stderr = p2.stderr.read()
+        if p1_stderr or p2_stderr:
+            self.__create_stderr_file(p1_stderr + p2_stderr, out_path)
+
+        if not os.WIFEXITED(status1) or not os.WIFEXITED(status2):
+            open(out_path + '.crashed', 'w').close()
+            return False
+
+        if self.__create_failed_file_if_needed(status1, out_path):
+            return False
+        if self.__create_failed_file_if_needed(status2, out_path):
+            return False
+
+        return True
+
+    def _diff_png(self, ref_path, result_path):
+        try:
+            import Image, ImageChops
+        except ImportError:
+            raise NotImplementedError
+
+        ref = Image.open(ref_path)
+        result = Image.open(result_path)
+        diff = ImageChops.difference(ref, result)
+        diff.save(result_path + '.diff', 'png')
+
+    def _create_diff(self, ref_path, result_path):
+        raise NotImplementedError
+
+    def create_refs(self, doc_path, refs_path):
+        raise NotImplementedError
+
+_backends = {}
+def register_backend(backend_name, backend_class):
+    _backends[backend_name] = backend_class
+
+def _get_backend(backend_name):
+    if backend_name not in _backends:
+        try:
+            __import__('backends.%s' % backend_name)
+        except ImportError:
+            pass
+
+    if backend_name not in _backends:
+        raise UnknownBackendError('Backend %s does not exist' % backend_name)
+
+    return _backends[backend_name]
+
+def get_backend(backend_name):
+    backend_class = _get_backend(backend_name)
+    return backend_class(backend_name)
+
+def get_all_backends():
+    backends = []
+
+    thisdir = os.path.abspath(os.path.dirname(__file__))
+    for fname in os.listdir(os.path.join(thisdir)):
+        name, ext = os.path.splitext(fname)
+        if not ext == '.py':
+            continue
+        try:
+            __import__('backends.%s' % name)
+        except ImportError:
+            continue
+
+        if name in _backends:
+            backends.append(_backends[name](name))
+
+    return backends
diff --git a/regtest/backends/cairo.py b/regtest/backends/cairo.py
new file mode 100644
index 0000000..0ec361f
--- /dev/null
+++ b/regtest/backends/cairo.py
@@ -0,0 +1,39 @@
+# cairo.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from backends import Backend, register_backend
+import subprocess
+import os
+
+class Cairo(Backend):
+
+    def __init__(self, name):
+        Backend.__init__(self, name)
+        self._pdftocairo = os.path.join(self._utilsdir, 'pdftocairo');
+
+    def create_refs(self, doc_path, refs_path):
+        out_path = os.path.join(refs_path, 'cairo')
+        p1 = subprocess.Popen([self._pdftocairo, '-cropbox', '-e', '-png', doc_path, out_path], stderr = subprocess.PIPE)
+        p2 = subprocess.Popen([self._pdftocairo, '-cropbox', '-o', '-png', doc_path, out_path], stderr = subprocess.PIPE)
+        return self._check_exit_status2(p1, p2, out_path)
+
+    def _create_diff(self, ref_path, result_path):
+        self._diff_png(ref_path, result_path)
+
+register_backend('cairo', Cairo)
+
diff --git a/regtest/backends/postscript.py b/regtest/backends/postscript.py
new file mode 100644
index 0000000..6236002
--- /dev/null
+++ b/regtest/backends/postscript.py
@@ -0,0 +1,35 @@
+# postscript.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from backends import Backend, register_backend
+import subprocess
+import os
+
+class PostScript(Backend):
+
+    def __init__(self, name):
+        Backend.__init__(self, name)
+        self._pdftops = os.path.join(self._utilsdir, 'pdftops');
+
+    def create_refs(self, doc_path, refs_path):
+        out_path = os.path.join(refs_path, 'postscript')
+        p = subprocess.Popen([self._pdftops, doc_path, out_path + '.ps'], stderr = subprocess.PIPE)
+        return self._check_exit_status(p, out_path)
+
+register_backend('postscript', PostScript)
+
diff --git a/regtest/backends/splash.py b/regtest/backends/splash.py
new file mode 100644
index 0000000..bc5e448
--- /dev/null
+++ b/regtest/backends/splash.py
@@ -0,0 +1,39 @@
+# splash.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from backends import Backend, register_backend
+import subprocess
+import os
+
+class Splash(Backend):
+
+    def __init__(self, name):
+        Backend.__init__(self, name)
+        self._pdftoppm = os.path.join(self._utilsdir, 'pdftoppm');
+
+    def create_refs(self, doc_path, refs_path):
+        out_path = os.path.join(refs_path, 'splash')
+        p1 = subprocess.Popen([self._pdftoppm, '-cropbox', '-e', '-png', doc_path, out_path], stderr = subprocess.PIPE)
+        p2 = subprocess.Popen([self._pdftoppm, '-cropbox', '-o', '-png', doc_path, out_path], stderr = subprocess.PIPE)
+        return self._check_exit_status2(p1, p2, out_path)
+
+    def _create_diff(self, ref_path, result_path):
+        self._diff_png(ref_path, result_path)
+
+register_backend('splash', Splash)
+
diff --git a/regtest/backends/text.py b/regtest/backends/text.py
new file mode 100644
index 0000000..14e7c03
--- /dev/null
+++ b/regtest/backends/text.py
@@ -0,0 +1,48 @@
+# text.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from backends import Backend, register_backend
+import subprocess
+import os
+
+class Text(Backend):
+
+    def __init__(self, name):
+        Backend.__init__(self, name)
+        self._pdftotext = os.path.join(self._utilsdir, 'pdftotext');
+
+    def create_refs(self, doc_path, refs_path):
+        out_path = os.path.join(refs_path, 'text')
+        p = subprocess.Popen([self._pdftotext, doc_path, out_path + '.txt'], stderr = subprocess.PIPE)
+        return self._check_exit_status(p, out_path)
+
+    def _create_diff(self, ref_path, result_path):
+        import difflib
+
+        ref = open(ref_path, 'r')
+        result = open(result_path, 'r')
+        diff = difflib.unified_diff(ref.readlines(), result.readlines(), ref_path, result_path)
+        ref.close()
+        result.close()
+
+        diff_file = open(result_path + '.diff', 'w')
+        diff_file.writelines(diff)
+        diff_file.close()
+
+register_backend('text', Text)
+
diff --git a/regtest/commands/__init__.py b/regtest/commands/__init__.py
new file mode 100644
index 0000000..93cec6c
--- /dev/null
+++ b/regtest/commands/__init__.py
@@ -0,0 +1,93 @@
+# commands
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import argparse
+
+__all__ = [ 'register_command',
+            'print_help',
+            'run',
+            'UnknownCommandError',
+            'Command' ]
+
+class UnknownCommandError(Exception):
+    '''Unknown command'''
+
+class Command:
+
+    name = None
+    usage_args = '[ options ... ]'
+    description = None
+
+    def __init__(self):
+        self._parser = argparse.ArgumentParser(
+            description = self.description,
+            prog = 'poppler-regtest %s' % (self.name),
+            usage = 'poppler-regtest %s %s' % (self.name, self.usage_args))
+
+    def _get_args_parser(self):
+        return self._parser
+
+    def execute(self, args):
+        ns = self._parser.parse_args(args)
+        self.run(vars(ns))
+
+    def run(self, options):
+        raise NotImplementedError
+
+_commands = {}
+def register_command(command_name, command_class):
+    _commands[command_name] = command_class
+
+def _get_command(command_name):
+    if command_name not in _commands:
+        try:
+            __import__('commands.%s' % command_name)
+        except ImportError:
+            pass
+
+    if command_name not in _commands:
+        raise UnknownCommandError('Invalid %s command' % command_name)
+
+    return _commands[command_name]
+
+def run(args):
+    command_class = _get_command(args[0])
+    command = command_class()
+    command.execute(args[1:])
+
+def print_help():
+    import os
+    thisdir = os.path.abspath(os.path.dirname(__file__))
+
+    for fname in os.listdir(os.path.join(thisdir)):
+        name, ext = os.path.splitext(fname)
+        if not ext == '.py':
+            continue
+        try:
+            __import__('commands.%s' % name)
+        except ImportError:
+            pass
+
+    print "Commands are:"
+    commands = [(x.name, x.description) for x in _commands.values()]
+    commands.sort()
+    for name, description in commands:
+        print "  %-15s %s" % (name, description)
+
+    print
+    print "For more information run 'poppler-regtest --help-command <command>'"
diff --git a/regtest/commands/create-refs.py b/regtest/commands/create-refs.py
new file mode 100644
index 0000000..4c5034b
--- /dev/null
+++ b/regtest/commands/create-refs.py
@@ -0,0 +1,65 @@
+# create-refs.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from commands import Command, register_command
+from TestReferences import TestReferences
+from Timer import Timer
+from Config import Config
+import os
+import tempfile
+
+class CreateRefs(Command):
+
+    name = 'create-refs'
+    usage_args = '[ options ... ] documents ... '
+    description = 'Create references for tests'
+
+    def __init__(self):
+        Command.__init__(self)
+        parser = self._get_args_parser()
+        parser.add_argument('--refs-dir',
+                            action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'),
+                            help = 'Directory where the references will be created')
+        parser.add_argument('-f', '--force',
+                            action = 'store_true', dest = 'force', default = False,
+                            help = 'Create references again for tests that already have references')
+        parser.add_argument('-c', '--checksums-only',
+                            action = 'store_true', dest = 'checksums_only', default = False,
+                            help = 'Leave only checksum files in references dir, other files will be deleted')
+        parser.add_argument('documents', nargs='*')
+
+    def run(self, options):
+        config = Config()
+        config.force = options['force']
+        config.checksums_only = options['checksums_only']
+
+        t = Timer()
+        for doc in options['documents']:
+            if os.path.isdir(doc):
+                docs_dir = doc
+            else:
+                docs_dir = os.path.dirname(doc)
+
+            refs = TestReferences(docs_dir, options['refs_dir'])
+            if doc == docs_dir:
+                refs.create_refs()
+            else:
+                refs.create_refs_for_file(os.path.basename(doc))
+        print "Refs created in %s" % (t.elapsed_str())
+
+register_command('create-refs', CreateRefs)
diff --git a/regtest/commands/run-tests.py b/regtest/commands/run-tests.py
new file mode 100644
index 0000000..b97b34b
--- /dev/null
+++ b/regtest/commands/run-tests.py
@@ -0,0 +1,69 @@
+# run-tests.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from commands import Command, register_command
+from TestRun import TestRun
+from Timer import Timer
+from Config import Config
+import os
+import tempfile
+
+class RunTests(Command):
+
+    name = 'run-tests'
+    usage_args = '[ options ... ] documents ... '
+    description = 'Run tests for documents'
+
+    def __init__(self):
+        Command.__init__(self)
+        parser = self._get_args_parser()
+        parser.add_argument('--refs-dir',
+                            action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'),
+                            help = 'Directory containing the references')
+        parser.add_argument('-o', '--out-dir',
+                            action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'),
+                            help = 'Directory where test results will be created')
+        parser.add_argument('--keep-results',
+                            action = 'store_true', dest = 'keep_results', default = False,
+                            help = 'Do not remove result files for passing tests')
+        parser.add_argument('--create-diffs',
+                            action = 'store_true', dest = 'create_diffs', default = False,
+                            help = 'Create diff files for failed tests')
+        parser.add_argument('documents', nargs='*')
+
+    def run(self, options):
+        config = Config()
+        config.keep_results = options['keep_results']
+        config.create_diffs = options['create_diffs']
+
+        t = Timer()
+        for doc in options['documents']:
+            if os.path.isdir(doc):
+                docs_dir = doc
+            else:
+                docs_dir = os.path.dirname(doc)
+
+            tests = TestRun(docs_dir, options['refs_dir'], options['out_dir'])
+            if doc == docs_dir:
+                tests.run_tests()
+            else:
+                tests.run_test(os.path.basename(doc))
+            tests.summary()
+        print "Tests run in %s" % (t.elapsed_str())
+
+register_command('run-tests', RunTests)
diff --git a/regtest/main.py b/regtest/main.py
new file mode 100644
index 0000000..5784f5a
--- /dev/null
+++ b/regtest/main.py
@@ -0,0 +1,77 @@
+# main.py
+#
+# Copyright (C) 2011 Carlos Garcia Campos <carlosgc at gnome.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import argparse
+import commands
+import backends
+import os
+from Config import Config
+
+class ListAction(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string = None):
+        setattr(namespace, self.dest, values.split(','))
+
+class HelpAction(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string = None):
+        if option_string == '--help-command':
+            commands.run([values, '--help'])
+            sys.exit(0)
+
+        parser.print_help()
+        commands.print_help()
+
+        sys.exit(0)
+
+def main(args):
+    parser = argparse.ArgumentParser(
+        description = 'Poppler regression tests',
+        prog = 'poppler-regtest',
+        usage = '%(prog)s [options ...] command [command-options ...] tests ...',
+        add_help = False)
+    parser.add_argument('-h', '--help',
+                        action = HelpAction, nargs = 0)
+    parser.add_argument('--help-command', metavar = 'COMMAND',
+                        action = HelpAction,
+                        help = 'Show help for a given command')
+    parser.add_argument('--utils-dir', action = 'store', dest = 'utils_dir', default = os.path.abspath("../utils"),
+                        help = 'Directory of poppler utils used for the tests')
+    parser.add_argument('-b', '--backends',
+                        action = ListAction, dest = 'backends',
+                        help = 'List of backends that will be used (separated by comma)')
+
+    ns, args = parser.parse_known_args(args)
+    if not args:
+        parser.print_help()
+        sys.exit(0)
+
+    Config(vars(ns))
+    try:
+        commands.run(args)
+    except commands.UnknownCommandError:
+        sys.stderr.write("Unknown command: %s\n" % (args[0]))
+        commands.print_help()
+        sys.exit(1)
+    except backends.UnknownBackendError, e:
+        sys.stderr.write(str(e) + "\n")
+        sys.stdout.write("Backends are: %s\n" % (", ".join([backend.get_name() for backend in backends.get_all_backends()])))
+        sys.exit(1)
+
+if __name__ == '__main__':
+    import sys
+    main(sys.argv[1:])
diff --git a/regtest/poppler-regtest b/regtest/poppler-regtest
new file mode 100755
index 0000000..fb1e126
--- /dev/null
+++ b/regtest/poppler-regtest
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+import sys
+import main
+
+main.main(sys.argv[1:])
commit 245e331a14e11a615bf47abbeb34a3561e393b41
Author: Albert Astals Cid <aacid at kde.org>
Date:   Mon Sep 12 13:43:49 2011 +0200

    0.17.4

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1db4be1..ba899a1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ test_big_endian(WORDS_BIGENDIAN)
 
 set(POPPLER_MAJOR_VERSION "0")
 set(POPPLER_MINOR_VERSION "17")
-set(POPPLER_MICRO_VERSION "3")
+set(POPPLER_MICRO_VERSION "4")
 set(POPPLER_VERSION "${POPPLER_MAJOR_VERSION}.${POPPLER_MINOR_VERSION}.${POPPLER_MICRO_VERSION}")
 
 # command line switches
diff --git a/NEWS b/NEWS
index e864980..3c19060 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+Release 0.17.4 (0.18 RC)
+        core:
+         * SplashOutputDev: Compile when defining USE_FIXEDPOINT
+         * PNGWriter: Compile with libpng >= 1.5.0
+        
 Release 0.17.3 (0.18 Beta 3)
         core:
          * PSOutputDev: Use Patterns for tiling fill when PS level >= 2
diff --git a/configure.ac b/configure.ac
index 6d4332d..0fa75cb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 m4_define([poppler_version_major],[0])
 m4_define([poppler_version_minor],[17])
-m4_define([poppler_version_micro],[3])
+m4_define([poppler_version_micro],[4])
 m4_define([poppler_version],[poppler_version_major.poppler_version_minor.poppler_version_micro])
 
 AC_PREREQ(2.59)
diff --git a/cpp/Doxyfile b/cpp/Doxyfile
index 3b4abd8..09ac90e 100644
--- a/cpp/Doxyfile
+++ b/cpp/Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME           = "Poppler CPP"
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 0.17.3
+PROJECT_NUMBER         = 0.17.4
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
diff --git a/qt4/src/Doxyfile b/qt4/src/Doxyfile
index f747599..73909e2 100644
--- a/qt4/src/Doxyfile
+++ b/qt4/src/Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME           = "Poppler Qt4 "
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 0.17.3
+PROJECT_NUMBER         = 0.17.4
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
commit 194b2413eb2c6a1641508aec63336aaf89ec3b51
Author: Albert Astals Cid <aacid at kde.org>
Date:   Mon Sep 12 13:43:35 2011 +0200

    gir 0.18

diff --git a/glib/CMakeLists.txt b/glib/CMakeLists.txt
index 73980ee..edc22f1 100644
--- a/glib/CMakeLists.txt
+++ b/glib/CMakeLists.txt
@@ -107,20 +107,20 @@ if (HAVE_INTROSPECTION)
   set(INTROSPECTION_COMPILER_ARGS "--includedir=${CMAKE_CURRENT_SOURCE_DIR}")
 
   set(introspection_files ${poppler_glib_SRCS} ${poppler_glib_public_headers} poppler-private.h)
-  set(Poppler_0_16_gir "poppler-glib")
-  set(Poppler_0_16_gir_INCLUDES GObject-2.0 cairo-1.0)
+  set(Poppler_0_18_gir "poppler-glib")
+  set(Poppler_0_18_gir_INCLUDES GObject-2.0 cairo-1.0)
   get_directory_property(_tmp_includes INCLUDE_DIRECTORIES)
   _list_prefix(_includes _tmp_includes "-I")
-  set(Poppler_0_16_gir_CFLAGS ${_includes})
-  set(Poppler_0_16_gir_LIBS poppler-glib)
+  set(Poppler_0_18_gir_CFLAGS ${_includes})
+  set(Poppler_0_18_gir_LIBS poppler-glib)
   _list_prefix(_abs_introspection_files introspection_files "${CMAKE_CURRENT_SOURCE_DIR}/")
   list(APPEND _abs_introspection_files
     ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c
     ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h
   )
-  set(Poppler_0_16_gir_FILES ${_abs_introspection_files})
+  set(Poppler_0_18_gir_FILES ${_abs_introspection_files})
 
-  list(APPEND INTROSPECTION_GIRS Poppler-0.16.gir)
+  list(APPEND INTROSPECTION_GIRS Poppler-0.18.gir)
 
   gir_add_introspections(INTROSPECTION_GIRS)
 endif ()
diff --git a/glib/Makefile.am b/glib/Makefile.am
index e362ff5..67116fc 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -92,13 +92,13 @@ INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)
 INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
 
 introspection_files = $(libpoppler_glib_la_SOURCES) $(poppler_glib_include_HEADERS)
-Poppler-0.16.gir: libpoppler-glib.la
-Poppler_0_16_gir_INCLUDES = GObject-2.0 cairo-1.0
-Poppler_0_16_gir_CFLAGS = $(INCLUDES) -I$(top_builddir)
-Poppler_0_16_gir_LIBS = libpoppler-glib.la
-Poppler_0_16_gir_FILES = $(addprefix $(srcdir)/, $(introspection_files))
+Poppler-0.18.gir: libpoppler-glib.la
+Poppler_0_18_gir_INCLUDES = GObject-2.0 cairo-1.0
+Poppler_0_18_gir_CFLAGS = $(INCLUDES) -I$(top_builddir)
+Poppler_0_18_gir_LIBS = libpoppler-glib.la
+Poppler_0_18_gir_FILES = $(addprefix $(srcdir)/, $(introspection_files))
 
-INTROSPECTION_GIRS += Poppler-0.16.gir
+INTROSPECTION_GIRS += Poppler-0.18.gir
 
 girdir = $(datadir)/gir-1.0
 dist_gir_DATA = $(INTROSPECTION_GIRS)
commit bf2cb5c9c47b4a61192101f0a48771657228e383
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Aug 30 19:08:40 2011 +0930

    Fix compile error with libpng >= 1.5.0
    
    libpng 1.5.0 changed one of the types in the png_set_iCCP() function prototype.

diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index d47efa6..f88c3a7 100644
--- a/goo/PNGWriter.cc
+++ b/goo/PNGWriter.cc
@@ -56,6 +56,13 @@ void PNGWriter::setSRGBProfile()
 
 bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
 {
+  /* libpng changed the png_set_iCCP() prototype in 1.5.0 */
+#if PNG_LIBPNG_VER < 10500
+        png_charp icc_data_ptr = (png_charp)icc_data;
+#else
+        png_const_bytep icc_data_ptr = (png_const_bytep)icc_data;
+#endif
+
 	/* initialize stuff */
 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 	if (!png_ptr) {
@@ -112,7 +119,7 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
 	png_set_pHYs(png_ptr, info_ptr, hDPI/0.0254, vDPI/0.0254, PNG_RESOLUTION_METER);
 
 	if (icc_data)
-		png_set_iCCP(png_ptr, info_ptr, icc_name, PNG_COMPRESSION_TYPE_BASE, (char*)icc_data, icc_data_size);
+		png_set_iCCP(png_ptr, info_ptr, icc_name, PNG_COMPRESSION_TYPE_BASE, icc_data_ptr, icc_data_size);
 	else if (sRGB_profile)
 		png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE);
 


More information about the poppler mailing list