Add hb-diff-colorize

Accepts --format=html now.
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index 65640bc..03a7710 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -1,65 +1,81 @@
 #!/usr/bin/python
 
-import sys, os, re, difflib, unicodedata, errno
+import sys, os, re, difflib, unicodedata, errno, cgi
 from itertools import *
 
 diff_symbols = "-+=*&^%$#@!~/"
 diff_colors = ['red', 'green', 'blue']
 
-class Colors:
+class ColorFormatter:
+
 	class Null:
-		red = ''
-		green = ''
-		end = ''
+		@staticmethod
+		def start_color (c): return ''
+		@staticmethod
+		def end_color (): return ''
+		@staticmethod
+		def escape (s): return s
+		@staticmethod
+		def newline (): return '\n'
+
 	class ANSI:
-		red = '\033[41;37;1m'
-		green = '\033[42;37;1m'
-		end = '\033[m'
+		@staticmethod
+		def start_color (c):
+			return {
+				'red': '\033[41;37;1m',
+				'green': '\033[42;37;1m',
+				'blue': '\033[44;37;1m',
+			}[c]
+		@staticmethod
+		def end_color ():
+			return '\033[m'
+		@staticmethod
+		def escape (s): return s
+		@staticmethod
+		def newline (): return '\n'
+
 	class HTML:
-		red = '<span style="color:red">'
-		green = '<span style="color:green">'
-		end = '</span>'
+		@staticmethod
+		def start_color (c):
+			return '<span style="background:%s">' % c
+		@staticmethod
+		def end_color ():
+			return '</span>'
+		@staticmethod
+		def escape (s): return cgi.escape (s)
+		@staticmethod
+		def newline (): return '<br/>\n'
 
 	@staticmethod
 	def Auto (argv = [], out = sys.stdout):
-		if os.isatty (out.fileno ()):
-			color = Colors.ANSI
-		else:
-			color = Colors.Null
-		if "--color" in argv:
-			argv.remove ("--color")
-			color = Colors.ANSI
-		if "--color=ansi" in argv:
-			argv.remove ("--color=ansi")
-			color = Colors.ANSI
-		if "--color=html" in argv:
-			argv.remove ("--color=html")
-			color = Colors.HTML
-		if "--no-color" in argv:
-			argv.remove ("--no-color")
-			color = Colors.Null
-		return color
+		format = ColorFormatter.ANSI
+		if "--format" in argv:
+			argv.remove ("--format")
+			format = ColorFormatter.ANSI
+		if "--format=ansi" in argv:
+			argv.remove ("--format=ansi")
+			format = ColorFormatter.ANSI
+		if "--format=html" in argv:
+			argv.remove ("--format=html")
+			format = ColorFormatter.HTML
+		if "--no-format" in argv:
+			argv.remove ("--no-format")
+			format = ColorFormatter.Null
+		return format
 
 
-	@staticmethod
-	def Default (argv = []):
-		return Colors.ANSI
-
-
-class FancyDiffer:
+class DiffColorizer:
 
 	diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)')
 
-	@staticmethod
-	def diff_lines (l1, l2, colors=Colors.Null):
+	def __init__ (self, formatter, colors=diff_colors, symbols=diff_symbols):
+		self.formatter = formatter
+		self.colors = colors
+		self.symbols = symbols
 
-		# Easy without colors
-		if colors == Colors.Null:
-			if l1 == l2:
-				return [' ', l1]
-			return ['-', l1, '+', l2]
-
-		ss = [FancyDiffer.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in (l1, l2)]
+	def colorize_lines (self, lines):
+		lines = (l if l else '' for l in lines)
+		ss = [self.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in lines]
 		oo = ["",""]
 		st = [False, False]
 		for l in difflib.Differ().compare (*ss):
@@ -68,29 +84,47 @@
 			if l[0] == ' ':
 				for i in range(2):
 					if st[i]:
-						oo[i] += colors.end
+						oo[i] += self.formatter.end_color ()
 						st[i] = False
-				oo = [o + l[2:] for o in oo]
+				oo = [o + self.formatter.escape (l[2:]) for o in oo]
 				continue
-			if l[0] == '-':
-				if not st[0]:
-					oo[0] += colors.red
-					st[0] = True
-				oo[0] += l[2:]
+			if l[0] in self.symbols:
+				i = self.symbols.index (l[0])
+				if not st[i]:
+					oo[i] += self.formatter.start_color (self.colors[i])
+					st[i] = True
+				oo[i] += self.formatter.escape (l[2:])
 				continue
-			if l[0] == '+':
-				if not st[1]:
-					oo[1] += colors.green
-					st[1] = True
-				oo[1] += l[2:]
 		for i in range(2):
 			if st[i]:
-				oo[i] += colors.end
-				st[i] = 0
+				oo[i] += self.formatter.end_color ()
+				st[i] = False
 		oo = [o.replace ('\n', '') for o in oo]
-		if oo[0] == oo[1]:
-			return [' ', oo[0], '\n']
-		return ['-', oo[0], '\n', '+', oo[1], '\n']
+		return [s1+s2+self.formatter.newline () for (s1,s2) in zip (self.symbols, oo) if s2]
+
+	def colorize_diff (self, f):
+		lines = [None, None]
+		for l in f:
+			if l[0] not in self.symbols:
+				yield self.formatter.escape (l).replace ('\n', self.formatter.newline ())
+				continue
+			i = self.symbols.index (l[0])
+			if lines[i]:
+				# Flush
+				for line in self.colorize_lines (lines):
+					yield line
+				lines = [None, None]
+			lines[i] = l[1:]
+			if (all (lines)):
+				# Flush
+				for line in self.colorize_lines (lines):
+					yield line
+				lines = [None, None]
+		if (any (lines)):
+			# Flush
+			for line in self.colorize_lines (lines):
+				yield line
+
 
 class ZipDiffer: