Merge "Merge from upstream HarfBuzz (version 0.9.38)"
diff --git a/.travis.yml b/.travis.yml
index 1c0dbab..4b3e0f8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,12 @@
 compiler:
   - clang
   - gcc
+env:
+  global:
+    - CPPFLAGS=""
+    - CFLAGS="-Werror --coverage"
+    - CXXFLAGS="-Werror --coverage"
+    - LDFLAGS="--coverage"
 install:
   - sudo apt-get install pkg-config ragel gtk-doc-tools # for autogen.sh
   - sudo apt-get install libfreetype6-dev # for font functions
@@ -9,7 +15,11 @@
   - sudo apt-get install libcairo2-dev # for utils
   - sudo apt-get install libicu-dev # for extra unicode functions
   - sudo apt-get install libgraphite2-dev # for extra shapers
-script: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 && make CPPFLAGS=-Werror && make check CPPFLAGS=-Werror
+  - sudo pip install cpp-coveralls # for coveralls.io code coverage tracking
+script:
+  - NOCONFIGURE=1 ./autogen.sh
+  - ./configure --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
+  - make && make check && { rm -f src/.libs/NONE.gcov; touch src/NONE; test $CC != gcc || coveralls; }
 notifications:
   irc: "irc.freenode.org#harfbuzz"
   email: harfbuzz@lists.freedesktop.org
diff --git a/Makefile.am b/Makefile.am
index fa87114..58afd9b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,6 +10,7 @@
 	autogen.sh \
 	harfbuzz.doap \
 	Android.mk \
+	README.python \
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
diff --git a/NEWS b/NEWS
index a838715..3a33bdf 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,128 @@
+Overview of changes leading to 0.9.38
+Friday, January 23, 2015
+=====================================
+
+- Fix minor out-of-bounds access in Indic shaper.
+- Change New Tai Lue shaping engine from South-East Asian to default,
+  reflecting change in Unicode encoding model.
+- Add hb-shape --font-size.  Can take up to two numbers for separate
+  x / y size.
+- Fix CoreText and FreeType scale issues with negative scales.
+- Reject blobs larger than 2GB.  This might break some icu-le-hb clients
+  that need security fixes.  See:
+  http://www.icu-project.org/trac/ticket/11450
+- Avoid accessing font tables during face destruction, in casce rogue
+  clients released face data already.
+- Fix up gobject-introspection a bit.  Python bindings kinda working.
+  See README.python.
+- Misc fixes.
+- API additions:
+  hb_ft_face_create_referenced()
+  hb_ft_font_create_referenced()
+
+
+Overview of changes leading to 0.9.37
+Wednesday, December 17, 2014
+=====================================
+
+- Fix out-of-bounds access in Context lookup format 3.
+- Indic: Allow ZWJ/ZWNJ before syllable modifiers.
+
+
+Overview of changes leading to 0.9.36
+Thursday, November 20, 2014
+=====================================
+
+- First time that three months went by without a release since
+  0.9.2 was released on August 10, 2012!
+- Fix performance bug in hb_ot_collect_glyphs():
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1090869
+- Add basic vertical-text support to hb-ot-font.
+- Misc build fixes.
+
+
+Overview of changes leading to 0.9.35
+Saturday, August 13, 2014
+=====================================
+
+- Fix major shape-plan caching bug when more than one shaper were
+  provided to hb_shape_full() (as exercised by XeTeX).
+  http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1246370.html
+- Fix Arabic fallback shaping regression.  This was broken in 0.9.32.
+- Major hb-coretext fixes.  That backend is complete now, including
+  respecing buffer direction and language, down to vertical writing.
+- Build fixes for Windows CE.  Should build fine now.
+- Misc fixes:
+  Use atexit() only if it's safe to call from shared library
+  https://bugs.freedesktop.org/show_bug.cgi?id=82246
+  Mandaic had errors in its Unicode Joining_Type
+  https://bugs.freedesktop.org/show_bug.cgi?id=82306
+- API changes:
+
+  * hb_buffer_clear_contents() does not reset buffer flags now.
+
+    After 763e5466c0a03a7c27020e1e2598e488612529a7, one doesn't
+    need to set flags for different pieces of text.  The flags now
+    are something the client sets up once, depending on how it
+    actually uses the buffer.  As such, don't clear it in
+    clear_contents().
+
+    I don't expect any changes to be needed to any existing client.
+
+
+Overview of changes leading to 0.9.34
+Saturday, August 2, 2014
+=====================================
+
+- hb_feature_from_string() now accepts CSS font-feature-settings format.
+- As a result, hb-shape / hb-view --features also accept CSS-style strings.
+  Eg, "'liga' off" is accepted now.
+- Add old-spec Myanmar shaper:
+  https://bugs.freedesktop.org/show_bug.cgi?id=81775
+- Don't apply 'calt' in Hangul shaper.
+- Fix mark advance zeroing for Hebrew shaper:
+  https://bugs.freedesktop.org/show_bug.cgi?id=76767
+- Implement Windows-1256 custom Arabic shaping.  Only built on Windows,
+  and requires help from get_glyph().  Used by Firefox.
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1045139
+- Disable 'liga' in vertical text.
+- Build fixes.
+- API changes:
+
+  * Make HB_BUFFER_FLAG_BOT/EOT easier to use.
+
+    Previously, we expected users to provide BOT/EOT flags when the
+    text *segment* was at paragraph boundaries.  This meant that for
+    clients that provide full paragraph to HarfBuzz (eg. Pango), they
+    had code like this:
+
+      hb_buffer_set_flags (hb_buffer,
+                           (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) |
+                           (item_offset + item_length == paragraph_length ?
+                            HB_BUFFER_FLAG_EOT : 0));
+
+      hb_buffer_add_utf8 (hb_buffer,
+                          paragraph_text, paragraph_length,
+                          item_offset, item_length);
+
+    After this change such clients can simply say:
+
+      hb_buffer_set_flags (hb_buffer,
+                           HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
+
+      hb_buffer_add_utf8 (hb_buffer,
+                          paragraph_text, paragraph_length,
+                          item_offset, item_length);
+
+    Ie, HarfBuzz itself checks whether the segment is at the beginning/end
+    of the paragraph.  Clients that only pass item-at-a-time to HarfBuzz
+    continue not setting any flags whatsoever.
+
+    Another way to put it is: if there's pre-context text in the buffer,
+    HarfBuzz ignores the BOT flag.  If there's post-context, it ignores
+    EOT flag.
+
+
 Overview of changes leading to 0.9.33
 Tuesday, July 22, 2014
 =====================================
diff --git a/README b/README
index 74e739d..d34bc74 100644
--- a/README
+++ b/README
@@ -1,3 +1,6 @@
+[![Build Status](https://travis-ci.org/behdad/harfbuzz.svg)](https://travis-ci.org/behdad/harfbuzz)
+[![Coverage Status](https://img.shields.io/coveralls/behdad/harfbuzz.svg)](https://coveralls.io/r/behdad/harfbuzz)
+
 This is HarfBuzz, a text shaping library.
 
 For bug reports, mailing list, and other information please visit:
diff --git a/README.md b/README.md
new file mode 120000
index 0000000..100b938
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+README
\ No newline at end of file
diff --git a/README.python b/README.python
new file mode 100644
index 0000000..eabdf5b
--- /dev/null
+++ b/README.python
@@ -0,0 +1,26 @@
+To enable HarfBuzz bindings for Python among other languages, make sure
+you have latest version of gobject-introspection compiled, and then
+run autogen.sh (if building from git), and then:
+
+  ./configure --with-gobject --enable-introspection
+
+Make sure that gobject-introspection is enabled then in the final report.
+
+Compile and install.
+
+Make sure you have the installation lib dir in LD_LIBRARY_PATH, as needed
+for the linker to find the library.
+
+Then make sure you also have GI_TYPELIB_PATH pointing to the resulting
+$prefix/lib/girepository-* directory.
+
+Make sure you have pygobject installed.  Then check that the following
+import works in your Python interpretter:
+
+  from gi.repository import HarfBuzz
+
+If it does, you are ready to call HarfBuzz from Python!  Congratulations.
+See src/sample.py.
+
+The Python API will change.  Let us know on the mailing list if you are
+using it, and send lots of feedback.
diff --git a/configure.ac b/configure.ac
index 78e3178..3e42d95 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [0.9.33],
+        [0.9.38],
         [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -285,7 +285,7 @@
 	AC_MSG_ERROR([graphite2 support requested but libgraphite2 not found])
 fi
 if $have_graphite2; then
-    AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite2 library])
+	AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite2 library])
 fi
 AM_CONDITIONAL(HAVE_GRAPHITE2, $have_graphite2)
 
@@ -297,20 +297,14 @@
 	[with_freetype=auto])
 have_freetype=false
 if test "x$with_freetype" = "xyes" -o "x$with_freetype" = "xauto"; then
-	PKG_CHECK_MODULES(FREETYPE, freetype2 >= 2.3.8, have_freetype=true, :)
+	# See freetype/docs/VERSION.DLL; 12.0.6 means freetype-2.4.2
+	PKG_CHECK_MODULES(FREETYPE, freetype2 >= 12.0.6, have_freetype=true, :)
 fi
 if test "x$with_freetype" = "xyes" -a "x$have_freetype" != "xtrue"; then
 	AC_MSG_ERROR([FreeType support requested but libfreetype2 not found])
 fi
 if $have_freetype; then
 	AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
-	_save_libs="$LIBS"
-	_save_cflags="$CFLAGS"
-	LIBS="$LIBS $FREETYPE_LIBS"
-	CFLAGS="$CFLAGS $FREETYPE_CFLAGS"
-	AC_CHECK_FUNCS(FT_Face_GetCharVariantIndex)
-	LIBS="$_save_libs"
-	CFLAGS="$_save_cflags"
 fi
 AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
 
diff --git a/src/Makefile.am b/src/Makefile.am
index acedaa0..c99967f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,6 +13,9 @@
 # The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
+# Convenience targets:
+lib: libharfbuzz.la
+
 lib_LTLIBRARIES = libharfbuzz.la
 
 HBCFLAGS =
@@ -91,6 +94,7 @@
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
+	hb-ot-shape-complex-arabic-win1256.hh \
 	hb-ot-shape-complex-default.cc \
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
@@ -233,8 +237,8 @@
 	$(AM_V_GEN) $(GLIB_MKENUMS) \
 		--identifier-prefix hb_ --symbol-prefix hb_gobject \
 		--template $^ | \
-	sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@.tmp" && \
-	mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false )
+	sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \
+	|| ($(RM) "$@"; false)
 endif
 EXTRA_DIST += \
 	harfbuzz-gobject.pc.in \
@@ -250,8 +254,8 @@
 		-e 's@%libdir%@$(libdir)@g' \
 		-e 's@%includedir%@$(includedir)@g' \
 		-e 's@%VERSION%@$(VERSION)@g' \
-	"$<" \
-	> "$@.tmp" && mv "$@.tmp" "$@" || ( $(RM) "$@.tmp"; false )
+	"$<" > "$@" \
+	|| ($(RM) "$@"; false)
 
 CLEANFILES += $(pkgconfig_DATA)
 
@@ -264,8 +268,9 @@
 	sed -e 's/ (.*//' | \
 	LANG=C sort; \
 	echo LIBRARY libharfbuzz-$(HB_VERSION_MAJOR).dll; \
-	) >"$@.tmp"
-	@ ! grep -q hb_ERROR "$@.tmp" && mv "$@.tmp" "$@" || ($(RM) "$@"; false)
+	) >"$@"
+	@ ! grep -q hb_ERROR "$@" \
+	|| ($(RM) "$@"; false)
 
 
 GENERATORS = \
@@ -277,26 +282,25 @@
 unicode-tables: arabic-table indic-table
 
 indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
-	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc.tmp && \
-	mv hb-ot-shape-complex-indic-table.cc.tmp $(srcdir)/hb-ot-shape-complex-indic-table.cc || \
-	($(RM) hb-ot-shape-complex-indic-table.cc.tmp; false)
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
+	|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
-	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \
-	mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \
-	($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false)
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh \
+	|| ($(RM) hb-ot-shape-complex-arabic-table.hh; false)
 
 built-sources: $(BUILT_SOURCES)
 
 .PHONY: unicode-tables arabic-table indic-table built-sources
 
-BUILT_SOURCES += \
-	hb-buffer-deserialize-json.hh \
-	hb-buffer-deserialize-text.hh \
-	hb-ot-shape-complex-indic-machine.hh \
-	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-sea-machine.hh \
+RAGEL_GENERATED = \
+	$(srcdir)/hb-buffer-deserialize-json.hh \
+	$(srcdir)/hb-buffer-deserialize-text.hh \
+	$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
+	$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
+	$(srcdir)/hb-ot-shape-complex-sea-machine.hh \
 	$(NULL)
+BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
@@ -304,9 +308,10 @@
 	hb-ot-shape-complex-myanmar-machine.rl \
 	hb-ot-shape-complex-sea-machine.rl \
 	$(NULL)
-.rl.hh:
-	$(AM_V_GEN)$(RAGEL) -e -F1 -o "$@.tmp" "$<" && \
-	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
+MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
+$(srcdir)/%.hh: $(srcdir)/%.rl
+	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
+	|| ($(RM) "$@"; false)
 
 noinst_PROGRAMS = \
 	main \
diff --git a/src/check-defs.sh b/src/check-defs.sh
index 357347f..65a2467 100755
--- a/src/check-defs.sh
+++ b/src/check-defs.sh
@@ -21,7 +21,7 @@
 	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
 	so=.libs/lib${lib}.so
 
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3`"
+	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`"
 
 	if test -f "$so"; then
 
diff --git a/src/check-includes.sh b/src/check-includes.sh
index 5643c69..902f235 100755
--- a/src/check-includes.sh
+++ b/src/check-includes.sh
@@ -27,7 +27,7 @@
 
 for x in $HBSOURCES; do
 	test -f "$srcdir/$x" && x="$srcdir/$x"
-	grep '#.*\<include\>' "$x" /dev/null | head -n 1
+	grep '#.*\<include\>' "$x" /dev/null | grep -v 'include _' | head -n 1
 done |
 grep -v '"hb-.*private[.]hh"' |
 grep -v 'hb-private[.]hh:' |
diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh
index e7e0e29..27deb42 100755
--- a/src/check-libstdc++.sh
+++ b/src/check-libstdc++.sh
@@ -17,14 +17,14 @@
 tested=false
 for suffix in so dylib; do
 	so=.libs/libharfbuzz.$suffix
-	if test -f "$so"; then
-		echo "Checking that we are not linking to libstdc++"
-		if ldd $so | grep 'libstdc[+][+]'; then
-			echo "Ouch, linked to libstdc++"
-			stat=1
-		fi
-		tested=true
+	if ! test -f "$so"; then continue; fi
+
+	echo "Checking that we are not linking to libstdc++"
+	if ldd $so | grep 'libstdc[+][+]'; then
+		echo "Ouch, linked to libstdc++"
+		stat=1
 	fi
+	tested=true
 done
 if ! $tested; then
 	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh
index 83d02c8..1446fa7 100755
--- a/src/check-static-inits.sh
+++ b/src/check-static-inits.sh
@@ -22,8 +22,8 @@
 
 echo "Checking that no object file has static initializers"
 for obj in $OBJS; do
-	if objdump -t "$obj" | grep '[.]ctors'; then
-		echo "Ouch, $obj has static initializers"
+	if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then
+		echo "Ouch, $obj has static initializers/finalizers"
 		stat=1
 	fi
 done
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index e000b01..b2bf43f 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -16,11 +16,17 @@
 
 echo "Checking that we are not exposing internal symbols"
 tested=false
-for so in `ls .libs/lib*.so .libs/lib*.dylib 2>/dev/null` ; do
+for suffix in so dylib; do
+	so=.libs/libharfbuzz.$suffix
+	if ! test -f "$so"; then continue; fi
 	
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3`"
+	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`"
+
 	prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
+	# On mac, C symbols are prefixed with _
+	if test $suffix = dylib; then prefix="_$prefix"; fi
+
 	echo "Processing $so"
 	if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then
 		echo "Ouch, internal symbols exposed"
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 4fb76f4..f5716bd 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -209,7 +209,7 @@
 	for (start,end) in zip (starts, ends):
 		if p not in [start>>page_bits, end>>page_bits]: continue
 		offset = "indic_offset_0x%04xu" % start
-		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end, start, offset)
+		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	for u,d in singles.items ():
 		if p != u>>page_bits: continue
 		print "      if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index b82b4b2..8759a25 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -78,8 +78,8 @@
 }
 
 /**
- * hb_blob_create: (Xconstructor)
- * @data: (array length=length) (closure user_data) (destroy destroy) (scope notified) (transfer none): Pointer to blob data.
+ * hb_blob_create: (skip)
+ * @data: Pointer to blob data.
  * @length: Length of @data in bytes.
  * @mode: Memory mode for @data.
  * @user_data: Data parameter to pass to @destroy.
@@ -102,7 +102,10 @@
 {
   hb_blob_t *blob;
 
-  if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
+  if (!length ||
+      length >= 1u << 31 ||
+      data + length < data /* overflows */ ||
+      !(blob = hb_object_create<hb_blob_t> ())) {
     if (destroy)
       destroy (user_data);
     return hb_blob_get_empty ();
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
index 7351b2a..91b350f 100644
--- a/src/hb-buffer-deserialize-json.rl
+++ b/src/hb-buffer-deserialize-json.rl
@@ -117,8 +117,8 @@
 
   const char *tok = NULL;
   int cs;
-  hb_glyph_info_t info;
-  hb_glyph_position_t pos;
+  hb_glyph_info_t info = {0};
+  hb_glyph_position_t pos = {0};
   %%{
     write init;
     write exec;
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 5eccd3c..069f925 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -48,15 +48,13 @@
   ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
-
   hb_unicode_funcs_t *unicode; /* Unicode functions */
-  hb_segment_properties_t props; /* Script, language, direction */
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
   hb_codepoint_t replacement; /* U+FFFD or something else. */
 
   /* Buffer contents */
-
   hb_buffer_content_type_t content_type;
+  hb_segment_properties_t props; /* Script, language, direction */
 
   bool in_error; /* Allocation failed */
   bool have_output; /* Whether we have an output buffer going on */
@@ -183,6 +181,9 @@
   inline bool ensure (unsigned int size)
   { return likely (!size || size < allocated) ? true : enlarge (size); }
 
+  inline bool ensure_inplace (unsigned int size)
+  { return likely (!size || size < allocated); }
+
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
   HB_INTERNAL bool shift_forward (unsigned int count);
 
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 2377ba4..7bf232d 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -178,6 +178,7 @@
 
   hb_unicode_funcs_destroy (unicode);
   unicode = hb_unicode_funcs_get_default ();
+  flags = HB_BUFFER_FLAG_DEFAULT;
   replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
 
   clear ();
@@ -191,7 +192,6 @@
 
   hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
   props = default_props;
-  flags = HB_BUFFER_FLAG_DEFAULT;
 
   content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   in_error = false;
@@ -702,11 +702,11 @@
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
-    HB_SEGMENT_PROPERTIES_DEFAULT,
     HB_BUFFER_FLAG_DEFAULT,
     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
+    HB_SEGMENT_PROPERTIES_DEFAULT,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
@@ -1400,7 +1400,7 @@
 /**
  * hb_buffer_add_utf8:
  * @buffer: a buffer.
- * @text: (array length=text_length):
+ * @text: (array length=text_length) (element-type uint8_t):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index a089e52..8837cef 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -234,8 +234,8 @@
 
 static hb_language_item_t *langs;
 
-#ifdef HAVE_ATEXIT
-static inline
+#ifdef HB_USE_ATEXIT
+static
 void free_langs (void)
 {
   while (langs) {
@@ -269,7 +269,7 @@
     goto retry;
   }
 
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
   if (!first_lang)
     atexit (free_langs); /* First person registers atexit() callback. */
 #endif
@@ -345,7 +345,7 @@
   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
   if (unlikely (language == HB_LANGUAGE_INVALID)) {
     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
-    hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
+    (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
   }
 
   return default_language;
diff --git a/src/hb-common.h b/src/hb-common.h
index f5141b9..b6ce3f7 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -123,12 +123,13 @@
 const char *
 hb_direction_to_string (hb_direction_t direction);
 
+#define HB_DIRECTION_IS_VALID(dir)	((((unsigned int) (dir)) & ~3U) == 4)
+/* Direction must be valid for the following */
 #define HB_DIRECTION_IS_HORIZONTAL(dir)	((((unsigned int) (dir)) & ~1U) == 4)
 #define HB_DIRECTION_IS_VERTICAL(dir)	((((unsigned int) (dir)) & ~1U) == 6)
 #define HB_DIRECTION_IS_FORWARD(dir)	((((unsigned int) (dir)) & ~2U) == 4)
 #define HB_DIRECTION_IS_BACKWARD(dir)	((((unsigned int) (dir)) & ~2U) == 5)
-#define HB_DIRECTION_IS_VALID(dir)	((((unsigned int) (dir)) & ~3U) == 4)
-#define HB_DIRECTION_REVERSE(dir)	((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */
+#define HB_DIRECTION_REVERSE(dir)	((hb_direction_t) (((unsigned int) (dir)) ^ 1))
 
 
 /* hb_language_t */
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index d92c6ba..16e069d 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -27,6 +27,7 @@
  */
 
 #define HB_SHAPER coretext
+#define hb_coretext_shaper_face_data_t CGFont
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-coretext.h"
@@ -77,10 +78,6 @@
  * shaper face data
  */
 
-struct hb_coretext_shaper_face_data_t {
-  CGFontRef cg_font;
-};
-
 static void
 release_data (void *info, const void *data, size_t size)
 {
@@ -93,13 +90,11 @@
 hb_coretext_shaper_face_data_t *
 _hb_coretext_shaper_face_data_create (hb_face_t *face)
 {
-  hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
-  if (unlikely (!data))
-    return NULL;
+  hb_coretext_shaper_face_data_t *data = NULL;
 
   if (face->destroy == (hb_destroy_func_t) CGFontRelease)
   {
-    data->cg_font = CGFontRetain ((CGFontRef) face->user_data);
+    data = CGFontRetain ((CGFontRef) face->user_data);
   }
   else
   {
@@ -110,14 +105,15 @@
       DEBUG_MSG (CORETEXT, face, "Face has empty blob");
 
     CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
-    data->cg_font = CGFontCreateWithDataProvider (provider);
-    CGDataProviderRelease (provider);
+    if (likely (provider))
+    {
+      data = CGFontCreateWithDataProvider (provider);
+      CGDataProviderRelease (provider);
+    }
   }
 
-  if (unlikely (!data->cg_font)) {
+  if (unlikely (!data)) {
     DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
-    free (data);
-    return NULL;
   }
 
   return data;
@@ -126,8 +122,7 @@
 void
 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
 {
-  CFRelease (data->cg_font);
-  free (data);
+  CFRelease (data);
 }
 
 CGFontRef
@@ -135,7 +130,7 @@
 {
   if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
   hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  return face_data->cg_font;
+  return face_data;
 }
 
 
@@ -145,6 +140,7 @@
 
 struct hb_coretext_shaper_font_data_t {
   CTFontRef ct_font;
+  CGFloat x_mult, y_mult; /* From CT space to HB space. */
 };
 
 hb_coretext_shaper_font_data_t *
@@ -159,7 +155,17 @@
   hb_face_t *face = font->face;
   hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
 
-  data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scale, NULL, NULL);
+  /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */
+  CGFloat font_size = 36.; /* Default... */
+  /* No idea if the following is even a good idea. */
+  if (font->y_ppem)
+    font_size = font->y_ppem;
+
+  if (font_size < 0)
+    font_size = -font_size;
+  data->x_mult = (CGFloat) font->x_scale / font_size;
+  data->y_mult = (CGFloat) font->y_scale / font_size;
+  data->ct_font = CTFontCreateWithGraphicsFont (face_data, font_size, NULL, NULL);
   if (unlikely (!data->ct_font)) {
     DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed");
     free (data);
@@ -332,7 +338,7 @@
 #define kUpperCaseType				38
 
 /* Table data courtesy of Apple. */
-struct feature_mapping_t {
+static const struct feature_mapping_t {
     FourCharCode otFeatureTag;
     uint16_t aatFeatureType;
     uint16_t selectorToEnable;
@@ -436,12 +442,29 @@
   hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
   hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
 
+  /* Attach marks to their bases, to match the 'ot' shaper.
+   * Adapted from hb-ot-shape:hb_form_clusters().
+   * Note that this only makes us be closer to the 'ot' shaper,
+   * but by no means the same.  For example, if there's
+   * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
+   * continue pointing to B2 even though B2 was merged into B1's
+   * cluster... */
+  {
+    hb_unicode_funcs_t *unicode = buffer->unicode;
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    for (unsigned int i = 1; i < count; i++)
+      if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
+	buffer->merge_clusters (i - 1, i + 1);
+  }
+
+  hb_auto_array_t<feature_record_t> feature_records;
+  hb_auto_array_t<range_record_t> range_records;
+
   /*
    * Set up features.
    * (copied + modified from code from hb-uniscribe.cc)
    */
-  hb_auto_array_t<feature_record_t> feature_records;
-  hb_auto_array_t<range_record_t> range_records;
   if (num_features)
   {
     /* Sort features by start/end events. */
@@ -549,7 +572,6 @@
 	  CFRelease (attributes);
 
 	  range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc);
-
 	  CFRelease (font_desc);
 	}
 	else
@@ -584,32 +606,26 @@
     num_features = 0;
   }
 
-#define FAIL(...) \
-  HB_STMT_START { \
-    DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
-    return false; \
-  } HB_STMT_END;
-
   unsigned int scratch_size;
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 
-#define ALLOCATE_ARRAY(Type, name, len) \
+#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
   Type *name = (Type *) scratch; \
   { \
     unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
-    assert (_consumed <= scratch_size); \
+    if (unlikely (_consumed > scratch_size)) \
+    { \
+      on_no_room; \
+      assert (0); \
+    } \
     scratch += _consumed; \
     scratch_size -= _consumed; \
   }
 
-#define utf16_index() var1.u32
-
-  ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2);
-
+  ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
   unsigned int chars_len = 0;
   for (unsigned int i = 0; i < buffer->len; i++) {
     hb_codepoint_t c = buffer->info[i].codepoint;
-    buffer->info[i].utf16_index() = chars_len;
     if (likely (c <= 0xFFFFu))
       pchars[chars_len++] = c;
     else if (unlikely (c > 0x10FFFFu))
@@ -620,232 +636,446 @@
     }
   }
 
-#undef utf16_index
-
-  CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL,
-                                                               pchars, chars_len,
-                                                               kCFAllocatorNull);
-
-  CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
-  CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
-  CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
-				  kCTFontAttributeName, font_data->ct_font);
-
-  if (num_features)
+  ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/);
+  chars_len = 0;
+  for (unsigned int i = 0; i < buffer->len; i++)
   {
-    ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len);
-
-    /* Need log_clusters to assign features. */
-    chars_len = 0;
-    for (unsigned int i = 0; i < buffer->len; i++)
-    {
-      hb_codepoint_t c = buffer->info[i].codepoint;
-      unsigned int cluster = buffer->info[i].cluster;
-      log_clusters[chars_len++] = cluster;
-      if (hb_in_range (c, 0x10000u, 0x10FFFFu))
-	log_clusters[chars_len++] = cluster; /* Surrogates. */
-    }
-
-    unsigned int start = 0;
-    range_record_t *last_range = &range_records[0];
-    for (unsigned int k = 0; k < chars_len; k++)
-    {
-      range_record_t *range = last_range;
-      while (log_clusters[k] < range->index_first)
-	range--;
-      while (log_clusters[k] > range->index_last)
-	range++;
-      if (range != last_range)
-      {
-        if (last_range->font)
-	  CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
-					  kCTFontAttributeName, last_range->font);
-
-	start = k;
-      }
-
-      last_range = range;
-    }
-    if (start != chars_len && last_range->font)
-      CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start - 1),
-				      kCTFontAttributeName, last_range->font);
-
-    for (unsigned int i = 0; i < range_records.len; i++)
-      if (range_records[i].font)
-	CFRelease (range_records[i].font);
+    hb_codepoint_t c = buffer->info[i].codepoint;
+    unsigned int cluster = buffer->info[i].cluster;
+    log_clusters[chars_len++] = cluster;
+    if (hb_in_range (c, 0x10000u, 0x10FFFFu))
+      log_clusters[chars_len++] = cluster; /* Surrogates. */
   }
 
-  CTLineRef line = CTLineCreateWithAttributedString (attr_string);
-  CFRelease (attr_string);
+#define FAIL(...) \
+  HB_STMT_START { \
+    DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
+    ret = false; \
+    goto fail; \
+  } HB_STMT_END;
 
-  CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
-  unsigned int num_runs = CFArrayGetCount (glyph_runs);
+  bool ret = true;
+  CFStringRef string_ref = NULL;
+  CTLineRef line = NULL;
 
-  buffer->len = 0;
-
-  const CFRange range_all = CFRangeMake (0, 0);
-
-  for (unsigned int i = 0; i < num_runs; i++)
+  if (0)
   {
-    CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i);
+resize_and_retry:
+    DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
+    /* string_ref uses the scratch-buffer for backing store, and line references
+     * string_ref (via attr_string).  We must release those before resizing buffer. */
+    assert (string_ref);
+    assert (line);
+    CFRelease (string_ref);
+    CFRelease (line);
+    string_ref = NULL;
+    line = NULL;
 
-    /* CoreText does automatic font fallback (AKA "cascading") for  characters
-     * not supported by the requested font, and provides no way to turn it off,
-     * so we detect if the returned run uses a font other than the requested
-     * one and fill in the buffer with .notdef glyphs instead of random glyph
-     * indices from a different font.
-     */
-    CFDictionaryRef attributes = CTRunGetAttributes (run);
-    CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
-    CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
-    if (!CFEqual (run_cg_font, face_data->cg_font))
-    {
-        CFRelease (run_cg_font);
+    /* Get previous start-of-scratch-area, that we use later for readjusting
+     * our existing scratch arrays. */
+    unsigned int old_scratch_used;
+    hb_buffer_t::scratch_buffer_t *old_scratch;
+    old_scratch = buffer->get_scratch_buffer (&old_scratch_used);
+    old_scratch_used = scratch - old_scratch;
 
-	CFRange range = CTRunGetStringRange (run);
-	buffer->ensure (buffer->len + range.length);
-	if (buffer->in_error)
-	  FAIL ("Buffer resize failed");
-	hb_glyph_info_t *info = buffer->info + buffer->len;
+    if (unlikely (!buffer->ensure (buffer->allocated * 2)))
+      FAIL ("Buffer resize failed");
 
-	CGGlyph notdef = 0;
-	double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, &notdef, NULL, 1);
-
-        for (CFIndex j = range.location; j < range.location + range.length; j++)
-	{
-	    UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
-	    if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
-	    {
-	      ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
-	      if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
-	        /* This is the second of a surrogate pair.  Don't need .notdef
-		 * for this one. */
-	        continue;
-	    }
-
-            info->codepoint = notdef;
-	    /* TODO We have to fixup clusters later.  See vis_clusters in
-	     * hb-uniscribe.cc for example. */
-            info->cluster = j;
-
-            info->mask = advance;
-            info->var1.u32 = 0;
-            info->var2.u32 = 0;
-
-	    info++;
-	    buffer->len++;
-        }
-        continue;
-    }
-    CFRelease (run_cg_font);
-
-    unsigned int num_glyphs = CTRunGetGlyphCount (run);
-    if (num_glyphs == 0)
-      continue;
-
-    buffer->ensure (buffer->len + num_glyphs);
-
+    /* Adjust scratch, pchars, and log_cluster arrays.  This is ugly, but really the
+     * cleanest way to do without completely restructuring the rest of this shaper. */
     scratch = buffer->get_scratch_buffer (&scratch_size);
+    pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch)));
+    log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch)));
+    scratch += old_scratch_used;
+    scratch_size -= old_scratch_used;
+  }
+retry:
+  {
+    string_ref = CFStringCreateWithCharactersNoCopy (NULL,
+						     pchars, chars_len,
+						     kCFAllocatorNull);
+    if (unlikely (!string_ref))
+      FAIL ("CFStringCreateWithCharactersNoCopy failed");
 
-    /* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always
-     * succeed, and so copying data to our own buffer will be rare. */
+    /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
+    {
+      CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault,
+										  chars_len);
+      if (unlikely (!attr_string))
+	FAIL ("CFAttributedStringCreateMutable failed");
+      CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
+      if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
+      {
+	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
+					kCTVerticalFormsAttributeName, kCFBooleanTrue);
+      }
 
-    const CGGlyph* glyphs = CTRunGetGlyphsPtr (run);
-    if (!glyphs) {
-      ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs);
-      CTRunGetGlyphs (run, range_all, glyph_buf);
-      glyphs = glyph_buf;
+      if (buffer->props.language)
+      {
+/* What's the iOS equivalent of this check?
+ * The symbols was introduced in iOS 7.0.
+ * At any rate, our fallback is safe and works fine. */
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
+#  define kCTLanguageAttributeName CFSTR ("NSLanguage")
+#endif
+        CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
+							    hb_language_to_string (buffer->props.language),
+							    kCFStringEncodingUTF8,
+							    kCFAllocatorNull);
+	if (unlikely (!lang))
+	  FAIL ("CFStringCreateWithCStringNoCopy failed");
+	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
+					kCTLanguageAttributeName, lang);
+	CFRelease (lang);
+      }
+      CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
+				      kCTFontAttributeName, font_data->ct_font);
+
+      if (num_features)
+      {
+	unsigned int start = 0;
+	range_record_t *last_range = &range_records[0];
+	for (unsigned int k = 0; k < chars_len; k++)
+	{
+	  range_record_t *range = last_range;
+	  while (log_clusters[k] < range->index_first)
+	    range--;
+	  while (log_clusters[k] > range->index_last)
+	    range++;
+	  if (range != last_range)
+	  {
+	    if (last_range->font)
+	      CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
+					      kCTFontAttributeName, last_range->font);
+
+	    start = k;
+	  }
+
+	  last_range = range;
+	}
+	if (start != chars_len && last_range->font)
+	  CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
+					  kCTFontAttributeName, last_range->font);
+      }
+
+      int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
+      CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
+      CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
+						    (const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
+						    (const void **) &level_number,
+						    1,
+						    &kCFTypeDictionaryKeyCallBacks,
+						    &kCFTypeDictionaryValueCallBacks);
+      if (unlikely (!options))
+        FAIL ("CFDictionaryCreate failed");
+
+      CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
+      CFRelease (options);
+      CFRelease (attr_string);
+      if (unlikely (!typesetter))
+	FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
+
+      line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0));
+      CFRelease (typesetter);
+      if (unlikely (!line))
+	FAIL ("CTTypesetterCreateLine failed");
     }
 
-    const CGPoint* positions = CTRunGetPositionsPtr (run);
-    if (!positions) {
-      ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs);
-      CTRunGetPositions (run, range_all, position_buf);
-      positions = position_buf;
-    }
+    CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
+    unsigned int num_runs = CFArrayGetCount (glyph_runs);
+    DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs);
 
-    const CFIndex* string_indices = CTRunGetStringIndicesPtr (run);
-    if (!string_indices) {
-      ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs);
-      CTRunGetStringIndices (run, range_all, index_buf);
-      string_indices = index_buf;
-    }
+    buffer->len = 0;
+    uint32_t status_and = ~0, status_or = 0;
 
+    const CFRange range_all = CFRangeMake (0, 0);
+
+    for (unsigned int i = 0; i < num_runs; i++)
+    {
+      CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i));
+      CTRunStatus run_status = CTRunGetStatus (run);
+      status_or  |= run_status;
+      status_and &= run_status;
+      DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
+
+      /* CoreText does automatic font fallback (AKA "cascading") for  characters
+       * not supported by the requested font, and provides no way to turn it off,
+       * so we must detect if the returned run uses a font other than the requested
+       * one and fill in the buffer with .notdef glyphs instead of random glyph
+       * indices from a different font.
+       */
+      CFDictionaryRef attributes = CTRunGetAttributes (run);
+      CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
+      if (!CFEqual (run_ct_font, font_data->ct_font))
+      {
+	/* The run doesn't use our main font instance.  We have to figure out
+	 * whether font fallback happened, or this is just CoreText giving us
+	 * another CTFont using the same underlying CGFont.  CoreText seems
+	 * to do that in a variety of situations, one of which being vertical
+	 * text, but also perhaps for caching reasons.
+	 *
+	 * First, see if it uses any of our subfonts created to set font features...
+	 *
+	 * Next, compare the CGFont to the one we used to create our fonts.
+	 * Even this doesn't work all the time.
+	 *
+	 * Finally, we compare PS names, which I don't think are unique...
+	 *
+	 * Looks like if we really want to be sure here we have to modify the
+	 * font to change the name table, similar to what we do in the uniscribe
+	 * backend.
+	 *
+	 * However, even that wouldn't work if we were passed in the CGFont to
+	 * begin with.
+	 *
+	 * Webkit uses a slightly different approach: it installs LastResort
+	 * as fallback chain, and then checks PS name of used font against
+	 * LastResort.  That one is safe for any font except for LastResort,
+	 * as opposed to ours, which can fail if we are using any uninstalled
+	 * font that has the same name as an installed font.
+	 *
+	 * See: http://github.com/behdad/harfbuzz/pull/36
+	 */
+	bool matched = false;
+	for (unsigned int i = 0; i < range_records.len; i++)
+	  if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
+	  {
+	    matched = true;
+	    break;
+	  }
+	if (!matched)
+	{
+	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
+	  if (run_cg_font)
+	  {
+	    matched = CFEqual (run_cg_font, face_data);
+	    CFRelease (run_cg_font);
+	  }
+	}
+	if (!matched)
+	{
+	  CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFontPostScriptNameKey);
+	  CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
+	  CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
+	  CFRelease (run_ps_name);
+	  CFRelease (font_ps_name);
+	  if (result == kCFCompareEqualTo)
+	    matched = true;
+	}
+	if (!matched)
+	{
+	  CFRange range = CTRunGetStringRange (run);
+          DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
+		     range.location, range.location + range.length);
+	  if (!buffer->ensure_inplace (buffer->len + range.length))
+	    goto resize_and_retry;
+	  hb_glyph_info_t *info = buffer->info + buffer->len;
+
+	  hb_codepoint_t notdef = 0;
+	  hb_direction_t dir = buffer->props.direction;
+	  hb_position_t x_advance, y_advance, x_offset, y_offset;
+	  hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance);
+	  hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset);
+	  hb_position_t advance = x_advance + y_advance;
+	  x_offset = -x_offset;
+	  y_offset = -y_offset;
+
+	  unsigned int old_len = buffer->len;
+	  for (CFIndex j = range.location; j < range.location + range.length; j++)
+	  {
+	      UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
+	      if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
+	      {
+		ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
+		if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
+		  /* This is the second of a surrogate pair.  Don't need .notdef
+		   * for this one. */
+		  continue;
+	      }
+
+	      info->codepoint = notdef;
+	      info->cluster = log_clusters[j];
+
+	      info->mask = advance;
+	      info->var1.u32 = x_offset;
+	      info->var2.u32 = y_offset;
+
+	      info++;
+	      buffer->len++;
+	  }
+	  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+	    buffer->reverse_range (old_len, buffer->len);
+	  continue;
+	}
+      }
+
+      unsigned int num_glyphs = CTRunGetGlyphCount (run);
+      if (num_glyphs == 0)
+	continue;
+
+      if (!buffer->ensure_inplace (buffer->len + num_glyphs))
+	goto resize_and_retry;
+
+      hb_glyph_info_t *run_info = buffer->info + buffer->len;
+
+      /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
+       * succeed, and so copying data to our own buffer will be rare.  Reports
+       * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
+       * frequently.  At any rate, we can test that codepath by setting USE_PTR
+       * to false. */
+
+#define USE_PTR true
+
+#define SCRATCH_SAVE() \
+  unsigned int scratch_size_saved = scratch_size; \
+  hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
+
+#define SCRATCH_RESTORE() \
+  scratch_size = scratch_size_saved; \
+  scratch = scratch_saved;
+
+      {
+        SCRATCH_SAVE();
+	const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
+	if (!glyphs) {
+	  ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
+	  CTRunGetGlyphs (run, range_all, glyph_buf);
+	  glyphs = glyph_buf;
+	}
+	const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
+	if (!string_indices) {
+	  ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
+	  CTRunGetStringIndices (run, range_all, index_buf);
+	  string_indices = index_buf;
+	}
+	hb_glyph_info_t *info = run_info;
+	for (unsigned int j = 0; j < num_glyphs; j++)
+	{
+	  info->codepoint = glyphs[j];
+	  info->cluster = log_clusters[string_indices[j]];
+	  info++;
+	}
+	SCRATCH_RESTORE();
+      }
+      {
+        SCRATCH_SAVE();
+	const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
+	if (!positions) {
+	  ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
+	  CTRunGetPositions (run, range_all, position_buf);
+	  positions = position_buf;
+	}
+	double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
+	DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
+	hb_glyph_info_t *info = run_info;
+	CGFloat x_mult = font_data->x_mult, y_mult = font_data->y_mult;
+	if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	{
+	  for (unsigned int j = 0; j < num_glyphs; j++)
+	  {
+	    double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_advance) - positions[j].x;
+	    info->mask = advance * x_mult;
+	    info->var1.u32 = positions[0].x * x_mult; /* Yes, zero. */
+	    info->var2.u32 = positions[j].y * y_mult;
+	    info++;
+	  }
+	}
+	else
+	{
+	  run_advance = -run_advance;
+	  for (unsigned int j = 0; j < num_glyphs; j++)
+	  {
+	    double advance = (j + 1 < num_glyphs ? positions[j + 1].y : positions[0].y + run_advance) - positions[j].y;
+	    info->mask = advance * y_mult;
+	    info->var1.u32 = positions[j].x * x_mult;
+	    info->var2.u32 = positions[0].y * y_mult; /* Yes, zero. */
+	    info++;
+	  }
+	}
+	SCRATCH_RESTORE();
+      }
+#undef SCRATCH_RESTORE
+#undef SCRATCH_SAVE
+#undef USE_PTR
 #undef ALLOCATE_ARRAY
 
-    double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
-
-    for (unsigned int j = 0; j < num_glyphs; j++) {
-      double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_width) - positions[j].x;
-
-      hb_glyph_info_t *info = &buffer->info[buffer->len];
-
-      info->codepoint = glyphs[j];
-      info->cluster = string_indices[j];
-
-      /* Currently, we do all x-positioning by setting the advance, we never use x-offset. */
-      info->mask = advance;
-      info->var1.u32 = 0;
-      info->var2.u32 = positions[j].y;
-
-      buffer->len++;
+      buffer->len += num_glyphs;
     }
-  }
 
-  buffer->clear_positions ();
+    /* Make sure all runs had the expected direction. */
+    bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+    assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
+    assert (bool (status_or  & kCTRunStatusRightToLeft) == backward);
 
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; ++i) {
-    hb_glyph_info_t *info = &buffer->info[i];
-    hb_glyph_position_t *pos = &buffer->pos[i];
+    buffer->clear_positions ();
 
-    /* TODO vertical */
-    pos->x_advance = info->mask;
-    pos->x_offset = info->var1.u32;
-    pos->y_offset = info->var2.u32;
-  }
-
-  /* Fix up clusters so that we never return out-of-order indices;
-   * if core text has reordered glyphs, we'll merge them to the
-   * beginning of the reordered cluster.
-   *
-   * This does *not* mean we'll form the same clusters as Uniscribe
-   * or the native OT backend, only that the cluster indices will be
-   * monotonic in the output buffer. */
-  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
-    unsigned int prev_cluster = 0;
-    for (unsigned int i = 0; i < count; i++) {
-      unsigned int curr_cluster = buffer->info[i].cluster;
-      if (curr_cluster < prev_cluster) {
-        for (unsigned int j = i; j > 0; j--) {
-          if (buffer->info[j - 1].cluster > curr_cluster)
-            buffer->info[j - 1].cluster = curr_cluster;
-          else
-            break;
-        }
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    hb_glyph_position_t *pos = buffer->pos;
+    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+      for (unsigned int i = 0; i < count; i++)
+      {
+	pos->x_advance = info->mask;
+	pos->x_offset = info->var1.u32;
+	pos->y_offset = info->var2.u32;
+	info++, pos++;
       }
-      prev_cluster = curr_cluster;
-    }
-  } else {
-    unsigned int prev_cluster = (unsigned int)-1;
-    for (unsigned int i = 0; i < count; i++) {
-      unsigned int curr_cluster = buffer->info[i].cluster;
-      if (curr_cluster > prev_cluster) {
-        for (unsigned int j = i; j > 0; j--) {
-          if (buffer->info[j - 1].cluster < curr_cluster)
-            buffer->info[j - 1].cluster = curr_cluster;
-          else
-            break;
-        }
+    else
+      for (unsigned int i = 0; i < count; i++)
+      {
+	pos->y_advance = info->mask;
+	pos->x_offset = info->var1.u32;
+	pos->y_offset = info->var2.u32;
+	info++, pos++;
       }
-      prev_cluster = curr_cluster;
+
+    /* Fix up clusters so that we never return out-of-order indices;
+     * if core text has reordered glyphs, we'll merge them to the
+     * beginning of the reordered cluster.  CoreText is nice enough
+     * to tell us whenever it has produced nonmonotonic results...
+     * Note that we assume the input clusters were nonmonotonic to
+     * begin with.
+     *
+     * This does *not* mean we'll form the same clusters as Uniscribe
+     * or the native OT backend, only that the cluster indices will be
+     * monotonic in the output buffer. */
+    if (count > 1 && (status_or & kCTRunStatusNonMonotonic))
+    {
+      hb_glyph_info_t *info = buffer->info;
+      if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
+      {
+	unsigned int cluster = info[count - 1].cluster;
+	for (unsigned int i = count - 1; i > 0; i--)
+	{
+	  cluster = MIN (cluster, info[i - 1].cluster);
+	  info[i - 1].cluster = cluster;
+	}
+      }
+      else
+      {
+	unsigned int cluster = info[0].cluster;
+	for (unsigned int i = 1; i < count; i++)
+	{
+	  cluster = MIN (cluster, info[i].cluster);
+	  info[i].cluster = cluster;
+	}
+      }
     }
   }
 
-  CFRelease (string_ref);
-  CFRelease (line);
+#undef FAIL
 
-  return true;
+fail:
+  if (string_ref)
+    CFRelease (string_ref);
+  if (line)
+    CFRelease (line);
+
+  for (unsigned int i = 0; i < range_records.len; i++)
+    if (range_records[i].font)
+      CFRelease (range_records[i].font);
+
+  return ret;
 }
 
 
diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh
index 6520d3d..c4266ff 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face-private.hh
@@ -66,7 +66,7 @@
   {
     hb_blob_t *blob;
 
-    if (unlikely (!this || !reference_table_func))
+    if (unlikely (!reference_table_func))
       return hb_blob_get_empty ();
 
     blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 44e0b0b..f57f566 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -51,6 +51,13 @@
  *     In particular, FT_Get_Advance() without the NO_HINTING flag seems to be
  *     buggy.
  *
+ *     FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
+ *     would work fine.  However, we also abuse this API for performing in font-space,
+ *     but don't pass the correct flags to FreeType.  We just abuse the no-hinting mode
+ *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
+ *     pass NO_HINTING around.  This seems to work best, until we go ahead and add a full
+ *     load_flags API.
+ *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
  *   - In the future, we should add constructors to create fonts in font space?
@@ -70,12 +77,10 @@
 {
   FT_Face ft_face = (FT_Face) font_data;
 
-#ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX
   if (unlikely (variation_selector)) {
     *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
     return *glyph != 0;
   }
-#endif
 
   *glyph = FT_Get_Char_Index (ft_face, unicode);
   return *glyph != 0;
@@ -94,6 +99,9 @@
   if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
     return 0;
 
+  if (font->x_scale < 0)
+    v = -v;
+
   return (v + (1<<9)) >> 10;
 }
 
@@ -136,7 +144,7 @@
 			  void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT;
+  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
     return false;
@@ -185,7 +193,7 @@
 			 void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT;
+  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
     return false;
@@ -340,11 +348,7 @@
 
     blob = hb_blob_create ((const char *) ft_face->stream->base,
 			   (unsigned int) ft_face->stream->size,
-			   /* TODO: We assume that it's mmap()'ed, but FreeType code
-			    * suggests that there are cases we reach here but font is
-			    * not mmapped.  For example, when mmap() fails.  No idea
-			    * how to deal with it better here. */
-			   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
+			   HB_MEMORY_MODE_READONLY,
 			   ft_face, destroy);
     face = hb_face_create (blob, ft_face->face_index);
     hb_blob_destroy (blob);
@@ -358,6 +362,22 @@
   return face;
 }
 
+/**
+ * hb_ft_face_create_referenced:
+ * @ft_face:
+ *
+ * 
+ *
+ * Return value: (transfer full): 
+ * Since: 1.0
+ **/
+hb_face_t *
+hb_ft_face_create_referenced (FT_Face ft_face)
+{
+  FT_Reference_Face (ft_face);
+  return hb_ft_face_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
+}
+
 static void
 hb_ft_face_finalize (FT_Face ft_face)
 {
@@ -420,23 +440,43 @@
   hb_font_set_scale (font,
 		     (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16),
 		     (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16));
+#if 0 /* hb-ft works in no-hinting model */
   hb_font_set_ppem (font,
 		    ft_face->size->metrics.x_ppem,
 		    ft_face->size->metrics.y_ppem);
+#endif
 
   return font;
 }
 
+/**
+ * hb_ft_font_create_referenced:
+ * @ft_face:
+ *
+ * 
+ *
+ * Return value: (transfer full): 
+ * Since: 1.0
+ **/
+hb_font_t *
+hb_ft_font_create_referenced (FT_Face ft_face)
+{
+  FT_Reference_Face (ft_face);
+  return hb_ft_font_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
+}
+
 
 /* Thread-safe, lock-free, FT_Library */
 
 static FT_Library ft_library;
 
-static inline
+#ifdef HB_USE_ATEXIT
+static
 void free_ft_library (void)
 {
   FT_Done_FreeType (ft_library);
 }
+#endif
 
 static FT_Library
 get_ft_library (void)
@@ -455,7 +495,7 @@
       goto retry;
     }
 
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
     atexit (free_ft_library); /* First person registers atexit() callback. */
 #endif
   }
@@ -493,14 +533,19 @@
 
   FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
-  assert (font->y_scale >= 0);
   FT_Set_Char_Size (ft_face,
-		    font->x_scale, font->y_scale,
+		    abs (font->x_scale), abs (font->y_scale),
 		    0, 0);
 #if 0
 		    font->x_ppem * 72 * 64 / font->x_scale,
 		    font->y_ppem * 72 * 64 / font->y_scale);
 #endif
+  if (font->x_scale < 0 || font->y_scale < 0)
+  {
+    FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
+			  0, font->y_scale < 0 ? -1 : +1};
+    FT_Set_Transform (ft_face, &matrix, NULL);
+  }
 
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
diff --git a/src/hb-ft.h b/src/hb-ft.h
index 696251e..92f4b36 100644
--- a/src/hb-ft.h
+++ b/src/hb-ft.h
@@ -34,19 +34,76 @@
 
 HB_BEGIN_DECLS
 
-/* Note: FreeType is not thread-safe.  Hence, these functions are not either. */
+/*
+ * Note: FreeType is not thread-safe.
+ * Hence, these functions are not either.
+ */
 
+/*
+ * hb-face from ft-face.
+ */
+
+/* This one creates a new hb-face for given ft-face.
+ * When the returned hb-face is destroyed, the destroy
+ * callback is called (if not NULL), with the ft-face passed
+ * to it.
+ *
+ * The client is responsible to make sure that ft-face is
+ * destroyed after hb-face is destroyed.
+ *
+ * Most often you don't want this function.  You should use either
+ * hb_ft_face_create_cached(), or hb_ft_face_create_referenced().
+ * In particular, if you are going to pass NULL as destroy, you
+ * probably should use (the more recent) hb_ft_face_create_referenced()
+ * instead.
+ */
 hb_face_t *
 hb_ft_face_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy);
 
+/* This version is like hb_ft_face_create(), except that it caches
+ * the hb-face using the generic pointer of the ft-face.  This means
+ * that subsequent calls to this function with the same ft-face will
+ * return the same hb-face (correctly referenced).
+ *
+ * Client is still responsible for making sure that ft-face is destroyed
+ * after hb-face is.
+ */
 hb_face_t *
 hb_ft_face_create_cached (FT_Face ft_face);
 
+/* This version is like hb_ft_face_create(), except that it calls
+ * FT_Reference_Face() on ft-face, as such keeping ft-face alive
+ * as long as the hb-face is.
+ *
+ * This is the most convenient version to use.  Use it unless you have
+ * very good reasons not to.
+ */
+hb_face_t *
+hb_ft_face_create_referenced (FT_Face ft_face);
+
+
+/*
+ * hb-font from ft-face.
+ */
+
+/*
+ * Note:
+ *
+ * Set face size on ft-face before creating hb-font from it.
+ * Otherwise hb-ft would NOT pick up the font size correctly.
+ */
+
+/* See notes on hb_ft_face_create().  Same issues re lifecycle-management
+ * apply here.  Use hb_ft_font_create_referenced() if you can. */
 hb_font_t *
 hb_ft_font_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy);
 
+/* See notes on hb_ft_face_create_referenced() re lifecycle-management
+ * issues. */
+hb_font_t *
+hb_ft_font_create_referenced (FT_Face ft_face);
 
 
 /* Makes an hb_font_t use FreeType internally to implement font functions. */
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index af43cab..61dff5e 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -382,3 +382,14 @@
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+hb_blob_t *
+hb_glib_blob_create (GBytes *gbytes)
+{
+  gsize size = 0;
+  gconstpointer data = g_bytes_get_data (gbytes, &size);
+  return hb_blob_create ((const char *) data,
+			 size,
+			 HB_MEMORY_MODE_READONLY,
+			 g_bytes_ref (gbytes),
+			 (hb_destroy_func_t) g_bytes_unref);
+}
diff --git a/src/hb-glib.h b/src/hb-glib.h
index 63a9d33..1a8f42e 100644
--- a/src/hb-glib.h
+++ b/src/hb-glib.h
@@ -46,6 +46,9 @@
 hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void);
 
+hb_blob_t *
+hb_glib_blob_create (GBytes *gbytes);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 709aa94..807c330 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -274,8 +274,8 @@
   while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
 	  DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
   {
-    buffer->ensure (buffer->allocated * 2);
-    if (unlikely (buffer->in_error)) {
+    if (unlikely (!buffer->ensure (buffer->allocated * 2)))
+    {
       if (feats) gr_featureval_destroy (feats);
       gr_seg_destroy (seg);
       return false;
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 86c8b5c..24cec9d 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -363,10 +363,8 @@
   if (!hb_atomic_ptr_get (&normalizer)) {
     UErrorCode icu_err = U_ZERO_ERROR;
     /* We ignore failure in getNFCInstace(). */
-    hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err));
+    (void) hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err));
   }
 #endif
   return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
 }
-
-
diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh
index eaa2169..6281201 100644
--- a/src/hb-mutex-private.hh
+++ b/src/hb-mutex-private.hh
@@ -46,7 +46,7 @@
 
 #include <windows.h>
 typedef CRITICAL_SECTION hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT	{ NULL, 0, 0, NULL, NULL, 0 }
+#define HB_MUTEX_IMPL_INIT	{0}
 #define hb_mutex_impl_init(M)	InitializeCriticalSection (M)
 #define hb_mutex_impl_lock(M)	EnterCriticalSection (M)
 #define hb_mutex_impl_unlock(M)	LeaveCriticalSection (M)
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 8a9ae34..7bd0f16 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -68,8 +68,6 @@
 #define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT}
 struct hb_user_data_array_t
 {
-  /* TODO Add tracing. */
-
   struct hb_user_data_item_t {
     hb_user_data_key_t *key;
     void *data;
@@ -106,69 +104,6 @@
 
 #define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_USER_DATA_ARRAY_INIT}
 
-  static inline void *create (unsigned int size) {
-    hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
-
-    if (likely (obj))
-      obj->init ();
-
-    return obj;
-  }
-
-  inline void init (void) {
-    ref_count.init (1);
-    user_data.init ();
-  }
-
-  inline bool is_inert (void) const {
-    return unlikely (ref_count.is_invalid ());
-  }
-
-  inline void reference (void) {
-    if (unlikely (!this || this->is_inert ()))
-      return;
-    ref_count.inc ();
-  }
-
-  inline bool destroy (void) {
-    if (unlikely (!this || this->is_inert ()))
-      return false;
-    if (ref_count.dec () != 1)
-      return false;
-
-    ref_count.finish (); /* Do this before user_data */
-    user_data.finish ();
-
-    return true;
-  }
-
-  inline bool set_user_data (hb_user_data_key_t *key,
-			     void *              data,
-			     hb_destroy_func_t   destroy_func,
-			     hb_bool_t           replace) {
-    if (unlikely (!this || this->is_inert ()))
-      return false;
-
-    return user_data.set (key, data, destroy_func, replace);
-  }
-
-  inline void *get_user_data (hb_user_data_key_t *key) {
-    if (unlikely (!this || this->is_inert ()))
-      return NULL;
-
-    return user_data.get (key);
-  }
-
-  inline void trace (const char *function) const {
-    if (unlikely (!this)) return;
-    /* TODO We cannot use DEBUG_MSG_FUNC here since that one currently only
-     * prints the class name and throws away the template info. */
-    DEBUG_MSG (OBJECT, (void *) this,
-	       "%s refcount=%d",
-	       function,
-	       this ? ref_count.ref_count : 0);
-  }
-
   private:
   ASSERT_POD ();
 };
@@ -179,32 +114,56 @@
 template <typename Type>
 static inline void hb_object_trace (const Type *obj, const char *function)
 {
-  obj->header.trace (function);
+  DEBUG_MSG (OBJECT, (void *) obj,
+	     "%s refcount=%d",
+	     function,
+	     obj ? obj->header.ref_count.ref_count : 0);
 }
+
 template <typename Type>
 static inline Type *hb_object_create (void)
 {
-  Type *obj = (Type *) hb_object_header_t::create (sizeof (Type));
+  Type *obj = (Type *) calloc (1, sizeof (Type));
+
+  if (unlikely (!obj))
+    return obj;
+
+  hb_object_init (obj);
   hb_object_trace (obj, HB_FUNC);
   return obj;
 }
 template <typename Type>
+static inline void hb_object_init (Type *obj)
+{
+  obj->header.ref_count.init (1);
+  obj->header.user_data.init ();
+}
+template <typename Type>
 static inline bool hb_object_is_inert (const Type *obj)
 {
-  return unlikely (obj->header.is_inert ());
+  return unlikely (obj->header.ref_count.is_invalid ());
 }
 template <typename Type>
 static inline Type *hb_object_reference (Type *obj)
 {
   hb_object_trace (obj, HB_FUNC);
-  obj->header.reference ();
+  if (unlikely (!obj || hb_object_is_inert (obj)))
+    return obj;
+  obj->header.ref_count.inc ();
   return obj;
 }
 template <typename Type>
 static inline bool hb_object_destroy (Type *obj)
 {
   hb_object_trace (obj, HB_FUNC);
-  return obj->header.destroy ();
+  if (unlikely (!obj || hb_object_is_inert (obj)))
+    return false;
+  if (obj->header.ref_count.dec () != 1)
+    return false;
+
+  obj->header.ref_count.finish (); /* Do this before user_data */
+  obj->header.user_data.finish ();
+  return true;
 }
 template <typename Type>
 static inline bool hb_object_set_user_data (Type               *obj,
@@ -213,14 +172,18 @@
 					    hb_destroy_func_t   destroy,
 					    hb_bool_t           replace)
 {
-  return obj->header.set_user_data (key, data, destroy, replace);
+  if (unlikely (!obj || hb_object_is_inert (obj)))
+    return false;
+  return obj->header.user_data.set (key, data, destroy, replace);
 }
 
 template <typename Type>
 static inline void *hb_object_get_user_data (Type               *obj,
 					     hb_user_data_key_t *key)
 {
-  return obj->header.get_user_data (key);
+  if (unlikely (!obj || hb_object_is_inert (obj)))
+    return NULL;
+  return obj->header.user_data.get (key);
 }
 
 
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index 57db59d..7500c32 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -197,6 +197,8 @@
 
 struct OpenTypeFontFile
 {
+  static const hb_tag_t tableTag	= HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
+
   static const hb_tag_t CFFTag		= HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */
   static const hb_tag_t TrueTypeTag	= HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */
   static const hb_tag_t TTCTag		= HB_TAG ('t','t','c','f'); /* TrueType Collection */
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 475187b..477d9e2 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -194,10 +194,11 @@
   {
     this->start = hb_blob_get_data (this->blob, NULL);
     this->end = this->start + hb_blob_get_length (this->blob);
+    assert (this->start <= this->end); /* Must not overflow. */
     this->edit_count = 0;
     this->debug_depth = 0;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
+    DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
 		     "start [%p..%p] (%lu bytes)",
 		     this->start, this->end,
 		     (unsigned long) (this->end - this->start));
@@ -205,7 +206,7 @@
 
   inline void end_processing (void)
   {
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
+    DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
 		     "end [%p..%p] %u edit requests",
 		     this->start, this->end, this->edit_count);
 
@@ -217,28 +218,31 @@
   inline bool check_range (const void *base, unsigned int len) const
   {
     const char *p = (const char *) base;
+    bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len;
 
-    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
-      (&this->debug_depth, "SANITIZE", this->blob, NULL,
-       "check_range [%p..%p] (%d bytes) in [%p..%p]",
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
        p, p + len, len,
-       this->start, this->end);
+       this->start, this->end,
+       ok ? "OK" : "OUT-OF-RANGE");
 
-    return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
+    return likely (ok);
   }
 
   inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
   {
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
+    unsigned int array_size = record_size * len;
+    bool ok = !overflows && this->check_range (base, array_size);
 
-    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
-      (&this->debug_depth, "SANITIZE", this->blob, NULL,
-       "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
-       p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
-       this->start, this->end);
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
+       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
+       this->start, this->end,
+       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
 
-    return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
+    return likely (ok);
   }
 
   template <typename Type>
@@ -255,15 +259,14 @@
     const char *p = (const char *) base;
     this->edit_count++;
 
-    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
-      (&this->debug_depth, "SANITIZE", this->blob, NULL,
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
        "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
        this->edit_count,
        p, p + len, len,
        this->start, this->end,
        this->writable ? "GRANTED" : "DENIED");
 
-    return TRACE_RETURN (this->writable);
+    return this->writable;
   }
 
   template <typename Type, typename ValueType>
@@ -297,7 +300,7 @@
     c->init (blob);
 
   retry:
-    DEBUG_MSG_FUNC (SANITIZE, blob, "start");
+    DEBUG_MSG_FUNC (SANITIZE, c->start, "start");
 
     c->start_processing ();
 
@@ -311,13 +314,13 @@
     sane = t->sanitize (c);
     if (sane) {
       if (c->edit_count) {
-	DEBUG_MSG_FUNC (SANITIZE, blob, "passed first round with %d edits; going for second round", c->edit_count);
+	DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count);
 
         /* sanitize again to ensure no toe-stepping */
         c->edit_count = 0;
 	sane = t->sanitize (c);
 	if (c->edit_count) {
-	  DEBUG_MSG_FUNC (SANITIZE, blob, "requested %d edits in second round; FAILLING", c->edit_count);
+	  DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count);
 	  sane = false;
 	}
       }
@@ -330,7 +333,7 @@
 	if (c->start) {
 	  c->writable = true;
 	  /* ok, we made it writable by relocating.  try again */
-	  DEBUG_MSG_FUNC (SANITIZE, blob, "retry");
+	  DEBUG_MSG_FUNC (SANITIZE, c->start, "retry");
 	  goto retry;
 	}
       }
@@ -338,7 +341,7 @@
 
     c->end_processing ();
 
-    DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
+    DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED");
     if (sane)
       return blob;
     else {
@@ -533,32 +536,77 @@
 struct BEInt<Type, 2>
 {
   public:
-  inline void set (Type i) { hb_be_uint16_put (v,i); }
-  inline operator Type (void) const { return hb_be_uint16_get (v); }
-  inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_eq (v, o.v); }
+  inline void set (Type V)
+  {
+    v[0] = (V >>  8) & 0xFF;
+    v[1] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] <<  8)
+         + (v[1]      );
+  }
+  inline bool operator == (const BEInt<Type, 2>& o) const
+  {
+    return v[0] == o.v[0]
+        && v[1] == o.v[1];
+  }
   inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); }
   private: uint8_t v[2];
 };
 template <typename Type>
-struct BEInt<Type, 4>
-{
-  public:
-  inline void set (Type i) { hb_be_uint32_put (v,i); }
-  inline operator Type (void) const { return hb_be_uint32_get (v); }
-  inline bool operator == (const BEInt<Type, 4>& o) const { return hb_be_uint32_eq (v, o.v); }
-  inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
-  private: uint8_t v[4];
-};
-template <typename Type>
 struct BEInt<Type, 3>
 {
   public:
-  inline void set (Type i) { hb_be_uint24_put (v,i); }
-  inline operator Type (void) const { return hb_be_uint24_get (v); }
-  inline bool operator == (const BEInt<Type, 3>& o) const { return hb_be_uint24_eq (v, o.v); }
+  inline void set (Type V)
+  {
+    v[0] = (V >> 16) & 0xFF;
+    v[1] = (V >>  8) & 0xFF;
+    v[2] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 16)
+         + (v[1] <<  8)
+         + (v[2]      );
+  }
+  inline bool operator == (const BEInt<Type, 3>& o) const
+  {
+    return v[0] == o.v[0]
+        && v[1] == o.v[1]
+        && v[2] == o.v[2];
+  }
   inline bool operator != (const BEInt<Type, 3>& o) const { return !(*this == o); }
   private: uint8_t v[3];
 };
+template <typename Type>
+struct BEInt<Type, 4>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >> 24) & 0xFF;
+    v[1] = (V >> 16) & 0xFF;
+    v[2] = (V >>  8) & 0xFF;
+    v[3] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 24)
+         + (v[1] << 16)
+         + (v[2] <<  8)
+         + (v[3]      );
+  }
+  inline bool operator == (const BEInt<Type, 4>& o) const
+  {
+    return v[0] == o.v[0]
+        && v[1] == o.v[1]
+        && v[2] == o.v[2]
+        && v[3] == o.v[3];
+  }
+  inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
+  private: uint8_t v[4];
+};
 
 /* Integer types in big-endian order and no alignment requirement */
 template <typename Type, unsigned int Size>
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index c9890c5..2af2f54 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -35,17 +35,128 @@
 #include "hb-ot-hmtx-table.hh"
 
 
+struct hb_ot_face_metrics_accelerator_t
+{
+  unsigned int num_metrics;
+  unsigned int num_advances;
+  unsigned int default_advance;
+  const OT::_mtx *table;
+  hb_blob_t *blob;
+
+  inline void init (hb_face_t *face,
+		    hb_tag_t _hea_tag, hb_tag_t _mtx_tag,
+		    unsigned int default_advance)
+  {
+    this->default_advance = default_advance;
+    this->num_metrics = face->get_num_glyphs ();
+
+    hb_blob_t *_hea_blob = OT::Sanitizer<OT::_hea>::sanitize (face->reference_table (_hea_tag));
+    const OT::_hea *_hea = OT::Sanitizer<OT::_hea>::lock_instance (_hea_blob);
+    this->num_advances = _hea->numberOfLongMetrics;
+    hb_blob_destroy (_hea_blob);
+
+    this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag));
+    if (unlikely (!this->num_advances ||
+		  2 * (this->num_advances + this->num_metrics) < hb_blob_get_length (this->blob)))
+    {
+      this->num_metrics = this->num_advances = 0;
+      hb_blob_destroy (this->blob);
+      this->blob = hb_blob_get_empty ();
+    }
+    this->table = OT::Sanitizer<OT::_mtx>::lock_instance (this->blob);
+  }
+
+  inline void fini (void)
+  {
+    hb_blob_destroy (this->blob);
+  }
+
+  inline unsigned int get_advance (hb_codepoint_t glyph) const
+  {
+    if (unlikely (glyph >= this->num_metrics))
+    {
+      /* If this->num_metrics is zero, it means we don't have the metrics table
+       * for this direction: return one EM.  Otherwise, it means that the glyph
+       * index is out of bound: return zero. */
+      if (this->num_metrics)
+	return 0;
+      else
+	return this->default_advance;
+    }
+
+    if (glyph >= this->num_advances)
+      glyph = this->num_advances - 1;
+
+    return this->table->longMetric[glyph].advance;
+  }
+};
+
+struct hb_ot_face_cmap_accelerator_t
+{
+  const OT::CmapSubtable *table;
+  const OT::CmapSubtable *uvs_table;
+  hb_blob_t *blob;
+
+  inline void init (hb_face_t *face)
+  {
+    this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap));
+    const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
+    const OT::CmapSubtable *subtable = NULL;
+    const OT::CmapSubtable *subtable_uvs = NULL;
+
+    /* 32-bit subtables. */
+    if (!subtable) subtable = cmap->find_subtable (3, 10);
+    if (!subtable) subtable = cmap->find_subtable (0, 6);
+    if (!subtable) subtable = cmap->find_subtable (0, 4);
+    /* 16-bit subtables. */
+    if (!subtable) subtable = cmap->find_subtable (3, 1);
+    if (!subtable) subtable = cmap->find_subtable (0, 3);
+    if (!subtable) subtable = cmap->find_subtable (0, 2);
+    if (!subtable) subtable = cmap->find_subtable (0, 1);
+    if (!subtable) subtable = cmap->find_subtable (0, 0);
+    /* Meh. */
+    if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
+
+    /* UVS subtable. */
+    if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5);
+    /* Meh. */
+    if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable);
+
+    this->table = subtable;
+    this->uvs_table = subtable_uvs;
+  }
+
+  inline void fini (void)
+  {
+    hb_blob_destroy (this->blob);
+  }
+
+  inline bool get_glyph (hb_codepoint_t  unicode,
+			 hb_codepoint_t  variation_selector,
+			 hb_codepoint_t *glyph) const
+  {
+    if (unlikely (variation_selector))
+    {
+      switch (this->uvs_table->get_glyph_variant (unicode,
+						  variation_selector,
+						  glyph))
+      {
+	case OT::GLYPH_VARIANT_NOT_FOUND:	return false;
+	case OT::GLYPH_VARIANT_FOUND:		return true;
+	case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
+      }
+    }
+
+    return this->table->get_glyph (unicode, glyph);
+  }
+};
+
 
 struct hb_ot_font_t
 {
-  unsigned int num_glyphs;
-  unsigned int num_hmetrics;
-  const OT::hmtx *hmtx;
-  hb_blob_t *hmtx_blob;
-
-  const OT::CmapSubtable *cmap;
-  const OT::CmapSubtable *cmap_uvs;
-  hb_blob_t *cmap_blob;
+  hb_ot_face_cmap_accelerator_t cmap;
+  hb_ot_face_metrics_accelerator_t h_metrics;
+  hb_ot_face_metrics_accelerator_t v_metrics;
 };
 
 
@@ -53,50 +164,16 @@
 _hb_ot_font_create (hb_font_t *font)
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
+  hb_face_t *face = font->face;
 
   if (unlikely (!ot_font))
     return NULL;
 
-  ot_font->num_glyphs = font->face->get_num_glyphs ();
+  unsigned int upem = face->get_upem ();
 
-  {
-    hb_blob_t *hhea_blob = OT::Sanitizer<OT::hhea>::sanitize (font->face->reference_table (HB_OT_TAG_hhea));
-    const OT::hhea *hhea = OT::Sanitizer<OT::hhea>::lock_instance (hhea_blob);
-    ot_font->num_hmetrics = hhea->numberOfHMetrics;
-    hb_blob_destroy (hhea_blob);
-  }
-  ot_font->hmtx_blob = OT::Sanitizer<OT::hmtx>::sanitize (font->face->reference_table (HB_OT_TAG_hmtx));
-  if (unlikely (!ot_font->num_hmetrics ||
-		2 * (ot_font->num_hmetrics + ot_font->num_glyphs) < hb_blob_get_length (ot_font->hmtx_blob)))
-  {
-    hb_blob_destroy (ot_font->hmtx_blob);
-    free (ot_font);
-    return NULL;
-  }
-  ot_font->hmtx = OT::Sanitizer<OT::hmtx>::lock_instance (ot_font->hmtx_blob);
-
-  ot_font->cmap_blob = OT::Sanitizer<OT::cmap>::sanitize (font->face->reference_table (HB_OT_TAG_cmap));
-  const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (ot_font->cmap_blob);
-  const OT::CmapSubtable *subtable = NULL;
-  const OT::CmapSubtable *subtable_uvs = NULL;
-
-  /* 32-bit subtables. */
-  if (!subtable) subtable = cmap->find_subtable (0, 6);
-  if (!subtable) subtable = cmap->find_subtable (0, 4);
-  if (!subtable) subtable = cmap->find_subtable (3, 10);
-  /* 16-bit subtables. */
-  if (!subtable) subtable = cmap->find_subtable (0, 3);
-  if (!subtable) subtable = cmap->find_subtable (3, 1);
-  /* Meh. */
-  if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
-
-  /* UVS subtable. */
-  if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5);
-  /* Meh. */
-  if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable);
-
-  ot_font->cmap = subtable;
-  ot_font->cmap_uvs = subtable_uvs;
+  ot_font->cmap.init (face);
+  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1);
+  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, upem); /* TODO Can we do this lazily? */
 
   return ot_font;
 }
@@ -104,8 +181,9 @@
 static void
 _hb_ot_font_destroy (hb_ot_font_t *ot_font)
 {
-  hb_blob_destroy (ot_font->cmap_blob);
-  hb_blob_destroy (ot_font->hmtx_blob);
+  ot_font->cmap.fini ();
+  ot_font->h_metrics.fini ();
+  ot_font->v_metrics.fini ();
 
   free (ot_font);
 }
@@ -121,20 +199,7 @@
 
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-
-  if (unlikely (variation_selector))
-  {
-    switch (ot_font->cmap_uvs->get_glyph_variant (unicode,
-						  variation_selector,
-						  glyph))
-    {
-      case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
-      case OT::GLYPH_VARIANT_FOUND:		return true;
-      case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
-    }
-  }
-
-  return ot_font->cmap->get_glyph (unicode, glyph);
+  return ot_font->cmap.get_glyph (unicode, variation_selector, glyph);
 }
 
 static hb_position_t
@@ -144,14 +209,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-
-  if (unlikely (glyph >= ot_font->num_glyphs))
-    return 0; /* Maybe better to return notdef's advance instead? */
-
-  if (glyph >= ot_font->num_hmetrics)
-    glyph = ot_font->num_hmetrics - 1;
-
-  return font->em_scale_x (ot_font->hmtx->longHorMetric[glyph].advanceWidth);
+  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph));
 }
 
 static hb_position_t
@@ -160,8 +218,8 @@
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
-  /* TODO */
-  return 0;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  return font->em_scale_y (-ot_font->v_metrics.get_advance (glyph));
 }
 
 static hb_bool_t
@@ -206,6 +264,7 @@
 			   hb_codepoint_t bottom_glyph HB_UNUSED,
 			   void *user_data HB_UNUSED)
 {
+  /* OpenType doesn't have vertical-kerning other than GPOS. */
   return 0;
 }
 
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index d433200..edc0e29 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -35,14 +35,19 @@
 
 /*
  * hhea -- The Horizontal Header Table
+ * vhea -- The Vertical Header Table
  */
 
 #define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
+#define HB_OT_TAG_vhea HB_TAG('v','h','e','a')
 
 
-struct hhea
+struct _hea
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_hhea;
+  static const hb_tag_t tableTag = HB_TAG('_','h','e','a');
+
+  static const hb_tag_t hheaTag	= HB_OT_TAG_hhea;
+  static const hb_tag_t vheaTag	= HB_OT_TAG_vhea;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
@@ -51,45 +56,45 @@
 
   public:
   FixedVersion	version;		/* 0x00010000u for version 1.0. */
-  FWORD		ascender;		/* Typographic ascent. <a
-					 * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
-					 * (Distance from baseline of highest
-					 * ascender)</a> */
-  FWORD		descender;		/* Typographic descent. <a
-					 * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
-					 * (Distance from baseline of lowest
-					 * descender)</a> */
-  FWORD		lineGap;		/* Typographic line gap. Negative
-					 * LineGap values are treated as zero
-					 * in Windows 3.1, System 6, and
-					 * System 7. */
-  UFWORD	advanceWidthMax;	/* Maximum advance width value in
-					 * 'hmtx' table. */
-  FWORD		minLeftSideBearing;	/* Minimum left sidebearing value in
-					 * 'hmtx' table. */
-  FWORD		minRightSideBearing;	/* Minimum right sidebearing value;
+  FWORD		ascender;		/* Typographic ascent. */
+  FWORD		descender;		/* Typographic descent. */
+  FWORD		lineGap;		/* Typographic line gap. */
+  UFWORD	advanceMax;		/* Maximum advance width/height value in
+					 * metrics table. */
+  FWORD		minLeadingBearing;	/* Minimum left/top sidebearing value in
+					 * metrics table. */
+  FWORD		minTrailingBearing;	/* Minimum right/bottom sidebearing value;
 					 * calculated as Min(aw - lsb -
-					 * (xMax - xMin)). */
-  FWORD		xMaxExtent;		/* Max(lsb + (xMax - xMin)). */
+					 * (xMax - xMin)) for horizontal. */
+  FWORD		maxExtent;		/* horizontal: Max(lsb + (xMax - xMin)),
+					 * vertical: minLeadingBearing+(yMax-yMin). */
   SHORT		caretSlopeRise;		/* Used to calculate the slope of the
-					 * cursor (rise/run); 1 for vertical. */
-  SHORT		caretSlopeRun;		/* 0 for vertical. */
+					 * cursor (rise/run); 1 for vertical caret,
+					 * 0 for horizontal.*/
+  SHORT		caretSlopeRun;		/* 0 for vertical caret, 1 for horizontal. */
   SHORT		caretOffset;		/* The amount by which a slanted
 					 * highlight on a glyph needs
 					 * to be shifted to produce the
 					 * best appearance. Set to 0 for
-					 * non--slanted fonts */
-  SHORT		reserved1;		/* set to 0 */
-  SHORT		reserved2;		/* set to 0 */
-  SHORT		reserved3;		/* set to 0 */
-  SHORT		reserved4;		/* set to 0 */
+					 * non-slanted fonts. */
+  SHORT		reserved1;		/* Set to 0. */
+  SHORT		reserved2;		/* Set to 0. */
+  SHORT		reserved3;		/* Set to 0. */
+  SHORT		reserved4;		/* Set to 0. */
   SHORT		metricDataFormat;	/* 0 for current format. */
-  USHORT	numberOfHMetrics;	/* Number of hMetric entries in 'hmtx'
-					 * table */
+  USHORT	numberOfLongMetrics;	/* Number of LongMetric entries in metric
+					 * table. */
   public:
   DEFINE_SIZE_STATIC (36);
 };
 
+struct hhea : _hea {
+  static const hb_tag_t tableTag	= HB_OT_TAG_hhea;
+};
+struct vhea : _hea {
+  static const hb_tag_t tableTag	= HB_OT_TAG_vhea;
+};
+
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index e918e3b..317854c 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -35,22 +35,27 @@
 
 /*
  * hmtx -- The Horizontal Metrics Table
+ * vmtx -- The Vertical Metrics Table
  */
 
 #define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
+#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
 
 
-struct LongHorMetric
+struct LongMetric
 {
-  USHORT	advanceWidth;
-  SHORT		lsb;
+  USHORT	advance; /* Advance width/height. */
+  SHORT		lsb; /* Leading (left/top) side bearing. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
-struct hmtx
+struct _mtx
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
+  static const hb_tag_t tableTag = HB_TAG('_','m','t','x');
+
+  static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
+  static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
@@ -60,7 +65,7 @@
   }
 
   public:
-  LongHorMetric	longHorMetric[VAR];	/* Paired advance width and left side
+  LongMetric	longMetric[VAR];	/* Paired advance width and leading
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
 					 * the 'hhea' table. If the font is
@@ -68,23 +73,29 @@
 					 * be in the array, but that entry is
 					 * required. The last entry applies to
 					 * all subsequent glyphs. */
-  SHORT		leftSideBearingX[VAR];	/* Here the advanceWidth is assumed
-					 * to be the same as the advanceWidth
+  SHORT		leadingBearingX[VAR];	/* Here the advance is assumed
+					 * to be the same as the advance
 					 * for the last entry above. The
 					 * number of entries in this array is
 					 * derived from numGlyphs (from 'maxp'
-					 * table) minus numberOfHMetrics. This
-					 * generally is used with a run of
-					 * monospaced glyphs (e.g., Kanji
+					 * table) minus numberOfLongMetrics.
+					 * This generally is used with a run
+					 * of monospaced glyphs (e.g., Kanji
 					 * fonts or Courier fonts). Only one
 					 * run is allowed and it must be at
 					 * the end. This allows a monospaced
-					 * font to vary the left side bearing
+					 * font to vary the side bearing
 					 * values for each glyph. */
   public:
-  DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX);
+  DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
 };
 
+struct hmtx : _mtx {
+  static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
+};
+struct vmtx : _mtx {
+  static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
+};
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index d8e3e6e..f7fef52 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -345,8 +345,8 @@
   inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
     *found = false;
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
-    *found = !matrix[row * cols + col].is_null ();
-    return this+matrix[row * cols + col];
+    *found = !matrixZ[row * cols + col].is_null ();
+    return this+matrixZ[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
@@ -354,19 +354,19 @@
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
+    if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
+      if (!matrixZ[i].sanitize (c, this)) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
   USHORT	rows;			/* Number of rows */
   protected:
   OffsetTo<Anchor>
-		matrix[VAR];		/* Matrix of offsets to Anchor tables--
+		matrixZ[VAR];		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
   public:
-  DEFINE_SIZE_ARRAY (2, matrix);
+  DEFINE_SIZE_ARRAY (2, matrixZ);
 };
 
 
@@ -530,7 +530,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -583,7 +583,7 @@
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
 
-    const PairValueRecord *record = CastP<PairValueRecord> (array);
+    const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -602,12 +602,24 @@
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
 
-    const PairValueRecord *record = CastP<PairValueRecord> (array);
+    const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
     unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
+
+    /* Hand-coded bsearch. */
+    if (unlikely (!count))
+      return TRACE_RETURN (false);
+    hb_codepoint_t x = buffer->info[pos].codepoint;
+    int min = 0, max = (int) count - 1;
+    while (min <= max)
     {
-      /* TODO bsearch */
-      if (buffer->info[pos].codepoint == record->secondGlyph)
+      int mid = (min + max) / 2;
+      const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
+      hb_codepoint_t mid_x = record->secondGlyph;
+      if (x < mid_x)
+        max = mid - 1;
+      else if (x > mid_x)
+        min = mid + 1;
+      else
       {
 	valueFormats[0].apply_value (c->font, c->direction, this,
 				     &record->values[0], buffer->cur_pos());
@@ -618,7 +630,6 @@
 	buffer->idx = pos;
 	return TRACE_RETURN (true);
       }
-      record = &StructAtOffset<PairValueRecord> (record, record_size);
     }
 
     return TRACE_RETURN (false);
@@ -634,20 +645,20 @@
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
-       && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
+       && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
     unsigned int count = len;
-    PairValueRecord *record = CastP<PairValueRecord> (array);
+    PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
     return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
 		      && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
   protected:
   USHORT	len;			/* Number of PairValueRecords */
-  USHORT	array[VAR];		/* Array of PairValueRecords--ordered
+  USHORT	arrayZ[VAR];		/* Array of PairValueRecords--ordered
 					 * by GlyphID of the second glyph */
   public:
-  DEFINE_SIZE_ARRAY (2, array);
+  DEFINE_SIZE_ARRAY (2, arrayZ);
 };
 
 struct PairPosFormat1
@@ -822,7 +833,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -989,7 +1000,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1088,7 +1099,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1209,7 +1220,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1328,7 +1339,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1387,7 +1398,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, lookup_type);
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.dispatch (c));
     case Pair:			return TRACE_RETURN (u.pair.dispatch (c));
@@ -1488,8 +1499,8 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
     unsigned int lookup_type = get_type ();
+    TRACE_DISPATCH (this, lookup_type);
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
       typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
@@ -1589,6 +1600,8 @@
 void
 GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
+  _hb_buffer_assert_gsubgpos_vars (buffer);
+
   unsigned int len;
   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
   hb_direction_t direction = buffer->props.direction;
@@ -1600,8 +1613,6 @@
   /* Handle attachments */
   for (unsigned int i = 0; i < len; i++)
     fix_mark_attachment (pos, i, direction);
-
-  _hb_buffer_deallocate_gsubgpos_vars (buffer);
 }
 
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index e193973..5d67be0 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -200,7 +200,7 @@
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
     unsigned int format = 2;
-    int delta;
+    int delta = 0;
     if (num_glyphs) {
       format = 1;
       /* TODO(serialize) check for wrap-around */
@@ -222,7 +222,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -422,7 +422,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -573,7 +573,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -889,7 +889,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1053,7 +1053,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
@@ -1100,7 +1100,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, lookup_type);
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.dispatch (c));
     case Multiple:		return TRACE_RETURN (u.multiple.dispatch (c));
@@ -1275,8 +1275,8 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
     unsigned int lookup_type = get_type ();
+    TRACE_DISPATCH (this, lookup_type);
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
       typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
@@ -1338,7 +1338,7 @@
 void
 GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  _hb_buffer_allocate_gsubgpos_vars (buffer);
+  _hb_buffer_assert_gsubgpos_vars (buffer);
 
   const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
   unsigned int count = buffer->len;
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 546ff4b..57fc1e0 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -38,10 +38,10 @@
 
 
 
-#define TRACE_DISPATCH(this) \
+#define TRACE_DISPATCH(this, format) \
 	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "");
+	 "format %d", (int) format);
 
 #ifndef HB_DEBUG_CLOSURE
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
@@ -168,6 +168,10 @@
     if (output == hb_set_get_empty ())
       return HB_VOID;
 
+    /* Return if new lookup was recursed to before. */
+    if (recursed_lookups.has (lookup_index))
+      return HB_VOID;
+
     hb_set_t *old_before = before;
     hb_set_t *old_input  = input;
     hb_set_t *old_after  = after;
@@ -181,6 +185,8 @@
     input  = old_input;
     after  = old_after;
 
+    recursed_lookups.add (lookup_index);
+
     return HB_VOID;
   }
 
@@ -190,6 +196,7 @@
   hb_set_t *after;
   hb_set_t *output;
   recurse_func_t recurse_func;
+  hb_set_t recursed_lookups;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
@@ -205,18 +212,30 @@
 			      after  (glyphs_after  ? glyphs_after  : hb_set_get_empty ()),
 			      output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
 			      recurse_func (NULL),
+			      recursed_lookups (),
 			      nesting_level_left (nesting_level_left_),
-			      debug_depth (0) {}
+			      debug_depth (0)
+  {
+    recursed_lookups.init ();
+  }
+  ~hb_collect_glyphs_context_t (void)
+  {
+    recursed_lookups.fini ();
+  }
 
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 };
 
 
 
+#ifndef HB_DEBUG_GET_COVERAGE
+#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
+#endif
+
 struct hb_get_coverage_context_t
 {
   inline const char *get_name (void) { return "GET_COVERAGE"; }
-  static const unsigned int max_debug_depth = 0;
+  static const unsigned int max_debug_depth = HB_DEBUG_GET_COVERAGE;
   typedef const Coverage &return_t;
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.get_coverage (); }
@@ -1117,9 +1136,9 @@
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_closure_lookup (c,
-			    inputCount, input,
+			    inputCount, inputZ,
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
@@ -1127,9 +1146,9 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_collect_glyphs_lookup (c,
-				   inputCount, input,
+				   inputCount, inputZ,
 				   lookupCount, lookupRecord,
 				   lookup_context);
   }
@@ -1137,15 +1156,15 @@
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return TRACE_RETURN (context_would_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return TRACE_RETURN (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return TRACE_RETURN (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
@@ -1153,8 +1172,8 @@
     TRACE_SANITIZE (this);
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
-	&& c->check_range (input,
-			   input[0].static_size * inputCount
+	&& c->check_range (inputZ,
+			   inputZ[0].static_size * inputCount
 			   + lookupRecordX[0].static_size * lookupCount);
   }
 
@@ -1163,12 +1182,12 @@
 					 * glyph sequence--includes the first
 					 * glyph */
   USHORT	lookupCount;		/* Number of LookupRecords */
-  USHORT	input[VAR];		/* Array of match inputs--start with
+  USHORT	inputZ[VAR];		/* Array of match inputs--start with
 					 * second glyph */
   LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
+  DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX);
 };
 
 struct RuleSet
@@ -1413,16 +1432,16 @@
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    if (!(this+coverage[0]).intersects (c->glyphs))
+    if (!(this+coverageZ[0]).intersects (c->glyphs))
       return;
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextClosureLookupContext lookup_context = {
       {intersects_coverage},
       this
     };
     context_closure_lookup (c,
-			    glyphCount, (const USHORT *) (coverage + 1),
+			    glyphCount, (const USHORT *) (coverageZ + 1),
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
@@ -1430,16 +1449,16 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage[0]).add_coverage (c->input);
+    (this+coverageZ[0]).add_coverage (c->input);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextCollectGlyphsLookupContext lookup_context = {
       {collect_coverage},
       this
     };
 
     context_collect_glyphs_lookup (c,
-				   glyphCount, (const USHORT *) (coverage + 1),
+				   glyphCount, (const USHORT *) (coverageZ + 1),
 				   lookupCount, lookupRecord,
 				   lookup_context);
   }
@@ -1448,41 +1467,42 @@
   {
     TRACE_WOULD_APPLY (this);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
+    return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
   {
-    return this+coverage[0];
+    return this+coverageZ[0];
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    unsigned int index = (this+coverage[0]).get_coverage (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
+    return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
-    if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
+    if (!count) return TRACE_RETURN (false); /* We want to access coverageZ[0] freely. */
+    if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
-    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
+      if (!coverageZ[i].sanitize (c, this)) return TRACE_RETURN (false);
+    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count);
     return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
@@ -1492,12 +1512,12 @@
 					 * sequence */
   USHORT	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
-		coverage[VAR];		/* Array of offsets to Coverage
+		coverageZ[VAR];		/* Array of offsets to Coverage
 					 * table in glyph sequence order */
   LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
+  DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX);
 };
 
 struct Context
@@ -1505,7 +1525,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -2090,6 +2110,7 @@
     if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!input.sanitize (c, this)) return TRACE_RETURN (false);
+    if (!input.len) return TRACE_RETURN (false); /* To be consistent with Context. */
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
@@ -2122,7 +2143,7 @@
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_DISPATCH (this);
+    TRACE_DISPATCH (this, u.format);
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index d94ac50..3f7c858 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -126,8 +126,7 @@
     lookup.add_coverage (&digest);
   }
 
-  template <typename TLookup>
-  inline void fini (const TLookup &lookup HB_UNUSED)
+  inline void fini (void)
   {
   }
 
@@ -420,6 +419,13 @@
 }
 
 static inline void
+_hb_buffer_assert_unicode_vars (hb_buffer_t *buffer)
+{
+  HB_BUFFER_ASSERT_VAR (buffer, unicode_props0);
+  HB_BUFFER_ASSERT_VAR (buffer, unicode_props1);
+}
+
+static inline void
 _hb_buffer_allocate_gsubgpos_vars (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props);
@@ -435,6 +441,14 @@
   HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props);
 }
 
+static inline void
+_hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
+{
+  HB_BUFFER_ASSERT_VAR (buffer, glyph_props);
+  HB_BUFFER_ASSERT_VAR (buffer, lig_props);
+  HB_BUFFER_ASSERT_VAR (buffer, syllable);
+}
+
 /* Make sure no one directly touches our props... */
 #undef unicode_props0
 #undef unicode_props1
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 661d90e..602b94e 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -84,9 +84,9 @@
 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 {
   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub_accels[i].fini (layout->gsub->get_lookup (i));
+    layout->gsub_accels[i].fini ();
   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos_accels[i].fini (layout->gpos->get_lookup (i));
+    layout->gpos_accels[i].fini ();
 
   free (layout->gsub_accels);
   free (layout->gpos_accels);
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index 2d8488e..a77f24e 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -33,6 +33,8 @@
 #include "hb-ot-layout-gsub-table.hh"
 
 
+/* Features ordered the same as the entries in shaping_table rows,
+ * followed by rlig.  Don't change. */
 static const hb_tag_t arabic_fallback_features[] =
 {
   HB_TAG('i','n','i','t'),
@@ -42,16 +44,6 @@
   HB_TAG('r','l','i','g'),
 };
 
-/* Same order as the fallback feature array */
-enum {
-  FALLBACK_INIT,
-  FALLBACK_MEDI,
-  FALLBACK_FINA,
-  FALLBACK_ISOL,
-  FALLBACK_RLIG,
-  ARABIC_NUM_FALLBACK_FEATURES
-};
-
 static OT::SubstLookup *
 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED,
 					  hb_font_t *font,
@@ -80,6 +72,9 @@
     num_glyphs++;
   }
 
+  if (!num_glyphs)
+    return NULL;
+
   /* Bubble-sort!
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
   hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]);
@@ -157,6 +152,9 @@
     }
   }
 
+  if (!num_ligatures)
+    return NULL;
+
   OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
   OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
   OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
@@ -193,17 +191,108 @@
     return arabic_fallback_synthesize_lookup_ligature (plan, font);
 }
 
+#define ARABIC_FALLBACK_MAX_LOOKUPS 5
+
 struct arabic_fallback_plan_t
 {
   ASSERT_POD ();
 
-  hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES];
-  OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES];
-  hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_NUM_FALLBACK_FEATURES];
+  unsigned int num_lookups;
+  bool free_lookups;
+
+  hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS];
+  OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS];
+  hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 };
 
 static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
 
+#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
+#define HB_WITH_WIN1256
+#endif
+
+#ifdef HB_WITH_WIN1256
+#include "hb-ot-shape-complex-arabic-win1256.hh"
+#endif
+
+struct ManifestLookup {
+  OT::Tag tag;
+  OT::OffsetTo<OT::SubstLookup> lookupOffset;
+};
+typedef OT::ArrayOf<ManifestLookup> Manifest;
+
+static bool
+arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan,
+				   const hb_ot_shape_plan_t *plan,
+				   hb_font_t *font)
+{
+#ifdef HB_WITH_WIN1256
+  /* Does this font look like it's Windows-1256-encoded? */
+  hb_codepoint_t g;
+  if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ &&
+	hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ &&
+	hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ &&
+	hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ &&
+	hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */))
+    return false;
+
+  const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest);
+  ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup)
+		 <= ARABIC_FALLBACK_MAX_LOOKUPS);
+  /* TODO sanitize the table? */
+
+  unsigned j = 0;
+  unsigned int count = manifest.len;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag);
+    if (fallback_plan->mask_array[j])
+    {
+      fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset));
+      if (fallback_plan->lookup_array[j])
+      {
+	fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]);
+	j++;
+      }
+    }
+  }
+
+  fallback_plan->num_lookups = j;
+  fallback_plan->free_lookups = false;
+
+  return j > 0;
+#else
+  return false;
+#endif
+}
+
+static bool
+arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan,
+				   const hb_ot_shape_plan_t *plan,
+				   hb_font_t *font)
+{
+  ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS);
+  unsigned int j = 0;
+  for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++)
+  {
+    fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]);
+    if (fallback_plan->mask_array[j])
+    {
+      fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i);
+      if (fallback_plan->lookup_array[j])
+      {
+	fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]);
+	j++;
+      }
+    }
+  }
+
+  fallback_plan->num_lookups = j;
+  fallback_plan->free_lookups = true;
+
+  return j > 0;
+}
+
 static arabic_fallback_plan_t *
 arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan,
 			     hb_font_t *font)
@@ -212,17 +301,21 @@
   if (unlikely (!fallback_plan))
     return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
 
-  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
-  {
-    fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_features[i]);
-    if (fallback_plan->mask_array[i]) {
-      fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan, font, i);
-      if (fallback_plan->lookup_array[i])
-	fallback_plan->accel_array[i].init (*fallback_plan->lookup_array[i]);
-    }
-  }
+  fallback_plan->num_lookups = 0;
+  fallback_plan->free_lookups = false;
 
-  return fallback_plan;
+  /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms,
+   * in case the font has cmap entries for the presentation-forms characters. */
+  if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font))
+    return fallback_plan;
+
+  /* See if this looks like a Windows-1256-encoded font.  If it does, use a
+   * hand-coded GSUB table. */
+  if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font))
+    return fallback_plan;
+
+  free (fallback_plan);
+  return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
 }
 
 static void
@@ -231,11 +324,12 @@
   if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
     return;
 
-  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+  for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i])
     {
-      fallback_plan->accel_array[i].fini (fallback_plan->lookup_array[i]);
-      free (fallback_plan->lookup_array[i]);
+      fallback_plan->accel_array[i].fini ();
+      if (fallback_plan->free_lookups)
+	free (fallback_plan->lookup_array[i]);
     }
 
   free (fallback_plan);
@@ -247,7 +341,7 @@
 			    hb_buffer_t *buffer)
 {
   OT::hb_apply_context_t c (0, font, buffer);
-  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+  for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i]) {
       c.set_lookup_mask (fallback_plan->mask_array[i]);
       hb_ot_layout_substitute_lookup (&c,
diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh
index d41d6ce..1710049 100644
--- a/src/hb-ot-shape-complex-arabic-table.hh
+++ b/src/hb-ot-shape-complex-arabic-table.hh
@@ -70,7 +70,7 @@
 
   /* Mandaic */
 
-  /* 0840 */ R,D,D,D,D,D,R,D,D,R,D,D,D,D,D,R,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X,
+  /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X,
   /* 0860 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 0880 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
 
diff --git a/src/hb-ot-shape-complex-arabic-win1256.hh b/src/hb-ot-shape-complex-arabic-win1256.hh
new file mode 100644
index 0000000..3a20b50
--- /dev/null
+++ b/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -0,0 +1,324 @@
+/*
+ * Copyright © 2014  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
+
+
+/*
+ * The macros in the first part of this file are generic macros that can
+ * be used to define the bytes for OpenType table data in code in a
+ * readable manner.  We can move the macros to reside with their respective
+ * struct types, but since we only use these to define one data table, the
+ * Windows-1256 Arabic shaping table in this file, we keep them here.
+ */
+
+
+/* First we measure, then we cut. */
+#ifndef OT_MEASURE
+#define OT_MEASURE
+#define OT_TABLE_START			static const struct TABLE_NAME {
+#define OT_TABLE_END			}
+#define OT_LABEL_START(Name)		unsigned char Name[
+#define OT_LABEL_END			];
+#define OT_BYTE(u8)			+1/*byte*/
+#define OT_USHORT(u16)			+2/*bytes*/
+#else
+#undef  OT_MEASURE
+#define OT_TABLE_START			TABLE_NAME = {
+#define OT_TABLE_END			};
+#define OT_LABEL_START(Name)		{
+#define OT_LABEL_END			},
+#define OT_BYTE(u8)			(u8),
+#define OT_USHORT(u16)			(unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu),
+#define OT_COUNT(Name, ItemSize)	((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \
+					 / (unsigned int)(ItemSize) \
+					 /* OT_ASSERT it's divisible (and positive). */)
+#define OT_DISTANCE(From,To)		((unsigned int) \
+					 ((char*)(&((struct TABLE_NAME*)0)->To) - \
+					  (char*)(&((struct TABLE_NAME*)0)->From)) \
+					 /* OT_ASSERT it's positive. */)
+#endif
+
+
+#define OT_LABEL(Name) \
+	OT_LABEL_END \
+	OT_LABEL_START(Name)
+
+/* Whenever we receive an argument that is a list, it will expand to
+ * contain commas.  That cannot be passed to another macro because the
+ * commas will throw off the preprocessor.  The solution is to wrap
+ * the passed-in argument in OT_LIST() before passing to the next macro.
+ * Unfortunately this trick requires vararg macros. */
+#define OT_LIST(...) __VA_ARGS__
+
+
+/*
+ * Basic Types
+ */
+
+#define OT_TAG(a,b,c,d) \
+	OT_BYTE(a) OT_BYTE(b) OT_BYTE(c) OT_BYTE(d)
+
+#define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \
+	OT_USHORT(OT_DISTANCE(From, To))
+
+#define OT_GLYPHID /* GlyphID */ \
+	OT_USHORT
+
+#define OT_UARRAY(Name, Items) \
+	OT_LABEL_START(Name) \
+	OT_USHORT(OT_COUNT(Name##Data, 2)) \
+	OT_LABEL(Name##Data) \
+	Items \
+	OT_LABEL_END
+
+#define OT_UHEADLESSARRAY(Name, Items) \
+	OT_LABEL_START(Name) \
+	OT_USHORT(OT_COUNT(Name##Data, 2) + 1) \
+	OT_LABEL(Name##Data) \
+	Items \
+	OT_LABEL_END
+
+
+/*
+ * Common Types
+ */
+
+#define OT_LOOKUP_FLAG_IGNORE_MARKS	0x08u
+
+#define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \
+	OT_LABEL_START(Name) \
+	OT_USHORT(LookupType) \
+	OT_USHORT(LookupFlag) \
+	OT_LABEL_END \
+	OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets))
+
+#define OT_SUBLOOKUP(Name, SubFormat, Items) \
+	OT_LABEL_START(Name) \
+	OT_USHORT(SubFormat) \
+	Items
+
+#define OT_COVERAGE1(Name, Items) \
+	OT_LABEL_START(Name) \
+	OT_USHORT(1) \
+	OT_LABEL_END \
+	OT_UARRAY(Name##Glyphs, OT_LIST(Items))
+
+
+/*
+ * GSUB
+ */
+
+#define OT_LOOKUP_TYPE_SUBST_SINGLE	1u
+#define OT_LOOKUP_TYPE_SUBST_MULTIPLE	2u
+#define OT_LOOKUP_TYPE_SUBST_LIGATURE	4u
+
+#define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \
+	OT_SUBLOOKUP(Name, 2, \
+		OT_OFFSET(Name, Name##Coverage) \
+		OT_LABEL_END \
+		OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \
+	) \
+	OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \
+	/* ASSERT_STATIC_EXPR len(FromGlyphs) == len(ToGlyphs) */
+
+#define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \
+	OT_SUBLOOKUP(Name, 1, \
+		OT_OFFSET(Name, Name##Coverage) \
+		OT_LABEL_END \
+		OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \
+	) \
+	OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \
+	/* ASSERT_STATIC_EXPR len(FirstGlyphs) == len(LigatureSetOffsets) */
+
+#define OT_LIGATURE_SET(Name, LigatureSetOffsets) \
+	OT_UARRAY(Name, OT_LIST(LigatureSetOffsets))
+
+#define OT_LIGATURE(Name, Components, LigGlyph) \
+	OT_LABEL_START(Name) \
+	LigGlyph \
+	OT_LABEL_END \
+	OT_UHEADLESSARRAY(Name##ComponentsArray, OT_LIST(Components))
+
+/*
+ *
+ * Start of Windows-1256 shaping table.
+ *
+ */
+
+/* Table name. */
+#define TABLE_NAME arabic_win1256_gsub_lookups
+
+/* Table manifest. */
+#define MANIFEST(Items) \
+	OT_LABEL_START(manifest) \
+	OT_USHORT(OT_COUNT(manifestData, 6)) \
+	OT_LABEL(manifestData) \
+	Items \
+	OT_LABEL_END
+
+#define MANIFEST_LOOKUP(Tag, Name) \
+	Tag \
+	OT_OFFSET(manifest, Name)
+
+/* Shorthand. */
+#define G	OT_GLYPHID
+
+/*
+ * Table Start
+ */
+OT_TABLE_START
+
+
+/*
+ * Manifest
+ */
+MANIFEST(
+	MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligLookup)
+	MANIFEST_LOOKUP(OT_TAG('i','n','i','t'), initLookup)
+	MANIFEST_LOOKUP(OT_TAG('m','e','d','i'), mediLookup)
+	MANIFEST_LOOKUP(OT_TAG('f','i','n','a'), finaLookup)
+	MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligMarksLookup)
+)
+
+/*
+ * Lookups
+ */
+OT_LOOKUP(initLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS,
+	OT_OFFSET(initLookup, initmediSubLookup)
+	OT_OFFSET(initLookup, initSubLookup)
+)
+OT_LOOKUP(mediLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS,
+	OT_OFFSET(mediLookup, initmediSubLookup)
+	OT_OFFSET(mediLookup, mediSubLookup)
+	OT_OFFSET(mediLookup, medifinaLamAlefSubLookup)
+)
+OT_LOOKUP(finaLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS,
+	OT_OFFSET(finaLookup, finaSubLookup)
+	/* We don't need this one currently as the sequence inherits masks
+	 * from the first item.  Just in case we change that in the future
+	 * to be smart about Arabic masks when ligating... */
+	OT_OFFSET(finaLookup, medifinaLamAlefSubLookup)
+)
+OT_LOOKUP(rligLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, OT_LOOKUP_FLAG_IGNORE_MARKS,
+	OT_OFFSET(rligLookup, lamAlefLigaturesSubLookup)
+)
+OT_LOOKUP(rligMarksLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, 0,
+	OT_OFFSET(rligMarksLookup, shaddaLigaturesSubLookup)
+)
+
+/*
+ * init/medi/fina forms
+ */
+OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initmediSubLookup,
+	G(198)	G(200)	G(201)	G(202)	G(203)	G(204)	G(205)	G(206)	G(211)
+	G(212)	G(213)	G(214)	G(223)	G(225)	G(227)	G(228)	G(236)	G(237),
+	G(162)	G(4)	G(5)	G(5)	G(6)	G(7)	G(9)	G(11)	G(13)
+	G(14)	G(15)	G(26)	G(140)	G(141)	G(142)	G(143)	G(154)	G(154)
+)
+OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initSubLookup,
+	G(218)	G(219)	G(221)	G(222)	G(229),
+	G(27)	G(30)	G(128)	G(131)	G(144)
+)
+OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(mediSubLookup,
+	G(218)	G(219)	G(221)	G(222)	G(229),
+	G(28)	G(31)	G(129)	G(138)	G(149)
+)
+OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(finaSubLookup,
+	G(194)	G(195)	G(197)	G(198)	G(199)	G(201)	G(204)	G(205)	G(206)
+	G(218)	G(219)	G(229)	G(236)	G(237),
+	G(2)	G(1)	G(3)	G(181)	G(0)	G(159)	G(8)	G(10)	G(12)
+	G(29)	G(127)	G(152) G(160)	G(156)
+)
+OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(medifinaLamAlefSubLookup,
+	G(165)	G(178)	G(180)	G(252),
+	G(170)	G(179)	G(185)	G(255)
+)
+
+/*
+ * Lam+Alef ligatures
+ */
+OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(lamAlefLigaturesSubLookup,
+	G(225),
+	OT_OFFSET(lamAlefLigaturesSubLookup, lamLigatureSet)
+)
+OT_LIGATURE_SET(lamLigatureSet,
+	OT_OFFSET(lamLigatureSet, lamInitLigature1)
+	OT_OFFSET(lamLigatureSet, lamInitLigature2)
+	OT_OFFSET(lamLigatureSet, lamInitLigature3)
+	OT_OFFSET(lamLigatureSet, lamInitLigature4)
+)
+OT_LIGATURE(lamInitLigature1, G(199), G(165))
+OT_LIGATURE(lamInitLigature2, G(195), G(178))
+OT_LIGATURE(lamInitLigature3, G(194), G(180))
+OT_LIGATURE(lamInitLigature4, G(197), G(252))
+
+/*
+ * Shadda ligatures
+ */
+OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(shaddaLigaturesSubLookup,
+	G(248),
+	OT_OFFSET(shaddaLigaturesSubLookup, shaddaLigatureSet)
+)
+OT_LIGATURE_SET(shaddaLigatureSet,
+	OT_OFFSET(shaddaLigatureSet, shaddaLigature1)
+	OT_OFFSET(shaddaLigatureSet, shaddaLigature2)
+	OT_OFFSET(shaddaLigatureSet, shaddaLigature3)
+)
+OT_LIGATURE(shaddaLigature1, G(243), G(172))
+OT_LIGATURE(shaddaLigature2, G(245), G(173))
+OT_LIGATURE(shaddaLigature3, G(246), G(175))
+
+/*
+ * Table end
+ */
+OT_TABLE_END
+
+
+/*
+ * Clean up
+ */
+#undef OT_TABLE_START
+#undef OT_TABLE_END
+#undef OT_LABEL_START
+#undef OT_LABEL_END
+#undef OT_BYTE
+#undef OT_USHORT
+#undef OT_DISTANCE
+#undef OT_COUNT
+
+/*
+ * Include a second time to get the table data...
+ */
+#if 0
+#include "hb-private.hh" /* Make check-includes.sh happy. */
+#endif
+#ifdef OT_MEASURE
+#include "hb-ot-shape-complex-arabic-win1256.hh"
+#endif
+
+#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH */
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 9870ba3..ae90864 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -223,8 +223,8 @@
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
     arabic_plan->do_fallback = arabic_plan->do_fallback &&
-			       !FEATURE_IS_SYRIAC (arabic_features[i]) &&
-			       plan->map.needs_fallback (arabic_features[i]);
+			       (FEATURE_IS_SYRIAC (arabic_features[i]) ||
+			        plan->map.needs_fallback (arabic_features[i]));
   }
 
   return arabic_plan;
@@ -248,18 +248,17 @@
   unsigned int prev = (unsigned int) -1, state = 0;
 
   /* Check pre-context */
-  if (!(buffer->flags & HB_BUFFER_FLAG_BOT))
-    for (unsigned int i = 0; i < buffer->context_len[0]; i++)
-    {
-      unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
+  for (unsigned int i = 0; i < buffer->context_len[0]; i++)
+  {
+    unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
 
-      if (unlikely (this_type == JOINING_TYPE_T))
-	continue;
+    if (unlikely (this_type == JOINING_TYPE_T))
+      continue;
 
-      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
-      state = entry->next_state;
-      break;
-    }
+    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+    state = entry->next_state;
+    break;
+  }
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -281,19 +280,18 @@
     state = entry->next_state;
   }
 
-  if (!(buffer->flags & HB_BUFFER_FLAG_EOT))
-    for (unsigned int i = 0; i < buffer->context_len[1]; i++)
-    {
-      unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i]));
+  for (unsigned int i = 0; i < buffer->context_len[1]; i++)
+  {
+    unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i]));
 
-      if (unlikely (this_type == JOINING_TYPE_T))
-	continue;
+    if (unlikely (this_type == JOINING_TYPE_T))
+      continue;
 
-      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
-      if (entry->prev_action != NONE && prev != (unsigned int) -1)
-	info[prev].arabic_shaping_action() = entry->prev_action;
-      break;
-    }
+    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+    if (entry->prev_action != NONE && prev != (unsigned int) -1)
+      info[prev].arabic_shaping_action() = entry->prev_action;
+    break;
+  }
 }
 
 static void
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index 54c12eb..6ac18b0 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -59,6 +59,15 @@
     map->add_feature (hangul_features[i], 1, F_NONE);
 }
 
+static void
+override_features_hangul (hb_ot_shape_planner_t *plan)
+{
+  /* Uniscribe does not apply 'calt' for Hangul, and certain fonts
+   * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups
+   * in calt, which is not desirable. */
+  plan->map.add_feature (HB_TAG('c','a','l','t'), 0, F_GLOBAL);
+}
+
 struct hangul_shape_plan_t
 {
   ASSERT_POD ();
@@ -404,7 +413,7 @@
 {
   "hangul",
   collect_features_hangul,
-  NULL, /* override_features */
+  override_features_hangul,
   data_create_hangul, /* data_create */
   data_destroy_hangul, /* data_destroy */
   preprocess_text_hangul,
diff --git a/src/hb-ot-shape-complex-hebrew.cc b/src/hb-ot-shape-complex-hebrew.cc
index 2381a6e..c7b7a5e 100644
--- a/src/hb-ot-shape-complex-hebrew.cc
+++ b/src/hb-ot-shape-complex-hebrew.cc
@@ -167,6 +167,6 @@
   NULL, /* decompose */
   compose_hebrew,
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index f6768a5..694b235 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -69,7 +69,7 @@
 forced_rakar = ZWJ H ZWJ Ra;
 symbol = Symbol.N?;
 matra_group = z{0,3}.M.N?.(H | forced_rakar)?;
-syllable_tail = (SM.SM?.ZWNJ?)? A{0,3}? VD{0,2};
+syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}? VD{0,2};
 place_holder = PLACEHOLDER | DOTTEDCIRCLE;
 halant_group = (z?.h.(ZWJ.N?)?);
 final_halant_group = halant_group | h.ZWNJ;
diff --git a/src/hb-ot-shape-complex-indic-table.cc b/src/hb-ot-shape-complex-indic-table.cc
index f58380e..2e159a1 100644
--- a/src/hb-ot-shape-complex-indic-table.cc
+++ b/src/hb-ot-shape-complex-indic-table.cc
@@ -861,41 +861,41 @@
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0028u, 0x0040u)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
-      if (hb_in_range (u, 0x00D0u, 0x00D8u)) return indic_table[u - 0x00D0u + indic_offset_0x00d0u];
-      if (hb_in_range (u, 0x0900u, 0x0DF8u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
+      if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
+      if (hb_in_range (u, 0x00D0u, 0x00D7u)) return indic_table[u - 0x00D0u + indic_offset_0x00d0u];
+      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
       if (unlikely (u == 0x00A0u)) return _(CP,x);
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1000u, 0x10A0u)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
-      if (hb_in_range (u, 0x1700u, 0x17F0u)) return indic_table[u - 0x1700u + indic_offset_0x1700u];
-      if (hb_in_range (u, 0x1900u, 0x1AA0u)) return indic_table[u - 0x1900u + indic_offset_0x1900u];
-      if (hb_in_range (u, 0x1B00u, 0x1C50u)) return indic_table[u - 0x1B00u + indic_offset_0x1b00u];
-      if (hb_in_range (u, 0x1CD0u, 0x1CF8u)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
+      if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
+      if (hb_in_range (u, 0x1700u, 0x17EFu)) return indic_table[u - 0x1700u + indic_offset_0x1700u];
+      if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return indic_table[u - 0x1900u + indic_offset_0x1900u];
+      if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return indic_table[u - 0x1B00u + indic_offset_0x1b00u];
+      if (hb_in_range (u, 0x1CD0u, 0x1CF7u)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x2008u, 0x2018u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
+      if (hb_in_range (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
       if (unlikely (u == 0x25CCu)) return _(CP,x);
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA800u, 0xAAF8u)) return indic_table[u - 0xA800u + indic_offset_0xa800u];
-      if (hb_in_range (u, 0xABC0u, 0xAC00u)) return indic_table[u - 0xABC0u + indic_offset_0xabc0u];
+      if (hb_in_range (u, 0xA800u, 0xAAF7u)) return indic_table[u - 0xA800u + indic_offset_0xa800u];
+      if (hb_in_range (u, 0xABC0u, 0xABFFu)) return indic_table[u - 0xABC0u + indic_offset_0xabc0u];
       break;
 
     case 0x10u:
-      if (hb_in_range (u, 0x10A00u, 0x10A48u)) return indic_table[u - 0x10A00u + indic_offset_0x10a00u];
+      if (hb_in_range (u, 0x10A00u, 0x10A47u)) return indic_table[u - 0x10A00u + indic_offset_0x10a00u];
       break;
 
     case 0x11u:
-      if (hb_in_range (u, 0x11000u, 0x110C0u)) return indic_table[u - 0x11000u + indic_offset_0x11000u];
-      if (hb_in_range (u, 0x11100u, 0x11238u)) return indic_table[u - 0x11100u + indic_offset_0x11100u];
-      if (hb_in_range (u, 0x112B0u, 0x11378u)) return indic_table[u - 0x112B0u + indic_offset_0x112b0u];
-      if (hb_in_range (u, 0x11480u, 0x114E0u)) return indic_table[u - 0x11480u + indic_offset_0x11480u];
-      if (hb_in_range (u, 0x11580u, 0x115C8u)) return indic_table[u - 0x11580u + indic_offset_0x11580u];
-      if (hb_in_range (u, 0x11600u, 0x116D0u)) return indic_table[u - 0x11600u + indic_offset_0x11600u];
+      if (hb_in_range (u, 0x11000u, 0x110BFu)) return indic_table[u - 0x11000u + indic_offset_0x11000u];
+      if (hb_in_range (u, 0x11100u, 0x11237u)) return indic_table[u - 0x11100u + indic_offset_0x11100u];
+      if (hb_in_range (u, 0x112B0u, 0x11377u)) return indic_table[u - 0x112B0u + indic_offset_0x112b0u];
+      if (hb_in_range (u, 0x11480u, 0x114DFu)) return indic_table[u - 0x11480u + indic_offset_0x11480u];
+      if (hb_in_range (u, 0x11580u, 0x115C7u)) return indic_table[u - 0x11580u + indic_offset_0x11580u];
+      if (hb_in_range (u, 0x11600u, 0x116CFu)) return indic_table[u - 0x11600u + indic_offset_0x11600u];
       break;
 
     default:
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index be5d574..7723600 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -1285,6 +1285,7 @@
       info.cluster = buffer->cur().cluster;
       info.mask = buffer->cur().mask;
       info.syllable() = buffer->cur().syllable();
+      /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
       while (buffer->idx < buffer->len &&
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index 258ccc4..d016380 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -536,6 +536,24 @@
 }
 
 
+/* Uniscribe seems to have a shaper for 'mymr' that is like the
+ * generic shaper, except that it zeros mark advances GDEF_LATE. */
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old =
+{
+  "default",
+  NULL, /* collect_features */
+  NULL, /* override_features */
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+  NULL, /* decompose */
+  NULL, /* compose */
+  NULL, /* setup_masks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+  true, /* fallback_position */
+};
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 {
   "myanmar",
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 3e581af..e268933 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -56,6 +56,7 @@
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
   HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
@@ -258,6 +259,7 @@
 
     /* Unicode-4.1 additions */
     case HB_SCRIPT_KHAROSHTHI:
+    case HB_SCRIPT_NEW_TAI_LUE:
     case HB_SCRIPT_SYLOTI_NAGRI:
 
     /* Unicode-5.1 additions */
@@ -329,16 +331,15 @@
 	return &_hb_ot_complex_shaper_default;
 
     case HB_SCRIPT_MYANMAR:
-      /* For Myanmar, we only want to use the Myanmar shaper if the "new" script
-       * tag is found.  For "old" script tag we want to use the default shaper. */
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
 	return &_hb_ot_complex_shaper_myanmar;
+      else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
+	return &_hb_ot_complex_shaper_myanmar_old;
       else
 	return &_hb_ot_complex_shaper_default;
 
     /* Unicode-4.1 additions */
     case HB_SCRIPT_BUGINESE:
-    case HB_SCRIPT_NEW_TAI_LUE:
 
     /* Unicode-5.1 additions */
     case HB_SCRIPT_CHAM:
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index a774f95..80d7da8 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -415,6 +415,8 @@
 				hb_font_t *font,
 				hb_buffer_t  *buffer)
 {
+  _hb_buffer_assert_gsubgpos_vars (buffer);
+
   unsigned int start = 0;
   unsigned int last_cluster = buffer->info[0].cluster;
   unsigned int count = buffer->len;
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 2a6a439..4287253 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -289,6 +289,8 @@
 			hb_buffer_t *buffer,
 			hb_font_t *font)
 {
+  _hb_buffer_assert_unicode_vars (buffer);
+
   hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference;
   const hb_ot_shape_normalize_context_t c = {
     plan,
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 1f99014..a0b503a 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -43,7 +43,6 @@
 
 static hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
-  HB_TAG('l','i','g','a'),
   HB_TAG('l','o','c','l'),
   HB_TAG('m','a','r','k'),
   HB_TAG('m','k','m','k'),
@@ -56,6 +55,7 @@
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
+  HB_TAG('l','i','g','a'),
   HB_TAG('r','c','l','t'),
 };
 
@@ -236,6 +236,7 @@
 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
 {
   if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
+      buffer->context_len[0] ||
       _hb_glyph_info_get_general_category (&buffer->info[0]) !=
       HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
     return;
@@ -243,7 +244,7 @@
   if (!font->has_glyph (0x25CCu))
     return;
 
-  hb_glyph_info_t dottedcircle;
+  hb_glyph_info_t dottedcircle = {0};
   dottedcircle.codepoint = 0x25CCu;
   _hb_glyph_info_set_unicode_props (&dottedcircle, buffer->unicode);
 
@@ -447,6 +448,7 @@
 {
   hb_buffer_t *buffer = c->buffer;
 
+  _hb_buffer_allocate_gsubgpos_vars (buffer);
   hb_ot_layout_substitute_start (c->font, buffer);
 
   if (!hb_ot_layout_has_glyph_classes (c->face))
@@ -635,6 +637,8 @@
 
   if (fallback)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
+
+  _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 5a4ca69..c92cdec 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -96,6 +96,8 @@
 
 #if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
 #define snprintf _snprintf
+/* Windows CE only has _strdup, while rest of Windows has both. */
+#define strdup _strdup
 #endif
 
 #ifdef _MSC_VER
@@ -126,10 +128,47 @@
 #  ifndef _WIN32_WINNT
 #    define _WIN32_WINNT 0x0600
 #  endif
-#  define WIN32_LEAN_AND_MEAN
-#  define STRICT
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN 1
+#  endif
+#  ifndef STRICT
+#    define STRICT 1
+#  endif
 #endif
 
+#ifdef _WIN32_WCE
+/* Some things not defined on Windows CE. */
+#define MemoryBarrier()
+#define getenv(Name) NULL
+#define setlocale(Category, Locale) "C"
+static int errno = 0; /* Use something better? */
+#endif
+
+#if HAVE_ATEXIT
+/* atexit() is only safe to be called from shared libraries on certain
+ * platforms.  Whitelist.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */
+#  if defined(__linux) && defined(__GLIBC_PREREQ)
+#    if __GLIBC_PREREQ(2,3)
+/* From atexit() manpage, it's safe with glibc 2.2.3 on Linux. */
+#      define HB_USE_ATEXIT 1
+#    endif
+#  elif defined(_MSC_VER) || defined(__MINGW32__)
+/* For MSVC:
+ * http://msdn.microsoft.com/en-ca/library/tze57ck3.aspx
+ * http://msdn.microsoft.com/en-ca/library/zk17ww08.aspx
+ * mingw32 headers say atexit is safe to use in shared libraries.
+ */
+#    define HB_USE_ATEXIT 1
+#  elif defined(__ANDROID__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+/* This was fixed in Android NKD r8 or r8b:
+ * https://code.google.com/p/android/issues/detail?id=6455
+ * which introduced GCC 4.6:
+ * https://developer.android.com/tools/sdk/ndk/index.html
+ */
+#    define HB_USE_ATEXIT 1
+#  endif
+#endif
 
 /* Basics */
 
@@ -500,47 +539,6 @@
 };
 
 
-
-
-/* Big-endian handling */
-
-static inline uint16_t hb_be_uint16 (const uint16_t v)
-{
-  const uint8_t *V = (const uint8_t *) &v;
-  return (V[0] << 8) | V[1];
-}
-
-static inline uint16_t hb_uint16_swap (const uint16_t v)
-{
-  return (v >> 8) | (v << 8);
-}
-
-static inline uint32_t hb_uint32_swap (const uint32_t v)
-{
-  return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16);
-}
-
-/* Note, of the following macros, uint16_get is the one called many many times.
- * If there is any optimizations to be done, it's in that macro.  However, I
- * already confirmed that on my T400 ThinkPad at least, using bswap_16(), which
- * results in a single ror instruction, does NOT speed this up.  In fact, it
- * resulted in a minor slowdown.  At any rate, note that v may not be correctly
- * aligned, so I think the current implementation is optimal.
- */
-
-#define hb_be_uint16_put(v,V)	HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
-#define hb_be_uint16_get(v)	(uint16_t) ((v[0] << 8) + v[1])
-#define hb_be_uint16_eq(a,b)	(a[0] == b[0] && a[1] == b[1])
-
-#define hb_be_uint32_put(v,V)	HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END
-#define hb_be_uint32_get(v)	(uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
-#define hb_be_uint32_eq(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
-
-#define hb_be_uint24_put(v,V)	HB_STMT_START { v[0] = (V>>16); v[1] = (V>>8); v[2] = (V); } HB_STMT_END
-#define hb_be_uint24_get(v)	(uint32_t) ((v[0] << 16) + (v[1] << 8) + v[2])
-#define hb_be_uint24_eq(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2])
-
-
 /* ASCII tag/character handling */
 
 static inline bool ISALPHA (unsigned char c)
@@ -593,6 +591,15 @@
 		  unsigned int level,
 		  int level_dir,
 		  const char *message,
+		  va_list ap) HB_PRINTF_FUNC(7, 0);
+template <int max_level> static inline void
+_hb_debug_msg_va (const char *what,
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  unsigned int level,
+		  int level_dir,
+		  const char *message,
 		  va_list ap)
 {
   if (!_hb_debug (level, max_level))
@@ -708,7 +715,9 @@
  */
 
 template <typename T>
-struct hb_printer_t {};
+struct hb_printer_t {
+  const char *print (const T&) { return "something"; }
+};
 
 template <>
 struct hb_printer_t<bool> {
@@ -815,7 +824,9 @@
    * to generate a warning than unused variables. */
   ASSERT_STATIC (sizeof (hb_assert_unsigned_t<T>) >= 0);
 
-  return (u - lo) <= (hi - lo);
+  /* The casts below are important as if T is smaller than int,
+   * the subtract results will become a signed int! */
+  return (T)(u - lo) <= (T)(hi - lo);
 }
 
 template <typename T> static inline bool
@@ -839,7 +850,7 @@
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
-template <typename T, typename T2> inline void
+template <typename T, typename T2> static inline void
 hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
 {
   if (unlikely (!len))
@@ -872,7 +883,7 @@
   } while (k);
 }
 
-template <typename T> inline void
+template <typename T> static inline void
 hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
 {
   hb_bubble_sort (array, len, compar, (int *) NULL);
@@ -901,12 +912,12 @@
 
 struct hb_options_t
 {
-  int initialized : 1;
-  int uniscribe_bug_compatible : 1;
+  unsigned int initialized : 1;
+  unsigned int uniscribe_bug_compatible : 1;
 };
 
 union hb_options_union_t {
-  int i;
+  unsigned int i;
   hb_options_t opts;
 };
 ASSERT_STATIC (sizeof (int) == sizeof (hb_options_union_t));
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index 705f554..59e8f45 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -150,7 +150,7 @@
   bool in_error;
 
   inline void init (void) {
-    header.init ();
+    hb_object_init (this);
     clear ();
   }
   inline void fini (void) {
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 5ffc6b1..2166173 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -29,6 +29,12 @@
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 
+
+#ifndef HB_DEBUG_SHAPE_PLAN
+#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
+#endif
+
+
 #define HB_SHAPER_IMPLEMENT(shaper) \
 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
@@ -42,6 +48,11 @@
 		    unsigned int        num_user_features,
 		    const char * const *shaper_list)
 {
+  DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
+		  "num_features=%d shaper_list=%p",
+		  num_user_features,
+		  shaper_list);
+
   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
 
 #define HB_SHAPER_PLAN(shaper) \
@@ -104,6 +115,12 @@
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list)
 {
+  DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
+		  "face=%p num_features=%d shaper_list=%p",
+		  face,
+		  num_user_features,
+		  shaper_list);
+
   hb_shape_plan_t *shape_plan;
   hb_feature_t *features = NULL;
 
@@ -271,6 +288,11 @@
 		       const hb_feature_t *features,
 		       unsigned int        num_features)
 {
+  DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
+		  "num_features=%d shaper_func=%p",
+		  num_features,
+		  shape_plan->shaper_func);
+
   if (unlikely (hb_object_is_inert (shape_plan) ||
 		hb_object_is_inert (font) ||
 		hb_object_is_inert (buffer)))
@@ -383,6 +405,12 @@
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list)
 {
+  DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
+		  "face=%p num_features=%d shaper_list=%p",
+		  face,
+		  num_user_features,
+		  shaper_list);
+
   hb_shape_plan_proposal_t proposal = {
     *props,
     shaper_list,
@@ -392,25 +420,22 @@
   };
 
   if (shaper_list) {
-    /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
-#define HB_SHAPER_PLAN(shaper) \
-	  HB_STMT_START { \
-	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
-	      proposal.shaper_func = _hb_##shaper##_shape; \
-	  } HB_STMT_END
-
+    /* Choose shaper.  Adapted from hb_shape_plan_plan().
+     * Must choose shaper exactly the same way as that function. */
     for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
       if (0)
 	;
 #define HB_SHAPER_IMPLEMENT(shaper) \
-      else if (0 == strcmp (*shaper_item, #shaper)) \
-	HB_SHAPER_PLAN (shaper);
+      else if (0 == strcmp (*shaper_item, #shaper) && \
+	       hb_##shaper##_shaper_face_data_ensure (face)) \
+      { \
+	proposal.shaper_func = _hb_##shaper##_shape; \
+	break; \
+      }
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 
-#undef HB_SHAPER_PLAN
-
-    if (unlikely (!proposal.shaper_list))
+    if (unlikely (!proposal.shaper_func))
       return hb_shape_plan_get_empty ();
   }
 
@@ -419,7 +444,10 @@
   hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
   for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
     if (hb_shape_plan_matches (node->shape_plan, &proposal))
+    {
+      DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
       return hb_shape_plan_reference (node->shape_plan);
+    }
 
   /* Not found. */
 
@@ -442,6 +470,7 @@
     free (node);
     goto retry;
   }
+  DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
 
   return hb_shape_plan_reference (shape_plan);
 }
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 812cf59..9a59c08 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -200,7 +200,7 @@
  * hb_feature_from_string:
  * @str: (array length=len):
  * @len: 
- * @feature: (out) (allow-none):
+ * @feature: (out) (optional):
  *
  * 
  *
@@ -279,11 +279,13 @@
 
 static const char **static_shaper_list;
 
-static inline
+#ifdef HB_USE_ATEXIT
+static
 void free_static_shaper_list (void)
 {
   free (static_shaper_list);
 }
+#endif
 
 /**
  * hb_shape_list_shapers:
@@ -320,7 +322,7 @@
       goto retry;
     }
 
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
     atexit (free_static_shaper_list); /* First person registers atexit() callback. */
 #endif
   }
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index 44f718a..580b95c 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -40,12 +40,14 @@
 
 static const hb_shaper_pair_t *static_shapers;
 
-static inline
+#ifdef HB_USE_ATEXIT
+static
 void free_static_shapers (void)
 {
   if (unlikely (static_shapers != all_shapers))
     free ((void *) static_shapers);
 }
+#endif
 
 const hb_shaper_pair_t *
 _hb_shapers_get (void)
@@ -100,7 +102,7 @@
       goto retry;
     }
 
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
     atexit (free_static_shapers); /* First person registers atexit() callback. */
 #endif
   }
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index d59dfb2..fc19006 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -157,7 +157,7 @@
 
 /**
  * hb_unicode_funcs_create: (Xconstructor)
- * @parent: (allow-none):
+ * @parent: (nullable):
  *
  * 
  *
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 1594948..e7bcad2 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -43,6 +43,12 @@
 #endif
 
 
+static inline uint16_t hb_uint16_swap (const uint16_t v)
+{ return (v >> 8) | (v << 8); }
+static inline uint32_t hb_uint32_swap (const uint32_t v)
+{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
+
+
 typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/(
   const WCHAR *pwcInChars,
   int cInChars,
@@ -245,7 +251,7 @@
       goto retry;
     }
 
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
     atexit (free_uniscribe_funcs); /* First person registers atexit() callback. */
 #endif
   }
@@ -310,6 +316,7 @@
   const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
   UUID id;
   UuidCreate ((UUID*) &id);
+  ASSERT_STATIC (2 + 3 * (16/2) < LF_FACESIZE);
   unsigned int name_str_len = 0;
   face_name[name_str_len++] = 'F';
   face_name[name_str_len++] = '_';
@@ -902,8 +909,7 @@
       FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
     if (unlikely (hr == E_OUTOFMEMORY))
     {
-      buffer->ensure (buffer->allocated * 2);
-      if (buffer->in_error)
+      if (unlikely (!buffer->ensure (buffer->allocated * 2)))
 	FAIL ("Buffer resize failed");
       goto retry;
     }
@@ -972,8 +978,7 @@
 
 #undef utf16_index
 
-  buffer->ensure (glyphs_len);
-  if (buffer->in_error)
+  if (unlikely (!buffer->ensure (glyphs_len)))
     FAIL ("Buffer in error");
 
 #undef FAIL
diff --git a/src/sample.py b/src/sample.py
index 317a47e..f8d2216 100755
--- a/src/sample.py
+++ b/src/sample.py
@@ -1,13 +1,52 @@
 #!/usr/bin/python
+# -*- coding: utf-8 -*-
 
+from __future__ import print_function
 import sys
 from gi.repository import HarfBuzz as hb
+from gi.repository import GLib
 
-def nothing():
-	pass
+# Python 2/3 compatibility
+try:
+	unicode
+except NameError:
+	unicode = str
 
-fontdata = file (sys.argv[1]).read ()
-blob = hb.blob_create (fontdata, hb.memory_mode_t.WRITABLE, None, nothing)
-print blob
-buffer = hb.buffer_create ()
+def tounicode(s, encoding='utf-8'):
+	if not isinstance(s, unicode):
+		return s.decode(encoding)
+	else:
+		return s
 
+fontdata = open (sys.argv[1], 'rb').read ()
+text = tounicode(sys.argv[2])
+# Need to create GLib.Bytes explicitly until this bug is fixed:
+# https://bugzilla.gnome.org/show_bug.cgi?id=729541
+blob = hb.glib_blob_create (GLib.Bytes.new (fontdata))
+face = hb.face_create (blob, 0)
+del blob
+font = hb.font_create (face)
+upem = hb.face_get_upem (face)
+del face
+hb.font_set_scale (font, upem, upem)
+#hb.ft_font_set_funcs (font)
+hb.ot_font_set_funcs (font)
+
+buf = hb.buffer_create ()
+hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1)
+hb.buffer_guess_segment_properties (buf)
+
+hb.shape (font, buf, [])
+del font
+
+infos = hb.buffer_get_glyph_infos (buf)
+positions = hb.buffer_get_glyph_positions (buf)
+
+for info,pos in zip(infos, positions):
+	gid = info.codepoint
+	cluster = info.cluster
+	x_advance = pos.x_advance
+	x_offset = pos.x_offset
+	y_offset = pos.y_offset
+
+	print("gid%d=%d@%d,%d+%d" % (gid, cluster, x_advance, x_offset, y_offset))
diff --git a/test/api/test-blob.c b/test/api/test-blob.c
index bbb7e2e..f671331 100644
--- a/test/api/test-blob.c
+++ b/test/api/test-blob.c
@@ -53,6 +53,9 @@
   g_assert (hb_blob_is_immutable (hb_blob_get_empty ()));
   g_assert (hb_blob_get_empty () != NULL);
   g_assert (hb_blob_get_empty () == hb_blob_create (NULL, 0, HB_MEMORY_MODE_READONLY, NULL, NULL));
+  g_assert (hb_blob_get_empty () == hb_blob_create ("asdf", 0, HB_MEMORY_MODE_READONLY, NULL, NULL));
+  g_assert (hb_blob_get_empty () == hb_blob_create (NULL, -1, HB_MEMORY_MODE_READONLY, NULL, NULL));
+  g_assert (hb_blob_get_empty () == hb_blob_create ("asdfg", -1, HB_MEMORY_MODE_READONLY, NULL, NULL));
 
   blob = hb_blob_get_empty ();
   g_assert (blob == hb_blob_get_empty ());
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index af73c3f..17607f1 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -110,7 +110,6 @@
   g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID);
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
   g_assert (hb_buffer_get_language (b) == NULL);
-  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAG_DEFAULT);
 
 
   /* test property changes are retained */
@@ -131,9 +130,11 @@
   hb_buffer_set_flags (b, HB_BUFFER_FLAG_BOT);
   g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAG_BOT);
 
+  hb_buffer_set_replacement_codepoint (b, (unsigned int) -1);
+  g_assert (hb_buffer_get_replacement_codepoint (b) == (unsigned int) -1);
 
 
-  /* test clear clears all properties but unicode_funcs */
+  /* test clear_contents clears all these properties: */
 
   hb_buffer_clear_contents (b);
 
@@ -141,7 +142,11 @@
   g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID);
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
   g_assert (hb_buffer_get_language (b) == NULL);
-  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAGS_DEFAULT);
+
+  /* but not these: */
+
+  g_assert (hb_buffer_get_flags (b) != HB_BUFFER_FLAGS_DEFAULT);
+  g_assert (hb_buffer_get_replacement_codepoint (b) != HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT);
 
 
   /* test reset clears all properties */
@@ -158,6 +163,9 @@
   hb_buffer_set_flags (b, HB_BUFFER_FLAG_BOT);
   g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAG_BOT);
 
+  hb_buffer_set_replacement_codepoint (b, (unsigned int) -1);
+  g_assert (hb_buffer_get_replacement_codepoint (b) == (unsigned int) -1);
+
   hb_buffer_reset (b);
 
   g_assert (hb_buffer_get_unicode_funcs (b) == hb_unicode_funcs_get_default ());
@@ -165,6 +173,7 @@
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
   g_assert (hb_buffer_get_language (b) == NULL);
   g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAGS_DEFAULT);
+  g_assert (hb_buffer_get_replacement_codepoint (b) == HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT);
 }
 
 static void
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
index c2c4db6..70bcdd5 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -36,11 +36,15 @@
 	$(NULL)
 
 TESTS = \
+	tests/arabic-fallback-shaping.tests \
 	tests/arabic-feature-order.tests \
 	tests/context-matching.tests \
+	tests/hangul-jamo.tests \
+	tests/indic-joiner-candrabindu.tests \
 	tests/indic-old-spec.tests \
 	tests/indic-pref-blocking.tests \
 	tests/mongolian-variation-selector.tests \
+	tests/zero-width-marks.tests \
 	$(NULL)
 
 TEST_EXTENSIONS = \
diff --git a/test/shaping/fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf b/test/shaping/fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf
new file mode 100644
index 0000000..8fb2f16
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf b/test/shaping/fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
new file mode 100644
index 0000000..bbe2237
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf b/test/shaping/fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf
new file mode 100644
index 0000000..27efd7c
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf b/test/shaping/fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf
new file mode 100644
index 0000000..2cbb67a
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/MANIFEST b/test/shaping/fonts/sha1sum/MANIFEST
index 4aaa54d..924732d 100644
--- a/test/shaping/fonts/sha1sum/MANIFEST
+++ b/test/shaping/fonts/sha1sum/MANIFEST
@@ -2,12 +2,18 @@
 270b89df543a7e48e206a2d830c0e10e5265c630.ttf
 37033cc5cf37bb223d7355153016b6ccece93b28.ttf
 4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf
+5028afb650b1bb718ed2131e872fbcce57828fff.ttf
 57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf
+757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
+7e14e7883ed152baa158b80e207b66114c823a8b.ttf
 813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf
+8454d22037f892e76614e1645d066689a0200e61.ttf
 8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf
 a919b33197965846f21074b24e30250d67277bce.ttf
 bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
+bb9473d2403488714043bcfb946c9f78b86ad627.ttf
 d629e7fedc0b350222d7987345fe61613fa3929a.ttf
+df768b9c257e0c9c35786c47cae15c46571d56be.ttf
 e207635780b42f898d58654b65098763e340f5c7.ttf
 ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf
 f499fbc23865022234775c43503bba2e63978fe1.ttf
diff --git a/test/shaping/fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf b/test/shaping/fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf
new file mode 100644
index 0000000..b16dae6
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf b/test/shaping/fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf
new file mode 100644
index 0000000..c6d8b18
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf
Binary files differ
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index 7674fdf..a370e5e 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -411,7 +411,7 @@
 	def parse (s):
 		s = re.sub (r"0[xX]", " ", s)
 		s = re.sub (r"[<+>,;&#\\xXuU\n	]", " ", s)
-		return [int (x, 16) for x in s.split (' ') if len (x)]
+		return [int (x, 16) for x in s.split ()]
 
 	@staticmethod
 	def encode (s):
diff --git a/test/shaping/record-test.sh b/test/shaping/record-test.sh
index 81087f7..a69157f 100755
--- a/test/shaping/record-test.sh
+++ b/test/shaping/record-test.sh
@@ -27,7 +27,7 @@
 if ! test "x$glyphs" = "x$glyphs_subset"; then
 	echo "Subset font produced different glyphs!" >&2
 	echo "Perhaps font doesn't have glyph names; checking visually..." >&2
-	hb_view=${hb_shape//shape/view}
+	hb_view=${hb_shape/shape/view}
 	echo "$text" | $hb_view "$dir/font.ttf" --output-format=png --output-file="$dir/orig.png"
 	echo "$text" | $hb_view "$dir/font.ttf.subset" --output-format=png --output-file="$dir/subset.png"
 	if ! cmp "$dir/orig.png" "$dir/subset.png"; then
diff --git a/test/shaping/tests/MANIFEST b/test/shaping/tests/MANIFEST
index a792e91..849ebc5 100644
--- a/test/shaping/tests/MANIFEST
+++ b/test/shaping/tests/MANIFEST
@@ -1,5 +1,9 @@
+arabic-fallback-shaping.tests
 arabic-feature-order.tests
 context-matching.tests
+hangul-jamo.tests
+indic-joiner-candrabindu.tests
 indic-old-spec.tests
 indic-pref-blocking.tests
 mongolian-variation-selector.tests
+zero-width-marks.tests
diff --git a/test/shaping/tests/arabic-fallback-shaping.tests b/test/shaping/tests/arabic-fallback-shaping.tests
new file mode 100644
index 0000000..e3eaf3f
--- /dev/null
+++ b/test/shaping/tests/arabic-fallback-shaping.tests
@@ -0,0 +1 @@
+fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf:U+0633,U+064F,U+0644,U+064E,U+0651,U+0627,U+0651,U+0650,U+0645,U+062A,U+06CC:[uni06CC.fina=10+1655|uni062A.medi=9+868|uni0645.init=8+1098|uni0650=2@221,0+0|uni0651=2@260,736+0|uni064E=2@935,1259+0|uni0651=2@974,736+0|uni06440627.fina=2+1470|uni064F=0@558,-10+0|uni0633.init=0+1585]
diff --git a/test/shaping/tests/hangul-jamo.tests b/test/shaping/tests/hangul-jamo.tests
new file mode 100644
index 0000000..667a1cc
--- /dev/null
+++ b/test/shaping/tests/hangul-jamo.tests
@@ -0,0 +1,2 @@
+fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf:U+115F,U+11A2:[gid3=0+920|gid4=0+0]
+fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf:U+11A2:[gid1=0+920]
diff --git a/test/shaping/tests/indic-joiner-candrabindu.tests b/test/shaping/tests/indic-joiner-candrabindu.tests
new file mode 100644
index 0000000..351e927
--- /dev/null
+++ b/test/shaping/tests/indic-joiner-candrabindu.tests
@@ -0,0 +1,2 @@
+fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf:U+0B13,U+200D,U+0B01:[omorya=0+1450]
+fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf:U+0B13,U+200C,U+0B01:[oorya=0+1309|space=1+0|candrabinduorya=1+0]
diff --git a/test/shaping/tests/zero-width-marks.tests b/test/shaping/tests/zero-width-marks.tests
new file mode 100644
index 0000000..be7ec96
--- /dev/null
+++ b/test/shaping/tests/zero-width-marks.tests
@@ -0,0 +1,2 @@
+fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf:U+1030:[circledash=0+636|u1030.med=0@-162,0+0]
+fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf:U+05E0,U+05B8,U+0591,U+05DA,U+05B0:[uni05DA05B0=3+991|uni2009=0+200|uni0591=0@75,0+0|uni05B8=0@495,0+0|uni05E0=0+683]
diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc
index 03de7e6..859f9a6 100644
--- a/util/hb-ot-shape-closure.cc
+++ b/util/hb-ot-shape-closure.cc
@@ -110,6 +110,6 @@
 int
 main (int argc, char **argv)
 {
-  main_font_text_t<shape_closure_consumer_t> driver;
+  main_font_text_t<shape_closure_consumer_t, FONT_SIZE_NONE, 0> driver;
   return driver.main (argc, argv);
 }
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
index 01081ea..f38f387 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -126,6 +126,6 @@
 int
 main (int argc, char **argv)
 {
-  main_font_text_t<shape_consumer_t<output_buffer_t> > driver;
+  main_font_text_t<shape_consumer_t<output_buffer_t>, FONT_SIZE_UPEM, 0> driver;
   return driver.main (argc, argv);
 }
diff --git a/util/hb-view.cc b/util/hb-view.cc
index 26fad66..ef75e6d 100644
--- a/util/hb-view.cc
+++ b/util/hb-view.cc
@@ -29,9 +29,12 @@
 #include "shape-consumer.hh"
 #include "view-cairo.hh"
 
+#define DEFAULT_FONT_SIZE 256
+#define SUBPIXEL_BITS 8
+
 int
 main (int argc, char **argv)
 {
-  main_font_text_t<shape_consumer_t<view_cairo_t> > driver;
+  main_font_text_t<shape_consumer_t<view_cairo_t>, DEFAULT_FONT_SIZE, SUBPIXEL_BITS> driver;
   return driver.main (argc, argv);
 }
diff --git a/util/helper-cairo-ansi.cc b/util/helper-cairo-ansi.cc
index 376bf2b..50f9eb4 100644
--- a/util/helper-cairo-ansi.cc
+++ b/util/helper-cairo-ansi.cc
@@ -63,10 +63,10 @@
    * Find the tight image top/bottom and only print in between. */
 
   /* Use corner color as background color. */
-  uint32_t bg_color = * (uint32_t *) data;
+  uint32_t bg_color = data ? * (uint32_t *) data : 0;
 
   /* Drop first row while empty */
-  while (height) 
+  while (height)
   {
     unsigned int i;
     for (i = 0; i < width; i++)
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
index ee940ae..d576c3f 100644
--- a/util/helper-cairo.cc
+++ b/util/helper-cairo.cc
@@ -70,8 +70,7 @@
 }
 
 cairo_scaled_font_t *
-helper_cairo_create_scaled_font (const font_options_t *font_opts,
-				 double font_size)
+helper_cairo_create_scaled_font (const font_options_t *font_opts)
 {
   hb_font_t *font = hb_font_reference (font_opts->get_font ());
 
@@ -105,7 +104,8 @@
 
   cairo_matrix_init_identity (&ctm);
   cairo_matrix_init_scale (&font_matrix,
-			   font_size, font_size);
+			   font_opts->font_size_x,
+			   font_opts->font_size_y);
   font_options = cairo_font_options_create ();
   cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
   cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
@@ -423,7 +423,7 @@
 			       hb_buffer_t         *buffer,
 			       const char          *text,
 			       unsigned int         text_len,
-			       double               scale,
+			       int                  scale_bits,
 			       hb_bool_t            utf8_clusters)
 {
   memset (l, 0, sizeof (*l));
@@ -456,16 +456,16 @@
   for (i = 0; i < (int) l->num_glyphs; i++)
   {
     l->glyphs[i].index = hb_glyph[i].codepoint;
-    l->glyphs[i].x = ( hb_position->x_offset + x) * scale;
-    l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
+    l->glyphs[i].x = scalbn ( hb_position->x_offset + x, scale_bits);
+    l->glyphs[i].y = scalbn (-hb_position->y_offset + y, scale_bits);
     x +=  hb_position->x_advance;
     y += -hb_position->y_advance;
 
     hb_position++;
   }
   l->glyphs[i].index = -1;
-  l->glyphs[i].x = x * scale;
-  l->glyphs[i].y = y * scale;
+  l->glyphs[i].x = scalbn (x, scale_bits);
+  l->glyphs[i].y = scalbn (y, scale_bits);
 
   if (l->num_clusters) {
     memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
index 567777e..ed55a45 100644
--- a/util/helper-cairo.hh
+++ b/util/helper-cairo.hh
@@ -33,8 +33,7 @@
 
 
 cairo_scaled_font_t *
-helper_cairo_create_scaled_font (const font_options_t *font_opts,
-				 double font_size);
+helper_cairo_create_scaled_font (const font_options_t *font_opts);
 
 extern const char *helper_cairo_supported_formats[];
 
@@ -76,7 +75,7 @@
 			       hb_buffer_t         *buffer,
 			       const char          *text,
 			       unsigned int         text_len,
-			       double               scale,
+			       int                  scale_bits,
 			       hb_bool_t            utf8_clusters);
 
 #endif
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index ac51b2d..628cdf9 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -31,12 +31,12 @@
 
 /* main() body for utilities taking font and processing text.*/
 
-template <typename consumer_t>
+template <typename consumer_t, int default_font_size, int subpixel_bits>
 struct main_font_text_t
 {
   main_font_text_t (void)
 		  : options ("[FONT-FILE] [TEXT]"),
-		    font_opts (&options),
+		    font_opts (&options, default_font_size, subpixel_bits),
 		    input (&options),
 		    consumer (&options) {}
 
diff --git a/util/options.cc b/util/options.cc
index 0adc179..7387a56 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -55,6 +55,7 @@
   va_list vap;
   va_start (vap, format);
   msg = g_strdup_vprintf (format, vap);
+  va_end (vap);
   const char *prgname = g_get_prgname ();
   g_printerr ("%s: %s\n", prgname, msg);
   if (suggest_help)
@@ -264,7 +265,6 @@
     {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: " DEFAULT_FORE ")",	"rrggbb/rrggbbaa"},
     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
-    {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: " G_STRINGIFY(DEFAULT_FONT_SIZE) ")","size"},
     {NULL}
   };
   parser->add_group (entries,
@@ -349,6 +349,28 @@
 		     this);
 }
 
+static gboolean
+parse_font_size (const char *name G_GNUC_UNUSED,
+		 const char *arg,
+		 gpointer    data,
+		 GError    **error G_GNUC_UNUSED)
+{
+  font_options_t *font_opts = (font_options_t *) data;
+  if (0 == strcmp (arg, "upem"))
+  {
+    font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM;
+    return true;
+  }
+  switch (sscanf (arg, "%lf %lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
+    case 1: font_opts->font_size_y = font_opts->font_size_x;
+    case 2: return true;
+    default:
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		   "%s argument should be one to four space-separated numbers",
+		   name);
+      return false;
+  }
+}
 void
 font_options_t::add_options (option_parser_t *parser)
 {
@@ -369,11 +391,22 @@
     parser->free_later (text);
   }
 
+  char *font_size_text;
+  if (default_font_size == FONT_SIZE_UPEM)
+    font_size_text = (char *) "Font size (default: upem)";
+  else
+  {
+    font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size);
+    parser->free_later (font_size_text);
+  }
+
   GOptionEntry entries[] =
   {
     {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Set font file-name",			"filename"},
     {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Set face index (default: 0)",		"index"},
-    {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,					"format"},
+    {"font-size",	0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN,
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_size,	font_size_text,				"1/2 numbers or 'upem'"},
+    {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,					"impl"},
     {NULL}
   };
   parser->add_group (entries,
@@ -514,8 +547,14 @@
 
   font = hb_font_create (face);
 
-  unsigned int upem = hb_face_get_upem (face);
-  hb_font_set_scale (font, upem, upem);
+  if (font_size_x == FONT_SIZE_UPEM)
+    font_size_x = hb_face_get_upem (face);
+  if (font_size_y == FONT_SIZE_UPEM)
+    font_size_y = hb_face_get_upem (face);
+
+  int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
+  int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
+  hb_font_set_scale (font, scale_x, scale_y);
   hb_face_destroy (face);
 
   void (*set_font_funcs) (hb_font_t *) = NULL;
diff --git a/util/options.hh b/util/options.hh
index 223778d..8b9b10e 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -143,7 +143,8 @@
 #define DEFAULT_MARGIN 16
 #define DEFAULT_FORE "#000000"
 #define DEFAULT_BACK "#FFFFFF"
-#define DEFAULT_FONT_SIZE 256
+#define FONT_SIZE_UPEM 0x7FFFFFFF
+#define FONT_SIZE_NONE 0
 
 struct view_options_t : option_group_t
 {
@@ -153,7 +154,6 @@
     back = DEFAULT_BACK;
     line_space = 0;
     margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
-    font_size = DEFAULT_FONT_SIZE;
 
     add_options (parser);
   }
@@ -167,7 +167,6 @@
   struct margin_t {
     double t, r, b, l;
   } margin;
-  double font_size;
 };
 
 
@@ -273,9 +272,14 @@
 
 struct font_options_t : option_group_t
 {
-  font_options_t (option_parser_t *parser) {
+  font_options_t (option_parser_t *parser,
+		  int default_font_size_,
+		  unsigned int subpixel_bits_) {
+    default_font_size = default_font_size_;
+    subpixel_bits = subpixel_bits_;
     font_file = NULL;
     face_index = 0;
+    font_size_x = font_size_y = default_font_size;
     font_funcs = NULL;
 
     font = NULL;
@@ -292,6 +296,10 @@
 
   const char *font_file;
   int face_index;
+  int default_font_size;
+  unsigned int subpixel_bits;
+  mutable double font_size_x;
+  mutable double font_size_y;
   const char *font_funcs;
 
   private:
diff --git a/util/view-cairo.cc b/util/view-cairo.cc
index 666013e..160250e 100644
--- a/util/view-cairo.cc
+++ b/util/view-cairo.cc
@@ -54,7 +54,7 @@
 void
 view_cairo_t::render (const font_options_t *font_opts)
 {
-  cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts, view_options.font_size);
+  cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts);
   double w, h;
   get_surface_size (scaled_font, &w, &h);
   cairo_t *cr = helper_cairo_create_context (w, h, &view_options, &output_options);
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
index 9dea06e..cb52373 100644
--- a/util/view-cairo.hh
+++ b/util/view-cairo.hh
@@ -37,7 +37,7 @@
 	       : output_options (parser, helper_cairo_supported_formats),
 		 view_options (parser),
 		 direction (HB_DIRECTION_INVALID),
-		 lines (0), scale (1.0) {}
+		 lines (0), scale_bits (0) {}
   ~view_cairo_t (void) {
     if (debug)
       cairo_debug_reset_static_data ();
@@ -46,7 +46,7 @@
   void init (const font_options_t *font_opts)
   {
     lines = g_array_new (false, false, sizeof (helper_cairo_line_t));
-    scale = double (view_options.font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
+    scale_bits = -font_opts->subpixel_bits;
   }
   void new_line (void)
   {
@@ -71,7 +71,7 @@
   {
     direction = hb_buffer_get_direction (buffer);
     helper_cairo_line_t l;
-    helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale, utf8_clusters);
+    helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale_bits, utf8_clusters);
     g_array_append_val (lines, l);
   }
   void finish (const font_options_t *font_opts)
@@ -100,7 +100,7 @@
 
   hb_direction_t direction; // Remove this, make segment_properties accessible
   GArray *lines;
-  double scale;
+  int scale_bits;
 };
 
 #endif