bug 449292 - part 1 - harfbuzz-ng code from upstream. r=jdaggett sr=roc
authorJonathan Kew <jfkthame@gmail.com>
Fri, 11 Jun 2010 20:14:37 +0100
changeset 43519 17a929e875ca15136d20ba61ab07d4f5c058e686
parent 43518 37caccea3c818595150703a90e9ab66fa90bd6b8
child 43520 211a9ef74d59fc02f56f4f30aca1b04e360f5a4f
push idunknown
push userunknown
push dateunknown
reviewersjdaggett, roc
bugs449292
milestone1.9.3a6pre
bug 449292 - part 1 - harfbuzz-ng code from upstream. r=jdaggett sr=roc
gfx/harfbuzz/AUTHORS
gfx/harfbuzz/COPYING
gfx/harfbuzz/Makefile.am
gfx/harfbuzz/NEWS
gfx/harfbuzz/README
gfx/harfbuzz/TODO
gfx/harfbuzz/autogen.sh
gfx/harfbuzz/configure.ac
gfx/harfbuzz/git.mk
gfx/harfbuzz/harfbuzz.pc.in
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/check-libstdc++.sh
gfx/harfbuzz/src/hb-blob-private.h
gfx/harfbuzz/src/hb-blob.c
gfx/harfbuzz/src/hb-blob.h
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-buffer.h
gfx/harfbuzz/src/hb-common.c
gfx/harfbuzz/src/hb-common.h
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-font.h
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-ft.h
gfx/harfbuzz/src/hb-glib.c
gfx/harfbuzz/src/hb-glib.h
gfx/harfbuzz/src/hb-graphite.cc
gfx/harfbuzz/src/hb-graphite.h
gfx/harfbuzz/src/hb-icu.c
gfx/harfbuzz/src/hb-icu.h
gfx/harfbuzz/src/hb-language.c
gfx/harfbuzz/src/hb-language.h
gfx/harfbuzz/src/hb-object-private.h
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-head-private.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-private.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-private.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.cc
gfx/harfbuzz/src/hb-ot-layout.h
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot-shape.h
gfx/harfbuzz/src/hb-ot-tag.c
gfx/harfbuzz/src/hb-ot-tag.h
gfx/harfbuzz/src/hb-ot.h
gfx/harfbuzz/src/hb-private.h
gfx/harfbuzz/src/hb-shape.cc
gfx/harfbuzz/src/hb-shape.h
gfx/harfbuzz/src/hb-unicode-private.h
gfx/harfbuzz/src/hb-unicode.c
gfx/harfbuzz/src/hb-unicode.h
gfx/harfbuzz/src/hb.h
gfx/harfbuzz/src/main.cc
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/COPYING
@@ -0,0 +1,17 @@
+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.
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/Makefile.am
@@ -0,0 +1,45 @@
+SUBDIRS = src
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = harfbuzz.pc
+
+EXTRA_DIST = \
+	autogen.sh
+
+MAINTAINERCLEANFILES = \
+	$(srcdir)/INSTALL \
+	$(srcdir)/aclocal.m4 \
+	$(srcdir)/autoscan.log \
+	$(srcdir)/compile \
+	$(srcdir)/config.guess \
+	$(srcdir)/config.h.in \
+	$(srcdir)/config.sub \
+	$(srcdir)/configure.scan \
+	$(srcdir)/depcomp \
+	$(srcdir)/install-sh \
+	$(srcdir)/ltmain.sh \
+	$(srcdir)/missing \
+	$(srcdir)/mkinstalldirs \
+	$(srcdir)/ChangeLog \
+	`find "$(srcdir)" -type f -name Makefile.in -print`
+
+CHANGELOG_RANGE =
+
+ChangeLog: $(srcdir)/ChangeLog
+$(srcdir)/ChangeLog:
+	$(AM_V_GEN) if test -d "$(srcdir)/.git"; then \
+	  (GIT_DIR=$(top_srcdir)/.git ./missing --run \
+	   git log $(CHANGELOG_RANGE) --stat) | fmt --split-only > $@.tmp \
+	  && mv -f $@.tmp $@ \
+	  || ($(RM) $@.tmp; \
+	      echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \
+	      (test -f $@ || echo git-log is required to generate this file >> $@)); \
+	else \
+	  test -f $@ || \
+	  (echo A git checkout and git-log is required to generate ChangeLog >&2 && \
+	  echo A git checkout and git-log is required to generate this file >> $@); \
+	fi
+.PHONY: $(srcdir)/ChangeLog
+
+
+-include $(top_srcdir)/git.mk
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/README
@@ -0,0 +1,9 @@
+This is HarfBuzz, an OpenType Layout engine.
+
+Bug reports on these files should be sent to the HarfBuzz mailing list as
+listed on http://freedesktop.org/wiki/Software/harfbuzz
+
+For license information, see the file COPYING.
+
+Behdad Esfahbod
+May 24, 2009
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/TODO
@@ -0,0 +1,12 @@
+
+- kern/GPOS interaction
+
+- Use size_t in sanitize?
+- Buffer error handling?
+- Better define HB_INTERNAL
+- Future-proof metrics struct
+
+hb-ot:
+- Rename hb_internal_glyph_info_t to hb_ot_glyph_info_t
+- Add query API for aalt-like features
+- HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH vs LookupType::... mess
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/autogen.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+set -e
+
+ARGV0=$0
+
+# Allow invocation from a separate build directory; in that case, we change
+# to the source directory to run the auto*, then change back before running configure
+srcdir=`dirname $ARGV0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PACKAGE=harfbuzz
+
+LIBTOOLIZE_FLAGS="--copy --force --automake"
+ACLOCAL_FLAGS=""
+AUTOHEADER=${AUTOHEADER-autoheader}
+GTKDOCIZE_FLAGS="--copy"
+GTKDOCIZE=${GTKDOCIZE-gtkdocize}
+AUTOMAKE_FLAGS="--add-missing --gnu -Wall"
+AUTOCONF=${AUTOCONF-autoconf}
+
+CONFIGURE_AC=
+test -f configure.ac && CONFIGURE_AC=configure.ac
+
+if test "X$CONFIGURE_AC" = X; then
+  echo "$ARGV0: ERROR: No $srcdir/configure.in or $srcdir/configure.ac found."
+  exit 1
+fi
+
+extract_version() {
+	grep "^ *$1" "$CONFIGURE_AC" | sed 's/.*(\[*\([^])]*\)]*).*/\1/'
+}
+
+autoconf_min_vers=`extract_version AC_PREREQ`
+automake_min_vers=`extract_version AM_INIT_AUTOMAKE`
+libtoolize_min_vers=`extract_version AC_PROG_LIBTOOL`
+aclocal_min_vers=$automake_min_vers
+
+
+# Not all echo versions allow -n, so we check what is possible. This test is
+# based on the one in autoconf.
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ;;
+  *c*,*  ) ECHO_N=-n ;;
+  *)       ECHO_N= ;;
+esac
+
+
+# some terminal codes ...
+boldface="`tput bold 2>/dev/null || true`"
+normal="`tput sgr0 2>/dev/null || true`"
+printbold() {
+    echo $ECHO_N "$boldface"
+    echo "$@"
+    echo $ECHO_N "$normal"
+}
+printerr() {
+    echo "$@" >&2
+}
+
+
+# Usage:
+#     compare_versions MIN_VERSION ACTUAL_VERSION
+# returns true if ACTUAL_VERSION >= MIN_VERSION
+compare_versions() {
+    ch_min_version=$1
+    ch_actual_version=$2
+    ch_status=0
+    IFS="${IFS=         }"; ch_save_IFS="$IFS"; IFS="."
+    set $ch_actual_version
+    for ch_min in $ch_min_version; do
+        ch_cur=`echo $1 | sed 's/[^0-9].*$//'`; shift # remove letter suffixes
+        if [ -z "$ch_min" ]; then break; fi
+        if [ -z "$ch_cur" ]; then ch_status=1; break; fi
+        if [ $ch_cur -gt $ch_min ]; then break; fi
+        if [ $ch_cur -lt $ch_min ]; then ch_status=1; break; fi
+    done
+    IFS="$ch_save_IFS"
+    return $ch_status
+}
+
+# Usage:
+#     version_check PACKAGE VARIABLE CHECKPROGS MIN_VERSION SOURCE
+# checks to see if the package is available
+version_check() {
+    vc_package=$1
+    vc_variable=$2
+    vc_checkprogs=$3
+    vc_min_version=$4
+    vc_source=$5
+    vc_status=1
+
+    vc_checkprog=`eval echo "\\$$vc_variable"`
+    if [ -n "$vc_checkprog" ]; then
+	printbold "using $vc_checkprog for $vc_package"
+	return 0
+    fi
+
+    printbold "checking for $vc_package >= $vc_min_version..."
+    for vc_checkprog in $vc_checkprogs; do
+	echo $ECHO_N "  testing $vc_checkprog... "
+	if $vc_checkprog --version < /dev/null > /dev/null 2>&1; then
+	    vc_actual_version=`$vc_checkprog --version | head -n 1 | \
+                               sed 's/^.*[ 	]\([0-9.]*[a-z]*\).*$/\1/'`
+	    if compare_versions $vc_min_version $vc_actual_version; then
+		echo "found $vc_actual_version"
+		# set variable
+		eval "$vc_variable=$vc_checkprog"
+		vc_status=0
+		break
+	    else
+		echo "too old (found version $vc_actual_version)"
+	    fi
+	else
+	    echo "not found."
+	fi
+    done
+    if [ "$vc_status" != 0 ]; then
+	printerr "***Error***: You must have $vc_package >= $vc_min_version installed"
+	printerr "  to build $PROJECT.  Download the appropriate package for"
+	printerr "  from your distribution or get the source tarball at"
+        printerr "    $vc_source"
+	printerr
+    fi
+    return $vc_status
+}
+
+
+version_check autoconf AUTOCONF $AUTOCONF $autoconf_min_vers \
+    "http://ftp.gnu.org/pub/gnu/autoconf/autoconf-${autoconf_min_vers}.tar.gz" || DIE=1
+
+#
+# Hunt for an appropriate version of automake and aclocal; we can't
+# assume that 'automake' is necessarily the most recent installed version
+#
+# We check automake first to allow it to be a newer version than we know about.
+#
+version_check automake AUTOMAKE "$AUTOMAKE automake automake-1.10 automake-1.9 automake-1.8 automake-1.7" $automake_min_vers \
+    "http://ftp.gnu.org/pub/gnu/automake/automake-${automake_min_vers}.tar.gz" || DIE=1
+ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/`
+
+
+version_check libtool LIBTOOLIZE "$LIBTOOLIZE glibtoolize libtoolize" $libtoolize_min_vers \
+    "http://ftp.gnu.org/pub/gnu/libtool/libtool-${libtool_min_vers}.tar.gz" || DIE=1
+
+if test -n "$DIE"; then
+  exit 1
+fi
+
+
+if test -z "$*"; then
+  echo "$ARGV0:	Note: \`./configure' will be run with no arguments."
+  echo "		If you wish to pass any to it, please specify them on the"
+  echo "		\`$0' command line."
+  echo
+fi
+
+do_cmd() {
+    echo "$ARGV0: running \`$@'"
+    $@
+}
+
+do_cmd $LIBTOOLIZE $LIBTOOLIZE_FLAGS
+
+do_cmd $ACLOCAL $ACLOCAL_FLAGS
+
+do_cmd $AUTOHEADER
+
+touch ChangeLog
+
+# We don't call gtkdocize right now.  When we do, we should then modify
+# the generated gtk-doc.make and move it to build/Makefile.am.gtk-doc.
+# See that file for details.
+#do_cmd $GTKDOCIZE $GTKDOCIZE_FLAGS
+
+do_cmd $AUTOMAKE $AUTOMAKE_FLAGS
+
+do_cmd $AUTOCONF
+
+cd "$ORIGDIR" || exit 1
+
+rm -f config.cache
+
+do_cmd $srcdir/configure \
+	${1+"$@"} && echo "Now type \`make' to compile $PROJECT." || exit 1
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/configure.ac
@@ -0,0 +1,59 @@
+AC_PREREQ(2.59)
+AC_INIT(harfbuzz, 0.1, [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz])
+AC_CONFIG_SRCDIR([harfbuzz.pc.in])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([1.9.6 gnu dist-bzip2 no-dist-gzip -Wall no-define])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
+AC_LIBTOOL_WIN32_DLL
+AC_PROG_LIBTOOL dnl ([1.4]) Don't remove!
+
+AC_PROG_CC
+AC_PROG_CXX
+
+AC_C_FLEXIBLE_ARRAY_MEMBER
+
+AC_CHECK_FUNCS(mprotect sysconf getpagesize)
+AC_CHECK_HEADERS(unistd.h sys/mman.h)
+
+# Make sure we don't link to libstdc++
+if test "x$GCC" = "xyes"; then
+	CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions"
+fi
+
+dnl ==========================================================================
+
+PKG_CHECK_MODULES(GLIB, glib-2.0, have_glib=true, have_glib=false)
+if $have_glib; then
+	AC_DEFINE(HAVE_GLIB, 1, [Have glib2 library])
+fi
+AM_CONDITIONAL(HAVE_GLIB, $have_glib)
+
+PKG_CHECK_MODULES(ICU, icu, have_icu=true, have_icu=false)
+if $have_icu; then
+	AC_DEFINE(HAVE_ICU, 1, [Have ICU library])
+fi
+AM_CONDITIONAL(HAVE_ICU, $have_icu)
+
+PKG_CHECK_MODULES(FREETYPE, freetype2, have_freetype=true, have_freetype=false)
+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)
+
+dnl ==========================================================================
+
+AC_CONFIG_FILES([
+harfbuzz.pc
+Makefile
+src/Makefile
+])
+
+AC_OUTPUT
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/git.mk
@@ -0,0 +1,184 @@
+# git.mk
+#
+# Copyright 2009, Red Hat, Inc.
+# Written by Behdad Esfahbod
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+#
+# The canonical source for this file is pango/git.mk, or whereever the
+# header of pango/git.mk suggests in the future.
+#
+# To use in your project, import this file in your git repo's toplevel,
+# then do "make -f git.mk".  This modifies all Makefile.am files in
+# your project to include git.mk.
+#
+# This enables automatic .gitignore generation.  If you need to ignore
+# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
+# But think twice before doing that.  If a file has to be in .gitignore,
+# chances are very high that it's a generated file and should be in one
+# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
+#
+# The only case that you need to manually add a file to GITIGNOREFILES is
+# when remove files in one of mostlyclean-local, clean-local, distclean-local,
+# or maintainer-clean-local.
+#
+# Note that for files like editor backup, etc, there are better places to
+# ignore them.  See "man gitignore".
+#
+# If "make maintainer-clean" removes the files but they are not recognized
+# by this script (that is, if "git status" shows untracked files still), send
+# me the output of "git status" as well as your Makefile.am and Makefile for
+# the directories involved.
+#
+# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
+# pango/Makefile.am.
+#
+# Don't EXTRA_DIST this file.  It is supposed to only live in git clones,
+# not tarballs.  It serves no useful purpose in tarballs and clutters the
+# build dir.
+#
+# This file knows how to handle autoconf, automake, libtool, gtk-doc,
+# gnome-doc-utils, intltool.
+#
+#
+# KNOWN ISSUES:
+#
+# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
+#   submodule doesn't find us.  If you have configure.{in,ac} files in
+#   subdirs, add a proxy git.mk file in those dirs that simply does:
+#   "include $(top_srcdir)/../git.mk".  Add more ..'s to your taste.
+#   And add those files to git.  See vte/gnome-pty-helper/git.mk for
+#   example.
+#
+
+git-all: git-mk-install
+
+git-mk-install:
+	@echo Installing git makefile
+	@any_failed=; find $(top_srcdir) -name Makefile.am | while read x; do \
+		if grep 'include .*/git.mk' $$x >/dev/null; then \
+			echo $$x already includes git.mk; \
+		else \
+			failed=; \
+			echo "Updating $$x"; \
+			{ cat $$x; \
+			  echo ''; \
+			  echo '-include $$(top_srcdir)/git.mk'; \
+			} > $$x.tmp || failed=1; \
+			if test x$$failed = x; then \
+				mv $$x.tmp $$x || failed=1; \
+			fi; \
+			if test x$$failed = x; then : else \
+				echo Failed updating $$x; >&2 \
+				any_failed=1; \
+			fi; \
+	fi; done; test -z "$$any_failed"
+
+.PHONY: git-all git-mk-install
+
+
+### .gitignore generation
+
+$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
+	$(AM_V_GEN) \
+	{ \
+		if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
+			for x in \
+				$(DOC_MODULE)-decl-list.txt \
+				$(DOC_MODULE)-decl.txt \
+				tmpl/$(DOC_MODULE)-unused.sgml \
+				"tmpl/*.bak" \
+				xml html \
+			; do echo /$$x; done; \
+		fi; \
+		if test "x$(DOC_MODULE)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
+			for x in \
+				$(_DOC_C_DOCS) \
+				$(_DOC_LC_DOCS) \
+				$(_DOC_OMF_ALL) \
+				$(_DOC_DSK_ALL) \
+				$(_DOC_HTML_ALL) \
+				$(_DOC_POFILES) \
+				"*/.xml2po.mo" \
+				"*/*.omf.out" \
+			; do echo /$$x; done; \
+		fi; \
+		if test -f $(srcdir)/po/Makefile.in.in; then \
+			for x in \
+				po/Makefile.in.in \
+				po/Makefile.in \
+				po/Makefile \
+				po/POTFILES \
+				po/stamp-it \
+				po/.intltool-merge-cache \
+				"po/*.gmo" \
+				"po/*.mo" \
+				po/$(GETTEXT_PACKAGE).pot \
+				intltool-extract.in \
+				intltool-merge.in \
+				intltool-update.in \
+			; do echo /$$x; done; \
+		fi; \
+		if test -f $(srcdir)/configure; then \
+			for x in \
+				autom4te.cache \
+				configure \
+				config.h \
+				stamp-h1 \
+				libtool \
+				config.lt \
+			; do echo /$$x; done; \
+		fi; \
+		for x in \
+			.gitignore \
+			$(GITIGNOREFILES) \
+			$(CLEANFILES) \
+			$(PROGRAMS) \
+			$(check_PROGRAMS) \
+			$(EXTRA_PROGRAMS) \
+			$(LTLIBRARIES) \
+			so_locations \
+			.libs _libs \
+			$(MOSTLYCLEANFILES) \
+			"*.$(OBJEXT)" \
+			"*.lo" \
+			$(DISTCLEANFILES) \
+			$(am__CONFIG_DISTCLEAN_FILES) \
+			$(CONFIG_CLEAN_FILES) \
+			TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
+			"*.tab.c" \
+			$(MAINTAINERCLEANFILES) \
+			$(BUILT_SOURCES) \
+			$(DEPDIR) \
+			Makefile \
+			Makefile.in \
+			"*.orig" \
+			"*.rej" \
+			"*.bak" \
+			"*~" \
+			".*.sw[nop]" \
+		; do echo /$$x; done; \
+	} | \
+	sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
+	sed 's@/[.]/@/@g' | \
+	LC_ALL=C sort | uniq > $@.tmp && \
+	mv $@.tmp $@;
+
+all: $(srcdir)/.gitignore gitignore-recurse-maybe
+gitignore-recurse-maybe:
+	@if test "x$(SUBDIRS)" = "x$(DIST_SUBDIRS)"; then :; else \
+		$(MAKE) $(AM_MAKEFLAGS) gitignore-recurse; \
+	fi;
+gitignore-recurse:
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir"); \
+	done
+gitignore: $(srcdir)/.gitignore gitignore-recurse
+
+maintainer-clean: gitignore-clean
+gitignore-clean:
+	-rm -f $(srcdir)/.gitignore
+
+.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/harfbuzz.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: harfbuzz
+Description: Text shaping library
+Version: @VERSION@
+
+Libs: -L${libdir} -lharfbuzz
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -0,0 +1,111 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+
+# The following warning options are useful for debugging: -Wpadded -Wcast-align
+#AM_CXXFLAGS =
+
+lib_LTLIBRARIES = libharfbuzz.la
+
+HBCFLAGS =
+HBLIBS =
+HBSOURCES =  \
+	hb-blob.c \
+	hb-blob-private.h \
+	hb-buffer.cc \
+	hb-buffer-private.hh \
+	hb-font.cc \
+	hb-font-private.hh \
+	hb-object-private.h \
+	hb-open-file-private.hh \
+	hb-open-type-private.hh \
+	hb-language.c \
+	hb-ot-head-private.hh \
+	hb-private.h \
+	hb-shape.cc \
+	hb-unicode.c \
+	hb-unicode-private.h \
+	$(NULL)
+HBHEADERS = \
+	hb.h \
+	hb-blob.h \
+	hb-buffer.h \
+	hb-common.h \
+	hb-font.h \
+	hb-language.h \
+	hb-shape.h \
+	hb-unicode.h \
+	$(NULL)
+
+HBSOURCES += \
+	hb-ot-layout.cc \
+	hb-ot-layout-common-private.hh \
+	hb-ot-layout-gdef-private.hh \
+	hb-ot-layout-gpos-private.hh \
+	hb-ot-layout-gsubgpos-private.hh \
+	hb-ot-layout-gsub-private.hh \
+	hb-ot-layout-private.hh \
+	hb-ot-shape.cc \
+	hb-ot-shape-private.hh \
+	hb-ot-tag.c \
+	$(NULL)
+HBHEADERS += \
+	hb-ot.h \
+	hb-ot-layout.h \
+	hb-ot-tag.h \
+	$(NULL)
+
+if HAVE_GLIB
+HBCFLAGS += $(GLIB_CFLAGS)
+HBLIBS   += $(GLIB_LIBS)
+HBSOURCES += \
+	hb-glib.c \
+	$(NULL)
+HBHEADERS += \
+	hb-glib.h \
+	$(NULL)
+endif
+
+if HAVE_ICU
+HBCFLAGS += $(ICU_CFLAGS)
+HBLIBS   += $(ICU_LIBS)
+HBSOURCES += \
+	hb-icu.c \
+	$(NULL)
+HBHEADERS += \
+	hb-icu.h \
+	$(NULL)
+endif
+
+if HAVE_FREETYPE
+HBCFLAGS += $(FREETYPE_CFLAGS)
+HBLIBS   += $(FREETYPE_LIBS)
+HBSOURCES += \
+	hb-ft.cc \
+	$(NULL)
+HBHEADERS += \
+	hb-ft.h \
+	$(NULL)
+endif
+
+CXXLINK = $(LINK)
+libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
+libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
+libharfbuzz_la_LIBADD = $(HBLIBS)
+pkginclude_HEADERS = $(HBHEADERS)
+
+noinst_PROGRAMS = main
+
+main_SOURCES = main.cc
+main_CPPFLAGS = $(HBCFLAGS)
+main_LDADD = libharfbuzz.la $(HBLIBS)
+
+TESTS = \
+	check-internal-symbols.sh
+
+if HAVE_ICU
+else
+TESTS += check-libstdc++.sh
+endif
+
+-include $(top_srcdir)/git.mk
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/src/check-libstdc++.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which ldd 2>/dev/null >/dev/null; then
+	:
+else
+	echo "check-libstdc++.sh: 'ldd' not found; skipping test"
+	exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+so=.libs/libharfbuzz.so
+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
+else
+	echo "check-libstdc++.sh: libharfbuzz.so not found; skipping test"
+fi
+
+exit $stat
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-blob-private.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BLOB_PRIVATE_H
+#define HB_BLOB_PRIVATE_H
+
+#include "hb-private.h"
+
+#include "hb-blob.h"
+
+HB_BEGIN_DECLS
+
+struct _hb_blob_t {
+  hb_reference_count_t ref_count;
+
+  unsigned int length;
+
+  hb_mutex_t lock;
+  /* the rest are protected by lock */
+
+  unsigned int lock_count;
+  hb_memory_mode_t mode;
+
+  const char *data;
+
+  hb_destroy_func_t destroy;
+  void *user_data;
+};
+
+extern HB_INTERNAL hb_blob_t _hb_blob_nil;
+
+HB_END_DECLS
+
+#endif /* HB_BLOB_PRIVATE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-blob.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-blob-private.h"
+
+#ifdef HAVE_SYS_MMAN_H
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <sys/mman.h>
+#endif /* HAVE_SYS_MMAN_H */
+
+#ifndef HB_DEBUG_BLOB
+#define HB_DEBUG_BLOB HB_DEBUG+0
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+hb_blob_t _hb_blob_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+
+  0, /* length */
+
+  HB_MUTEX_INIT, /* lock */
+
+  0, /* lock_count */
+  HB_MEMORY_MODE_READONLY, /* mode */
+
+  NULL, /* data */
+
+  NULL, /* destroy */
+  NULL /* user_data */
+};
+
+static void
+_hb_blob_destroy_user_data (hb_blob_t *blob)
+{
+  if (blob->destroy) {
+    blob->destroy (blob->user_data);
+    blob->destroy = NULL;
+    blob->user_data = NULL;
+  }
+}
+
+static void
+_hb_blob_unlock_and_destroy (hb_blob_t *blob)
+{
+  hb_blob_unlock (blob);
+  hb_blob_destroy (blob);
+}
+
+hb_blob_t *
+hb_blob_create (const char        *data,
+		unsigned int       length,
+		hb_memory_mode_t   mode,
+		hb_destroy_func_t  destroy,
+		void              *user_data)
+{
+  hb_blob_t *blob;
+
+  if (!length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob)) {
+    if (destroy)
+      destroy (user_data);
+    return &_hb_blob_nil;
+  }
+
+  hb_mutex_init (blob->lock);
+  blob->lock_count = 0;
+
+  blob->data = data;
+  blob->length = length;
+  blob->mode = mode;
+
+  blob->destroy = destroy;
+  blob->user_data = user_data;
+
+  if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
+    blob->mode = HB_MEMORY_MODE_READONLY;
+    if (!hb_blob_try_writable (blob)) {
+      hb_blob_destroy (blob);
+      return &_hb_blob_nil;
+    }
+  }
+
+  return blob;
+}
+
+hb_blob_t *
+hb_blob_create_sub_blob (hb_blob_t    *parent,
+			 unsigned int  offset,
+			 unsigned int  length)
+{
+  hb_blob_t *blob;
+  const char *pdata;
+
+  if (!length || offset >= parent->length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob))
+    return &_hb_blob_nil;
+
+  pdata = hb_blob_lock (parent);
+
+  blob->data = pdata + offset;
+  blob->length = MIN (length, parent->length - offset);
+
+  hb_mutex_lock (parent->lock);
+  blob->mode = parent->mode;
+  hb_mutex_unlock (parent->lock);
+
+  blob->destroy = (hb_destroy_func_t) _hb_blob_unlock_and_destroy;
+  blob->user_data = hb_blob_reference (parent);
+
+  return blob;
+}
+
+hb_blob_t *
+hb_blob_create_empty (void)
+{
+  return &_hb_blob_nil;
+}
+
+hb_blob_t *
+hb_blob_reference (hb_blob_t *blob)
+{
+  HB_OBJECT_DO_REFERENCE (blob);
+}
+
+unsigned int
+hb_blob_get_reference_count (hb_blob_t *blob)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (blob);
+}
+
+void
+hb_blob_destroy (hb_blob_t *blob)
+{
+  HB_OBJECT_DO_DESTROY (blob);
+
+  _hb_blob_destroy_user_data (blob);
+
+  free (blob);
+}
+
+unsigned int
+hb_blob_get_length (hb_blob_t *blob)
+{
+  return blob->length;
+}
+
+const char *
+hb_blob_lock (hb_blob_t *blob)
+{
+  if (HB_OBJECT_IS_INERT (blob))
+    return NULL;
+
+  hb_mutex_lock (blob->lock);
+
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	     blob->lock_count, blob->data);
+
+  blob->lock_count++;
+
+  hb_mutex_unlock (blob->lock);
+
+  return blob->data;
+}
+
+void
+hb_blob_unlock (hb_blob_t *blob)
+{
+  if (HB_OBJECT_IS_INERT (blob))
+    return;
+
+  hb_mutex_lock (blob->lock);
+
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	     blob->lock_count, blob->data);
+
+  assert (blob->lock_count > 0);
+  blob->lock_count--;
+
+  hb_mutex_unlock (blob->lock);
+}
+
+hb_bool_t
+hb_blob_is_writable (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
+
+
+static hb_bool_t
+_try_make_writable_inplace_unix_locked (hb_blob_t *blob)
+{
+#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
+  uintptr_t pagesize = -1, mask, length;
+  const char *addr;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
+  pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
+#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+  pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
+#elif defined(HAVE_GETPAGESIZE)
+  pagesize = (uintptr_t) getpagesize ();
+#endif
+
+  if ((uintptr_t) -1L == pagesize) {
+    if (HB_DEBUG_BLOB)
+      fprintf (stderr, "%p %s: failed to get pagesize: %s\n", blob, __FUNCTION__, strerror (errno));
+    return FALSE;
+  }
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s: pagesize is %u\n", blob, __FUNCTION__, pagesize);
+
+  mask = ~(pagesize-1);
+  addr = (const char *) (((uintptr_t) blob->data) & mask);
+  length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s: calling mprotect on [%p..%p] (%d bytes)\n",
+	     blob, __FUNCTION__,
+	     addr, addr+length, length);
+  if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
+    if (HB_DEBUG_BLOB)
+      fprintf (stderr, "%p %s: %s\n", blob, __FUNCTION__, strerror (errno));
+    return FALSE;
+  }
+
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s: successfully made [%p..%p] (%d bytes) writable\n",
+	     blob, __FUNCTION__,
+	     addr, addr+length, length);
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
+
+static void
+_try_writable_inplace_locked (hb_blob_t *blob)
+{
+  if (HB_DEBUG_BLOB)
+    fprintf (stderr, "%p %s: making writable\n", blob, __FUNCTION__);
+
+  if (_try_make_writable_inplace_unix_locked (blob)) {
+    if (HB_DEBUG_BLOB)
+      fprintf (stderr, "%p %s: making writable -> succeeded\n", blob, __FUNCTION__);
+    blob->mode = HB_MEMORY_MODE_WRITABLE;
+  } else {
+    if (HB_DEBUG_BLOB)
+      fprintf (stderr, "%p %s: making writable -> FAILED\n", blob, __FUNCTION__);
+    /* Failed to make writable inplace, mark that */
+    blob->mode = HB_MEMORY_MODE_READONLY;
+  }
+}
+
+hb_bool_t
+hb_blob_try_writable_inplace (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
+    _try_writable_inplace_locked (blob);
+
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
+
+hb_bool_t
+hb_blob_try_writable (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
+    _try_writable_inplace_locked (blob);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY)
+  {
+    char *new_data;
+
+    if (HB_DEBUG_BLOB)
+      fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	       blob->lock_count, blob->data);
+
+    if (blob->lock_count)
+      goto done;
+
+    new_data = malloc (blob->length);
+    if (new_data) {
+      if (HB_DEBUG_BLOB)
+	fprintf (stderr, "%p %s: dupped successfully -> %p\n", blob, __FUNCTION__, blob->data);
+      memcpy (new_data, blob->data, blob->length);
+      _hb_blob_destroy_user_data (blob);
+      blob->mode = HB_MEMORY_MODE_WRITABLE;
+      blob->data = new_data;
+      blob->destroy = free;
+      blob->user_data = new_data;
+    }
+  }
+
+done:
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-blob.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BLOB_H
+#define HB_BLOB_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+typedef enum {
+  HB_MEMORY_MODE_DUPLICATE,
+  HB_MEMORY_MODE_READONLY,
+  HB_MEMORY_MODE_WRITABLE,
+  HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
+} hb_memory_mode_t;
+
+typedef struct _hb_blob_t hb_blob_t;
+
+hb_blob_t *
+hb_blob_create (const char        *data,
+		unsigned int       length,
+		hb_memory_mode_t   mode,
+		hb_destroy_func_t  destroy,
+		void              *user_data);
+
+hb_blob_t *
+hb_blob_create_sub_blob (hb_blob_t    *parent,
+			 unsigned int  offset,
+			 unsigned int  length);
+
+hb_blob_t *
+hb_blob_create_empty (void);
+
+hb_blob_t *
+hb_blob_reference (hb_blob_t *blob);
+
+unsigned int
+hb_blob_get_reference_count (hb_blob_t *blob);
+
+void
+hb_blob_destroy (hb_blob_t *blob);
+
+unsigned int
+hb_blob_get_length (hb_blob_t *blob);
+
+const char *
+hb_blob_lock (hb_blob_t *blob);
+
+void
+hb_blob_unlock (hb_blob_t *blob);
+
+hb_bool_t
+hb_blob_is_writable (hb_blob_t *blob);
+
+hb_bool_t
+hb_blob_try_writable_inplace (hb_blob_t *blob);
+
+hb_bool_t
+hb_blob_try_writable (hb_blob_t *blob);
+
+HB_END_DECLS
+
+#endif /* HB_BLOB_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_PRIVATE_H
+#define HB_BUFFER_PRIVATE_H
+
+#include "hb-private.h"
+#include "hb-buffer.h"
+#include "hb-unicode-private.h"
+
+HB_BEGIN_DECLS
+
+#define HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN 0xFFFF
+
+
+typedef struct _hb_internal_glyph_info_t {
+  hb_codepoint_t codepoint;
+  hb_mask_t      mask;
+  uint32_t       cluster;
+  uint16_t       component;
+  uint16_t       lig_id;
+  uint32_t       gproperty;
+} hb_internal_glyph_info_t;
+
+typedef struct _hb_internal_glyph_position_t {
+  hb_position_t  x_advance;
+  hb_position_t  y_advance;
+  hb_position_t  x_offset;
+  hb_position_t  y_offset;
+  uint32_t       back : 16;		/* number of glyphs to go back
+					   for drawing current glyph */
+  int32_t        cursive_chain : 16;	/* character to which this connects,
+					   may be positive or negative */
+} hb_internal_glyph_position_t;
+
+ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_internal_glyph_info_t));
+ASSERT_STATIC (sizeof (hb_glyph_position_t) == sizeof (hb_internal_glyph_position_t));
+ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
+
+
+HB_INTERNAL void
+_hb_buffer_swap (hb_buffer_t *buffer);
+
+HB_INTERNAL void
+_hb_buffer_clear_output (hb_buffer_t *buffer);
+
+HB_INTERNAL void
+_hb_buffer_add_output_glyphs (hb_buffer_t *buffer,
+			      unsigned int num_in,
+			      unsigned int num_out,
+			      const hb_codepoint_t *glyph_data,
+			      unsigned short component,
+			      unsigned short ligID);
+
+HB_INTERNAL void
+_hb_buffer_add_output_glyphs_be16 (hb_buffer_t *buffer,
+				   unsigned int num_in,
+				   unsigned int num_out,
+				   const uint16_t *glyph_data_be,
+				   unsigned short component,
+				   unsigned short ligID);
+
+HB_INTERNAL void
+_hb_buffer_add_output_glyph (hb_buffer_t *buffer,
+			     hb_codepoint_t glyph_index,
+			     unsigned short component,
+			     unsigned short ligID);
+
+HB_INTERNAL void
+_hb_buffer_next_glyph (hb_buffer_t *buffer);
+
+
+HB_INTERNAL void
+_hb_buffer_clear_masks (hb_buffer_t *buffer);
+
+HB_INTERNAL void
+_hb_buffer_set_masks (hb_buffer_t *buffer,
+		      hb_mask_t    value,
+		      hb_mask_t    mask,
+		      unsigned int cluster_start,
+		      unsigned int cluster_end);
+
+
+struct _hb_buffer_t {
+  hb_reference_count_t ref_count;
+
+  /* Information about how the text in the buffer should be treated */
+  hb_unicode_funcs_t *unicode;
+  hb_direction_t      direction;
+  hb_script_t         script;
+  hb_language_t       language;
+
+  /* Buffer contents */
+
+  unsigned int allocated; /* Length of allocated arrays */
+
+  hb_bool_t have_output; /* Whether we have an output buffer going on */
+  hb_bool_t have_positions; /* Whether we have positions */
+  hb_bool_t in_error; /* Allocation failed */
+
+  unsigned int i; /* Cursor into ->info and ->pos arrays */
+  unsigned int len; /* Length of ->info and ->pos arrays */
+  unsigned int out_len; /* Length of ->out array */
+
+  hb_internal_glyph_info_t     *info;
+  hb_internal_glyph_info_t     *out_info;
+  hb_internal_glyph_position_t *pos;
+
+  /* Other stuff */
+
+  unsigned int max_lig_id;
+
+
+  /* Methods */
+  inline unsigned int allocate_lig_id (void) { return max_lig_id++; }
+  inline void swap (void) { _hb_buffer_swap (this); }
+  inline void clear_output (void) { _hb_buffer_clear_output (this); }
+  inline void next_glyph (void) { _hb_buffer_next_glyph (this); }
+  inline void add_output_glyphs (unsigned int num_in,
+				 unsigned int num_out,
+				 const hb_codepoint_t *glyph_data,
+				 unsigned short component,
+				 unsigned short ligID)
+  { _hb_buffer_add_output_glyphs (this, num_in, num_out, glyph_data, component, ligID); }
+  inline void add_output_glyphs_be16 (unsigned int num_in,
+				      unsigned int num_out,
+				      const uint16_t *glyph_data_be,
+				      unsigned short component,
+				      unsigned short ligID)
+  { _hb_buffer_add_output_glyphs_be16 (this, num_in, num_out, glyph_data_be, component, ligID); }
+  inline void add_output_glyph (hb_codepoint_t glyph_index,
+				unsigned short component = 0xFFFF,
+				unsigned short ligID = 0xFFFF)
+  { _hb_buffer_add_output_glyph (this, glyph_index, component, ligID); }
+  inline void replace_glyph (hb_codepoint_t glyph_index) { add_output_glyph (glyph_index); }
+
+  inline void clear_masks (void) { _hb_buffer_clear_masks (this); }
+  inline void set_masks (hb_mask_t    value,
+			 hb_mask_t mask,
+			 unsigned int cluster_start,
+			 unsigned int cluster_end)
+  { _hb_buffer_set_masks (this, value, mask, cluster_start, cluster_end); }
+
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_BUFFER_PRIVATE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#include "hb-buffer-private.hh"
+
+#include <string.h>
+
+
+static hb_buffer_t _hb_buffer_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+
+  &_hb_unicode_funcs_nil  /* unicode */
+};
+
+/* Here is how the buffer works internally:
+ *
+ * There are two info pointers: info and out_info.  They always have
+ * the same allocated size, but different lengths.
+ *
+ * As an optimization, both info and out_info may point to the
+ * same piece of memory, which is owned by info.  This remains the
+ * case as long as out_len doesn't exceed len at any time.
+ * In that case, swap() is no-op and the glyph operations operate
+ * mostly in-place.
+ *
+ * As soon as out_info gets longer than info, out_info is moved over
+ * to an alternate buffer (which we reuse the pos buffer for!), and its
+ * current contents (out_len entries) are copied to the new place.
+ * This should all remain transparent to the user.  swap() then
+ * switches info and out_info.
+ */
+
+
+static hb_bool_t
+_hb_buffer_enlarge (hb_buffer_t *buffer, unsigned int size)
+{
+  if (unlikely (buffer->in_error))
+    return FALSE;
+
+  unsigned int new_allocated = buffer->allocated;
+  hb_internal_glyph_position_t *new_pos;
+  hb_internal_glyph_info_t *new_info;
+  bool separate_out;
+
+  separate_out = buffer->out_info != buffer->info;
+
+  while (size > new_allocated)
+    new_allocated += (new_allocated >> 1) + 8;
+
+  new_pos = (hb_internal_glyph_position_t *) realloc (buffer->pos, new_allocated * sizeof (buffer->pos[0]));
+  new_info = (hb_internal_glyph_info_t *) realloc (buffer->info, new_allocated * sizeof (buffer->info[0]));
+
+  if (unlikely (!new_pos || !new_info))
+    buffer->in_error = TRUE;
+
+  if (likely (new_pos))
+    buffer->pos = new_pos;
+
+  if (likely (new_info))
+    buffer->info = new_info;
+
+  buffer->out_info = separate_out ? (hb_internal_glyph_info_t *) buffer->pos : buffer->info;
+  if (likely (!buffer->in_error))
+    buffer->allocated = new_allocated;
+
+  return likely (!buffer->in_error);
+}
+
+static inline hb_bool_t
+_hb_buffer_ensure (hb_buffer_t *buffer, unsigned int size)
+{
+  return likely (size <= buffer->allocated) ? TRUE : _hb_buffer_enlarge (buffer, size);
+}
+
+static hb_bool_t
+_hb_buffer_ensure_separate (hb_buffer_t *buffer, unsigned int size)
+{
+  if (unlikely (!_hb_buffer_ensure (buffer, size))) return FALSE;
+
+  if (buffer->out_info == buffer->info)
+  {
+    assert (buffer->have_output);
+
+    buffer->out_info = (hb_internal_glyph_info_t *) buffer->pos;
+    memcpy (buffer->out_info, buffer->info, buffer->out_len * sizeof (buffer->out_info[0]));
+  }
+
+  return TRUE;
+}
+
+
+/* Public API */
+
+hb_buffer_t *
+hb_buffer_create (unsigned int pre_alloc_size)
+{
+  hb_buffer_t *buffer;
+
+  if (!HB_OBJECT_DO_CREATE (hb_buffer_t, buffer))
+    return &_hb_buffer_nil;
+
+  if (pre_alloc_size)
+    _hb_buffer_ensure (buffer, pre_alloc_size);
+
+  buffer->unicode = &_hb_unicode_funcs_nil;
+
+  return buffer;
+}
+
+hb_buffer_t *
+hb_buffer_reference (hb_buffer_t *buffer)
+{
+  HB_OBJECT_DO_REFERENCE (buffer);
+}
+
+unsigned int
+hb_buffer_get_reference_count (hb_buffer_t *buffer)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (buffer);
+}
+
+void
+hb_buffer_destroy (hb_buffer_t *buffer)
+{
+  HB_OBJECT_DO_DESTROY (buffer);
+
+  hb_unicode_funcs_destroy (buffer->unicode);
+
+  free (buffer->info);
+  free (buffer->pos);
+
+  free (buffer);
+}
+
+
+void
+hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
+			     hb_unicode_funcs_t *unicode)
+{
+  if (!unicode)
+    unicode = &_hb_unicode_funcs_nil;
+
+  hb_unicode_funcs_reference (unicode);
+  hb_unicode_funcs_destroy (buffer->unicode);
+  buffer->unicode = unicode;
+}
+
+hb_unicode_funcs_t *
+hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
+{
+  return buffer->unicode;
+}
+
+void
+hb_buffer_set_direction (hb_buffer_t    *buffer,
+			 hb_direction_t  direction)
+
+{
+  buffer->direction = direction;
+}
+
+hb_direction_t
+hb_buffer_get_direction (hb_buffer_t    *buffer)
+{
+  return buffer->direction;
+}
+
+void
+hb_buffer_set_script (hb_buffer_t *buffer,
+		      hb_script_t  script)
+{
+  buffer->script = script;
+}
+
+hb_script_t
+hb_buffer_get_script (hb_buffer_t *buffer)
+{
+  return buffer->script;
+}
+
+void
+hb_buffer_set_language (hb_buffer_t   *buffer,
+			hb_language_t  language)
+{
+  buffer->language = language;
+}
+
+hb_language_t
+hb_buffer_get_language (hb_buffer_t *buffer)
+{
+  return buffer->language;
+}
+
+
+void
+hb_buffer_clear (hb_buffer_t *buffer)
+{
+  buffer->have_output = FALSE;
+  buffer->have_positions = FALSE;
+  buffer->in_error = FALSE;
+  buffer->len = 0;
+  buffer->out_len = 0;
+  buffer->i = 0;
+  buffer->out_info = buffer->info;
+  buffer->max_lig_id = 0;
+}
+
+hb_bool_t
+hb_buffer_ensure (hb_buffer_t *buffer, unsigned int size)
+{
+  return _hb_buffer_ensure (buffer, size);
+}
+
+void
+hb_buffer_add_glyph (hb_buffer_t    *buffer,
+		     hb_codepoint_t  codepoint,
+		     hb_mask_t       mask,
+		     unsigned int    cluster)
+{
+  hb_internal_glyph_info_t *glyph;
+
+  if (unlikely (!_hb_buffer_ensure (buffer, buffer->len + 1))) return;
+
+  glyph = &buffer->info[buffer->len];
+  glyph->codepoint = codepoint;
+  glyph->mask = mask;
+  glyph->cluster = cluster;
+  glyph->component = 0;
+  glyph->lig_id = 0;
+  glyph->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
+
+  buffer->len++;
+}
+
+void
+hb_buffer_clear_positions (hb_buffer_t *buffer)
+{
+  _hb_buffer_clear_output (buffer);
+  buffer->have_output = FALSE;
+  buffer->have_positions = TRUE;
+
+  if (unlikely (!buffer->pos))
+  {
+    buffer->pos = (hb_internal_glyph_position_t *) calloc (buffer->allocated, sizeof (buffer->pos[0]));
+    return;
+  }
+
+  memset (buffer->pos, 0, sizeof (buffer->pos[0]) * buffer->len);
+}
+
+/* HarfBuzz-Internal API */
+
+void
+_hb_buffer_clear_output (hb_buffer_t *buffer)
+{
+  buffer->have_output = TRUE;
+  buffer->have_positions = FALSE;
+  buffer->out_len = 0;
+  buffer->out_info = buffer->info;
+}
+
+void
+_hb_buffer_swap (hb_buffer_t *buffer)
+{
+  unsigned int tmp;
+
+  assert (buffer->have_output);
+
+  if (unlikely (buffer->in_error)) return;
+
+  if (buffer->out_info != buffer->info)
+  {
+    hb_internal_glyph_info_t *tmp_string;
+    tmp_string = buffer->info;
+    buffer->info = buffer->out_info;
+    buffer->out_info = tmp_string;
+    buffer->pos = (hb_internal_glyph_position_t *) buffer->out_info;
+  }
+
+  tmp = buffer->len;
+  buffer->len = buffer->out_len;
+  buffer->out_len = tmp;
+
+  buffer->i = 0;
+}
+
+/* The following function copies `num_out' elements from `glyph_data'
+   to `buffer->out_info', advancing the in array pointer in the structure
+   by `num_in' elements, and the out array pointer by `num_out' elements.
+   Finally, it sets the `length' field of `out' equal to
+   `pos' of the `out' structure.
+
+   If `component' is 0xFFFF, the component value from buffer->i
+   will copied `num_out' times, otherwise `component' itself will
+   be used to fill the `component' fields.
+
+   If `lig_id' is 0xFFFF, the lig_id value from buffer->i
+   will copied `num_out' times, otherwise `lig_id' itself will
+   be used to fill the `lig_id' fields.
+
+   The mask for all replacement glyphs are taken
+   from the glyph at position `buffer->i'.
+
+   The cluster value for the glyph at position buffer->i is used
+   for all replacement glyphs */
+
+void
+_hb_buffer_add_output_glyphs (hb_buffer_t *buffer,
+			      unsigned int num_in,
+			      unsigned int num_out,
+			      const hb_codepoint_t *glyph_data,
+			      unsigned short component,
+			      unsigned short lig_id)
+{
+  unsigned int i;
+  unsigned int mask;
+  unsigned int cluster;
+
+  if (buffer->out_info != buffer->info ||
+      buffer->out_len + num_out > buffer->i + num_in)
+  {
+    if (unlikely (!_hb_buffer_ensure_separate (buffer, buffer->out_len + num_out)))
+      return;
+  }
+
+  mask = buffer->info[buffer->i].mask;
+  cluster = buffer->info[buffer->i].cluster;
+  if (component == 0xFFFF)
+    component = buffer->info[buffer->i].component;
+  if (lig_id == 0xFFFF)
+    lig_id = buffer->info[buffer->i].lig_id;
+
+  for (i = 0; i < num_out; i++)
+  {
+    hb_internal_glyph_info_t *info = &buffer->out_info[buffer->out_len + i];
+    info->codepoint = glyph_data[i];
+    info->mask = mask;
+    info->cluster = cluster;
+    info->component = component;
+    info->lig_id = lig_id;
+    info->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
+  }
+
+  buffer->i  += num_in;
+  buffer->out_len += num_out;
+}
+
+void
+_hb_buffer_add_output_glyphs_be16 (hb_buffer_t *buffer,
+				   unsigned int num_in,
+				   unsigned int num_out,
+				   const uint16_t *glyph_data_be,
+				   unsigned short component,
+				   unsigned short lig_id)
+{
+  unsigned int i;
+  unsigned int mask;
+  unsigned int cluster;
+
+  if (buffer->out_info != buffer->info ||
+      buffer->out_len + num_out > buffer->i + num_in)
+  {
+    if (unlikely (!_hb_buffer_ensure_separate (buffer, buffer->out_len + num_out)))
+      return;
+  }
+
+  mask = buffer->info[buffer->i].mask;
+  cluster = buffer->info[buffer->i].cluster;
+  if (component == 0xFFFF)
+    component = buffer->info[buffer->i].component;
+  if (lig_id == 0xFFFF)
+    lig_id = buffer->info[buffer->i].lig_id;
+
+  for (i = 0; i < num_out; i++)
+  {
+    hb_internal_glyph_info_t *info = &buffer->out_info[buffer->out_len + i];
+    info->codepoint = hb_be_uint16 (glyph_data_be[i]);
+    info->mask = mask;
+    info->cluster = cluster;
+    info->component = component;
+    info->lig_id = lig_id;
+    info->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
+  }
+
+  buffer->i  += num_in;
+  buffer->out_len += num_out;
+}
+
+void
+_hb_buffer_add_output_glyph (hb_buffer_t *buffer,
+			     hb_codepoint_t glyph_index,
+			     unsigned short component,
+			     unsigned short lig_id)
+{
+  hb_internal_glyph_info_t *info;
+
+  if (buffer->out_info != buffer->info)
+  {
+    if (unlikely (!_hb_buffer_ensure (buffer, buffer->out_len + 1))) return;
+    buffer->out_info[buffer->out_len] = buffer->info[buffer->i];
+  }
+  else if (buffer->out_len != buffer->i)
+    buffer->out_info[buffer->out_len] = buffer->info[buffer->i];
+
+  info = &buffer->out_info[buffer->out_len];
+  info->codepoint = glyph_index;
+  if (component != 0xFFFF)
+    info->component = component;
+  if (lig_id != 0xFFFF)
+    info->lig_id = lig_id;
+  info->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
+
+  buffer->i++;
+  buffer->out_len++;
+}
+
+void
+_hb_buffer_next_glyph (hb_buffer_t *buffer)
+{
+  if (buffer->have_output)
+  {
+    if (buffer->out_info != buffer->info)
+    {
+      if (unlikely (!_hb_buffer_ensure (buffer, buffer->out_len + 1))) return;
+      buffer->out_info[buffer->out_len] = buffer->info[buffer->i];
+    }
+    else if (buffer->out_len != buffer->i)
+      buffer->out_info[buffer->out_len] = buffer->info[buffer->i];
+
+    buffer->out_len++;
+  }
+
+  buffer->i++;
+}
+
+void
+_hb_buffer_clear_masks (hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].mask = 1;
+}
+
+void
+_hb_buffer_set_masks (hb_buffer_t *buffer,
+		      hb_mask_t    value,
+		      hb_mask_t    mask,
+		      unsigned int cluster_start,
+		      unsigned int cluster_end)
+{
+  hb_mask_t not_mask = ~mask;
+
+  if (cluster_start == 0 && cluster_end == (unsigned int)-1) {
+    unsigned int count = buffer->len;
+    for (unsigned int i = 0; i < count; i++)
+      buffer->info[i].mask = (buffer->info[i].mask & not_mask) | value;
+    return;
+  }
+
+  /* Binary search to find the start position and go from there. */
+  unsigned int min = 0, max = buffer->len;
+  while (min < max)
+  {
+    unsigned int mid = min + ((max - min) / 2);
+    if (buffer->info[mid].cluster < cluster_start)
+      min = mid + 1;
+    else
+      max = mid;
+  }
+  unsigned int count = buffer->len;
+  for (unsigned int i = min; i < count && buffer->info[i].cluster < cluster_end; i++)
+    buffer->info[i].mask = (buffer->info[i].mask & not_mask) | value;
+}
+
+
+/* Public API again */
+
+unsigned int
+hb_buffer_get_length (hb_buffer_t *buffer)
+{
+  return buffer->len;
+}
+
+/* Return value valid as long as buffer not modified */
+hb_glyph_info_t *
+hb_buffer_get_glyph_infos (hb_buffer_t *buffer)
+{
+  return (hb_glyph_info_t *) buffer->info;
+}
+
+/* Return value valid as long as buffer not modified */
+hb_glyph_position_t *
+hb_buffer_get_glyph_positions (hb_buffer_t *buffer)
+{
+  if (!buffer->have_positions)
+    hb_buffer_clear_positions (buffer);
+
+  return (hb_glyph_position_t *) buffer->pos;
+}
+
+
+static void
+reverse_range (hb_buffer_t *buffer,
+	       unsigned int start,
+	       unsigned int end)
+{
+  unsigned int i, j;
+
+  for (i = start, j = end - 1; i < j; i++, j--) {
+    hb_internal_glyph_info_t t;
+
+    t = buffer->info[i];
+    buffer->info[i] = buffer->info[j];
+    buffer->info[j] = t;
+  }
+
+  if (buffer->pos) {
+    for (i = 0, j = end - 1; i < j; i++, j--) {
+      hb_internal_glyph_position_t t;
+
+      t = buffer->pos[i];
+      buffer->pos[i] = buffer->pos[j];
+      buffer->pos[j] = t;
+    }
+  }
+}
+
+void
+hb_buffer_reverse (hb_buffer_t *buffer)
+{
+  if (unlikely (!buffer->len))
+    return;
+
+  reverse_range (buffer, 0, buffer->len);
+}
+
+void
+hb_buffer_reverse_clusters (hb_buffer_t *buffer)
+{
+  unsigned int i, start, count, last_cluster;
+
+  if (unlikely (!buffer->len))
+    return;
+
+  hb_buffer_reverse (buffer);
+
+  count = buffer->len;
+  start = 0;
+  last_cluster = buffer->info[0].cluster;
+  for (i = 1; i < count; i++) {
+    if (last_cluster != buffer->info[i].cluster) {
+      reverse_range (buffer, start, i);
+      start = i;
+      last_cluster = buffer->info[i].cluster;
+    }
+  }
+  reverse_range (buffer, start, i);
+}
+
+
+#define ADD_UTF(T) \
+	HB_STMT_START { \
+	  const T *next = (const T *) text + item_offset; \
+	  const T *end = next + item_length; \
+	  while (next < end) { \
+	    hb_codepoint_t u; \
+	    const T *old_next = next; \
+	    next = UTF_NEXT (next, end, u); \
+	    hb_buffer_add_glyph (buffer, u, 1,  old_next - (const T *) text); \
+	  } \
+	} HB_STMT_END
+
+
+#define UTF8_COMPUTE(Char, Mask, Len) \
+  if (Char < 128) { Len = 1; Mask = 0x7f; } \
+  else if ((Char & 0xe0) == 0xc0) { Len = 2; Mask = 0x1f; } \
+  else if ((Char & 0xf0) == 0xe0) { Len = 3; Mask = 0x0f; } \
+  else if ((Char & 0xf8) == 0xf0) { Len = 4; Mask = 0x07; } \
+  else Len = 0;
+
+static inline const uint8_t *
+hb_utf8_next (const uint8_t *text,
+	      const uint8_t *end,
+	      hb_codepoint_t *unicode)
+{
+  uint8_t c = *text;
+  unsigned int mask, len;
+
+  /* TODO check for overlong sequences?  also: optimize? */
+
+  UTF8_COMPUTE (c, mask, len);
+  if (unlikely (!len || (unsigned int) (end - text) < len)) {
+    *unicode = -1;
+    return text + 1;
+  } else {
+    hb_codepoint_t result;
+    unsigned int i;
+    result = c & mask;
+    for (i = 1; i < len; i++)
+      {
+	if (unlikely ((text[i] & 0xc0) != 0x80))
+	  {
+	    *unicode = -1;
+	    return text + 1;
+	  }
+	result <<= 6;
+	result |= (text[i] & 0x3f);
+      }
+    *unicode = result;
+    return text + len;
+  }
+}
+
+void
+hb_buffer_add_utf8 (hb_buffer_t  *buffer,
+		    const char   *text,
+		    unsigned int  text_length HB_UNUSED,
+		    unsigned int  item_offset,
+		    unsigned int  item_length)
+{
+#define UTF_NEXT(S, E, U)	hb_utf8_next (S, E, &(U))
+  ADD_UTF (uint8_t);
+#undef UTF_NEXT
+}
+
+static inline const uint16_t *
+hb_utf16_next (const uint16_t *text,
+	       const uint16_t *end,
+	       hb_codepoint_t *unicode)
+{
+  uint16_t c = *text++;
+
+  if (unlikely (c >= 0xd800 && c < 0xdc00)) {
+    /* high surrogate */
+    uint16_t l;
+    if (text < end && ((l = *text), unlikely (l >= 0xdc00 && l < 0xe000))) {
+      /* low surrogate */
+      *unicode = ((hb_codepoint_t) ((c) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000);
+       text++;
+    } else
+      *unicode = -1;
+  } else
+    *unicode = c;
+
+  return text;
+}
+
+void
+hb_buffer_add_utf16 (hb_buffer_t    *buffer,
+		     const uint16_t *text,
+		     unsigned int    text_length HB_UNUSED,
+		     unsigned int    item_offset,
+		     unsigned int    item_length)
+{
+#define UTF_NEXT(S, E, U)	hb_utf16_next (S, E, &(U))
+  ADD_UTF (uint16_t);
+#undef UTF_NEXT
+}
+
+void
+hb_buffer_add_utf32 (hb_buffer_t    *buffer,
+		     const uint32_t *text,
+		     unsigned int    text_length HB_UNUSED,
+		     unsigned int    item_offset,
+		     unsigned int    item_length)
+{
+#define UTF_NEXT(S, E, U)	((U) = *(S), (S)+1)
+  ADD_UTF (uint32_t);
+#undef UTF_NEXT
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007,2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_H
+#define HB_BUFFER_H
+
+#include "hb-common.h"
+#include "hb-unicode.h"
+#include "hb-language.h"
+
+HB_BEGIN_DECLS
+
+typedef struct _hb_buffer_t hb_buffer_t;
+
+typedef struct _hb_glyph_info_t {
+  hb_codepoint_t codepoint;
+  hb_mask_t      mask;
+  uint32_t       cluster;
+  uint32_t       internal1;
+  uint32_t       internal2;
+} hb_glyph_info_t;
+
+typedef struct _hb_glyph_position_t {
+  hb_position_t  x_advance;
+  hb_position_t  y_advance;
+  hb_position_t  x_offset;
+  hb_position_t  y_offset;
+  uint32_t       internal;
+} hb_glyph_position_t;
+
+
+hb_buffer_t *
+hb_buffer_create (unsigned int pre_alloc_size);
+
+hb_buffer_t *
+hb_buffer_reference (hb_buffer_t *buffer);
+
+unsigned int
+hb_buffer_get_reference_count (hb_buffer_t *buffer);
+
+void
+hb_buffer_destroy (hb_buffer_t *buffer);
+
+
+void
+hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
+			     hb_unicode_funcs_t *unicode_funcs);
+
+hb_unicode_funcs_t *
+hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer);
+
+void
+hb_buffer_set_direction (hb_buffer_t    *buffer,
+			 hb_direction_t  direction);
+
+hb_direction_t
+hb_buffer_get_direction (hb_buffer_t *buffer);
+
+void
+hb_buffer_set_script (hb_buffer_t *buffer,
+		      hb_script_t  script);
+
+hb_script_t
+hb_buffer_get_script (hb_buffer_t *buffer);
+
+void
+hb_buffer_set_language (hb_buffer_t   *buffer,
+			hb_language_t  language);
+
+hb_language_t
+hb_buffer_get_language (hb_buffer_t *buffer);
+
+
+void
+hb_buffer_clear (hb_buffer_t *buffer);
+
+void
+hb_buffer_clear_positions (hb_buffer_t *buffer);
+
+hb_bool_t
+hb_buffer_ensure (hb_buffer_t  *buffer,
+		  unsigned int  size);
+
+void
+hb_buffer_reverse (hb_buffer_t *buffer);
+
+void
+hb_buffer_reverse_clusters (hb_buffer_t *buffer);
+
+
+/* Filling the buffer in */
+
+void
+hb_buffer_add_glyph (hb_buffer_t    *buffer,
+		     hb_codepoint_t  codepoint,
+		     hb_mask_t       mask,
+		     unsigned int    cluster);
+
+void
+hb_buffer_add_utf8 (hb_buffer_t  *buffer,
+		    const char   *text,
+		    unsigned int  text_length,
+		    unsigned int  item_offset,
+		    unsigned int  item_length);
+
+void
+hb_buffer_add_utf16 (hb_buffer_t    *buffer,
+		     const uint16_t *text,
+		     unsigned int    text_length,
+		     unsigned int    item_offset,
+		     unsigned int    item_length);
+
+void
+hb_buffer_add_utf32 (hb_buffer_t    *buffer,
+		     const uint32_t *text,
+		     unsigned int    text_length,
+		     unsigned int    item_offset,
+		     unsigned int    item_length);
+
+
+/* Getting glyphs out of the buffer */
+
+/* Return value valid as long as buffer not modified */
+unsigned int
+hb_buffer_get_length (hb_buffer_t *buffer);
+
+/* Return value valid as long as buffer not modified */
+hb_glyph_info_t *
+hb_buffer_get_glyph_infos (hb_buffer_t *buffer);
+
+/* Return value valid as long as buffer not modified */
+hb_glyph_position_t *
+hb_buffer_get_glyph_positions (hb_buffer_t *buffer);
+
+
+HB_END_DECLS
+
+#endif /* HB_BUFFER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-common.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+hb_tag_t
+hb_tag_from_string (const char *s)
+{
+  char tag[4];
+  unsigned int i;
+
+  for (i = 0; i < 4 && s[i]; i++)
+    tag[i] = s[i];
+  for (; i < 4; i++)
+    tag[i] = ' ';
+
+  return HB_TAG_STR (tag);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007,2008,2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_COMMON_H
+#define HB_COMMON_H
+
+#ifdef _MSC_VER
+#define _HB__STR2__(x) #x
+#define _HB__STR1__(x) _HB__STR2__(x)
+#define _HB__LOC__ __FILE__ "("_HB__STR1__(__LINE__)") : Warning Msg: "
+#pragma message(_HB__LOC__"Not using stdint.h; integer types may have wrong size")
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#ifndef __cplusplus
+#define inline __inline
+#endif
+#else
+#include <stdint.h>
+#endif
+
+# ifdef __cplusplus
+#  define HB_BEGIN_DECLS	extern "C" {
+#  define HB_END_DECLS		}
+# else /* !__cplusplus */
+#  define HB_BEGIN_DECLS
+#  define HB_END_DECLS
+# endif /* !__cplusplus */
+
+typedef int hb_bool_t;
+
+typedef uint32_t hb_tag_t;
+#define HB_TAG(a,b,c,d) ((hb_tag_t)(((uint8_t)a<<24)|((uint8_t)b<<16)|((uint8_t)c<<8)|(uint8_t)d))
+#define HB_TAG_STR(s)   (HB_TAG(((const char *) s)[0], \
+				((const char *) s)[1], \
+				((const char *) s)[2], \
+				((const char *) s)[3]))
+#define HB_TAG_NONE HB_TAG(0,0,0,0)
+
+hb_tag_t hb_tag_from_string (const char *s);
+
+
+typedef uint32_t hb_codepoint_t;
+typedef int32_t hb_position_t;
+typedef uint32_t hb_mask_t;
+
+typedef void (*hb_destroy_func_t) (void *user_data);
+
+typedef enum _hb_direction_t {
+  HB_DIRECTION_LTR,
+  HB_DIRECTION_RTL,
+  HB_DIRECTION_TTB,
+  HB_DIRECTION_BTT
+} hb_direction_t;
+
+#define HB_DIRECTION_IS_HORIZONTAL(dir)	((((unsigned int) (dir)) & ~1U) == 0)
+#define HB_DIRECTION_IS_VERTICAL(dir)	((((unsigned int) (dir)) & ~1U) == 2)
+#define HB_DIRECTION_IS_FORWARD(dir)	((((unsigned int) (dir)) & ~2U) == 0)
+#define HB_DIRECTION_IS_BACKWARD(dir)	((((unsigned int) (dir)) & ~2U) == 1)
+#define HB_DIRECTION_REVERSE(dir)	((hb_direction_t) (((unsigned int) (dir)) ^ 1))
+
+
+#endif /* HB_COMMON_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FONT_PRIVATE_H
+#define HB_FONT_PRIVATE_H
+
+#include "hb-private.h"
+
+#include "hb-font.h"
+
+#include "hb-ot-head-private.hh"
+
+HB_BEGIN_DECLS
+
+/*
+ * hb_font_funcs_t
+ */
+
+struct _hb_font_funcs_t {
+  hb_reference_count_t ref_count;
+
+  hb_bool_t immutable;
+
+  struct {
+    hb_font_get_glyph_func_t		get_glyph;
+    hb_font_get_contour_point_func_t	get_contour_point;
+    hb_font_get_glyph_metrics_func_t	get_glyph_metrics;
+    hb_font_get_kerning_func_t		get_kerning;
+  } v;
+};
+
+extern HB_INTERNAL hb_font_funcs_t _hb_font_funcs_nil;
+
+
+/*
+ * hb_face_t
+ */
+
+struct _hb_face_t {
+  hb_reference_count_t ref_count;
+
+  hb_get_table_func_t  get_table;
+  hb_destroy_func_t    destroy;
+  void                *user_data;
+
+  hb_blob_t *head_blob;
+  const struct head *head_table;
+
+  struct hb_ot_layout_t *ot_layout;
+};
+
+
+/*
+ * hb_font_t
+ */
+
+struct _hb_font_t {
+  hb_reference_count_t ref_count;
+
+  unsigned int x_scale;
+  unsigned int y_scale;
+
+  unsigned int x_ppem;
+  unsigned int y_ppem;
+
+  hb_font_funcs_t   *klass;
+  hb_destroy_func_t  destroy;
+  void              *user_data;
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_FONT_PRIVATE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-font-private.hh"
+#include "hb-blob-private.h"
+#include "hb-open-file-private.hh"
+
+#include "hb-ot-layout-private.hh"
+
+#include <string.h>
+
+
+/*
+ * hb_font_funcs_t
+ */
+
+static hb_codepoint_t
+hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
+		       hb_face_t *face HB_UNUSED,
+		       const void *user_data HB_UNUSED,
+		       hb_codepoint_t unicode HB_UNUSED,
+		       hb_codepoint_t variation_selector HB_UNUSED)
+{ return 0; }
+
+static hb_bool_t
+hb_font_get_contour_point_nil (hb_font_t *font HB_UNUSED,
+			       hb_face_t *face HB_UNUSED,
+			       const void *user_data HB_UNUSED,
+			       unsigned int point_index HB_UNUSED,
+			       hb_codepoint_t glyph HB_UNUSED,
+			       hb_position_t *x HB_UNUSED,
+			       hb_position_t *y HB_UNUSED)
+{ return false; }
+
+static void
+hb_font_get_glyph_metrics_nil (hb_font_t *font HB_UNUSED,
+			       hb_face_t *face HB_UNUSED,
+			       const void *user_data HB_UNUSED,
+			       hb_codepoint_t glyph HB_UNUSED,
+			       hb_glyph_metrics_t *metrics HB_UNUSED)
+{ }
+
+static hb_position_t
+hb_font_get_kerning_nil (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data HB_UNUSED,
+			 hb_codepoint_t first_glyph HB_UNUSED,
+			 hb_codepoint_t second_glyph HB_UNUSED)
+{ return 0; }
+
+hb_font_funcs_t _hb_font_funcs_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  TRUE,  /* immutable */
+  {
+    hb_font_get_glyph_nil,
+    hb_font_get_contour_point_nil,
+    hb_font_get_glyph_metrics_nil,
+    hb_font_get_kerning_nil
+  }
+};
+
+hb_font_funcs_t *
+hb_font_funcs_create (void)
+{
+  hb_font_funcs_t *ffuncs;
+
+  if (!HB_OBJECT_DO_CREATE (hb_font_funcs_t, ffuncs))
+    return &_hb_font_funcs_nil;
+
+  ffuncs->v = _hb_font_funcs_nil.v;
+
+  return ffuncs;
+}
+
+hb_font_funcs_t *
+hb_font_funcs_reference (hb_font_funcs_t *ffuncs)
+{
+  HB_OBJECT_DO_REFERENCE (ffuncs);
+}
+
+unsigned int
+hb_font_funcs_get_reference_count (hb_font_funcs_t *ffuncs)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (ffuncs);
+}
+
+void
+hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
+{
+  HB_OBJECT_DO_DESTROY (ffuncs);
+
+  free (ffuncs);
+}
+
+hb_font_funcs_t *
+hb_font_funcs_copy (hb_font_funcs_t *other_ffuncs)
+{
+  hb_font_funcs_t *ffuncs;
+
+  if (!HB_OBJECT_DO_CREATE (hb_font_funcs_t, ffuncs))
+    return &_hb_font_funcs_nil;
+
+  ffuncs->v = other_ffuncs->v;
+
+  return ffuncs;
+}
+
+void
+hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
+{
+  if (HB_OBJECT_IS_INERT (ffuncs))
+    return;
+
+  ffuncs->immutable = TRUE;
+}
+
+
+void
+hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
+			      hb_font_get_glyph_func_t glyph_func)
+{
+  if (ffuncs->immutable)
+    return;
+
+  ffuncs->v.get_glyph = glyph_func ? glyph_func : hb_font_get_glyph_nil;
+}
+
+void
+hb_font_funcs_set_contour_point_func (hb_font_funcs_t *ffuncs,
+				      hb_font_get_contour_point_func_t contour_point_func)
+{
+  if (ffuncs->immutable)
+    return;
+
+  ffuncs->v.get_contour_point = contour_point_func ? contour_point_func : hb_font_get_contour_point_nil;
+}
+
+void
+hb_font_funcs_set_glyph_metrics_func (hb_font_funcs_t *ffuncs,
+				      hb_font_get_glyph_metrics_func_t glyph_metrics_func)
+{
+  if (ffuncs->immutable)
+    return;
+
+  ffuncs->v.get_glyph_metrics = glyph_metrics_func ? glyph_metrics_func : hb_font_get_glyph_metrics_nil;
+}
+
+void
+hb_font_funcs_set_kerning_func (hb_font_funcs_t *ffuncs,
+				hb_font_get_kerning_func_t kerning_func)
+{
+  if (ffuncs->immutable)
+    return;
+
+  ffuncs->v.get_kerning = kerning_func ? kerning_func : hb_font_get_kerning_nil;
+}
+
+
+hb_codepoint_t
+hb_font_get_glyph (hb_font_t *font, hb_face_t *face,
+		   hb_codepoint_t unicode, hb_codepoint_t variation_selector)
+{
+  return font->klass->v.get_glyph (font, face, font->user_data,
+				   unicode, variation_selector);
+}
+
+hb_bool_t
+hb_font_get_contour_point (hb_font_t *font, hb_face_t *face,
+			   unsigned int point_index,
+			   hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y)
+{
+  *x = 0; *y = 0;
+  return font->klass->v.get_contour_point (font, face, font->user_data,
+					   point_index,
+					   glyph, x, y);
+}
+
+void
+hb_font_get_glyph_metrics (hb_font_t *font, hb_face_t *face,
+			   hb_codepoint_t glyph, hb_glyph_metrics_t *metrics)
+{
+  memset (metrics, 0, sizeof (*metrics));
+  return font->klass->v.get_glyph_metrics (font, face, font->user_data,
+					   glyph, metrics);
+}
+
+hb_position_t
+hb_font_get_kerning (hb_font_t *font, hb_face_t *face,
+		     hb_codepoint_t first_glyph, hb_codepoint_t second_glyph)
+{
+  return font->klass->v.get_kerning (font, face, font->user_data,
+				     first_glyph, second_glyph);
+}
+
+
+/*
+ * hb_face_t
+ */
+
+static hb_face_t _hb_face_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+
+  NULL, /* get_table */
+  NULL, /* destroy */
+  NULL, /* user_data */
+
+  NULL, /* head_blob */
+  NULL, /* head_table */
+
+  NULL  /* ot_layout */
+};
+
+
+hb_face_t *
+hb_face_create_for_tables (hb_get_table_func_t  get_table,
+			   hb_destroy_func_t    destroy,
+			   void                *user_data)
+{
+  hb_face_t *face;
+
+  if (!HB_OBJECT_DO_CREATE (hb_face_t, face)) {
+    if (destroy)
+      destroy (user_data);
+    return &_hb_face_nil;
+  }
+
+  face->get_table = get_table;
+  face->destroy = destroy;
+  face->user_data = user_data;
+
+  face->ot_layout = _hb_ot_layout_new (face);
+
+  face->head_blob = Sanitizer<head>::sanitize (hb_face_get_table (face, HB_OT_TAG_head));
+  face->head_table = Sanitizer<head>::lock_instance (face->head_blob);
+
+  return face;
+}
+
+
+typedef struct _hb_face_for_data_closure_t {
+  hb_blob_t *blob;
+  unsigned int  index;
+} hb_face_for_data_closure_t;
+
+static hb_face_for_data_closure_t *
+_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index)
+{
+  hb_face_for_data_closure_t *closure;
+
+  closure = (hb_face_for_data_closure_t *) malloc (sizeof (hb_face_for_data_closure_t));
+  if (unlikely (!closure))
+    return NULL;
+
+  closure->blob = hb_blob_reference (blob);
+  closure->index = index;
+
+  return closure;
+}
+
+static void
+_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure)
+{
+  hb_blob_destroy (closure->blob);
+  free (closure);
+}
+
+static hb_blob_t *
+_hb_face_for_data_get_table (hb_tag_t tag, void *user_data)
+{
+  hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data;
+
+  const OpenTypeFontFile &ot_file = *Sanitizer<OpenTypeFontFile>::lock_instance (data->blob);
+  const OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
+
+  const OpenTypeTable &table = ot_face.get_table_by_tag (tag);
+
+  hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length);
+
+  hb_blob_unlock (data->blob);
+
+  return blob;
+}
+
+hb_face_t *
+hb_face_create_for_data (hb_blob_t    *blob,
+			 unsigned int  index)
+{
+  hb_blob_reference (blob);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (blob), index);
+  hb_blob_destroy (blob);
+
+  if (unlikely (!closure))
+    return &_hb_face_nil;
+
+  return hb_face_create_for_tables (_hb_face_for_data_get_table,
+				    (hb_destroy_func_t) _hb_face_for_data_closure_destroy,
+				    closure);
+}
+
+
+hb_face_t *
+hb_face_reference (hb_face_t *face)
+{
+  HB_OBJECT_DO_REFERENCE (face);
+}
+
+unsigned int
+hb_face_get_reference_count (hb_face_t *face)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (face);
+}
+
+void
+hb_face_destroy (hb_face_t *face)
+{
+  HB_OBJECT_DO_DESTROY (face);
+
+  _hb_ot_layout_free (face->ot_layout);
+
+  hb_blob_unlock (face->head_blob);
+  hb_blob_destroy (face->head_blob);
+
+  if (face->destroy)
+    face->destroy (face->user_data);
+
+  free (face);
+}
+
+hb_blob_t *
+hb_face_get_table (hb_face_t *face,
+		   hb_tag_t   tag)
+{
+  hb_blob_t *blob;
+
+  if (unlikely (!face || !face->get_table))
+    return &_hb_blob_nil;
+
+  blob = face->get_table (tag, face->user_data);
+
+  return blob;
+}
+
+
+/*
+ * hb_font_t
+ */
+
+static hb_font_t _hb_font_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+
+  0, /* x_scale */
+  0, /* y_scale */
+
+  0, /* x_ppem */
+  0, /* y_ppem */
+
+  NULL, /* klass */
+  NULL, /* destroy */
+  NULL  /* user_data */
+};
+
+hb_font_t *
+hb_font_create (void)
+{
+  hb_font_t *font;
+
+  if (!HB_OBJECT_DO_CREATE (hb_font_t, font))
+    return &_hb_font_nil;
+
+  font->klass = &_hb_font_funcs_nil;
+
+  return font;
+}
+
+hb_font_t *
+hb_font_reference (hb_font_t *font)
+{
+  HB_OBJECT_DO_REFERENCE (font);
+}
+
+unsigned int
+hb_font_get_reference_count (hb_font_t *font)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (font);
+}
+
+void
+hb_font_destroy (hb_font_t *font)
+{
+  HB_OBJECT_DO_DESTROY (font);
+
+  hb_font_funcs_destroy (font->klass);
+  if (font->destroy)
+    font->destroy (font->user_data);
+
+  free (font);
+}
+
+void
+hb_font_set_funcs (hb_font_t         *font,
+		   hb_font_funcs_t   *klass,
+		   hb_destroy_func_t  destroy,
+		   void              *user_data)
+{
+  if (HB_OBJECT_IS_INERT (font))
+    return;
+
+  if (font->destroy)
+    font->destroy (font->user_data);
+
+  if (!klass)
+    klass = &_hb_font_funcs_nil;
+
+  hb_font_funcs_reference (klass);
+  hb_font_funcs_destroy (font->klass);
+  font->klass = klass;
+  font->destroy = destroy;
+  font->user_data = user_data;
+}
+
+void
+hb_font_set_scale (hb_font_t *font,
+		   unsigned int x_scale,
+		   unsigned int y_scale)
+{
+  if (HB_OBJECT_IS_INERT (font))
+    return;
+
+  font->x_scale = x_scale;
+  font->y_scale = y_scale;
+}
+
+void
+hb_font_set_ppem (hb_font_t *font,
+		  unsigned int x_ppem,
+		  unsigned int y_ppem)
+{
+  if (HB_OBJECT_IS_INERT (font))
+    return;
+
+  font->x_ppem = x_ppem;
+  font->y_ppem = y_ppem;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FONT_H
+#define HB_FONT_H
+
+#include "hb-common.h"
+#include "hb-blob.h"
+
+HB_BEGIN_DECLS
+
+typedef struct _hb_face_t hb_face_t;
+typedef struct _hb_font_t hb_font_t;
+
+/*
+ * hb_face_t
+ */
+
+hb_face_t *
+hb_face_create_for_data (hb_blob_t    *blob,
+			 unsigned int  index);
+
+typedef hb_blob_t * (*hb_get_table_func_t)  (hb_tag_t tag, void *user_data);
+
+/* calls destroy() when not needing user_data anymore */
+hb_face_t *
+hb_face_create_for_tables (hb_get_table_func_t  get_table,
+			   hb_destroy_func_t    destroy,
+			   void                *user_data);
+
+hb_face_t *
+hb_face_reference (hb_face_t *face);
+
+unsigned int
+hb_face_get_reference_count (hb_face_t *face);
+
+void
+hb_face_destroy (hb_face_t *face);
+
+/* Returns NULL if not found */
+hb_blob_t *
+hb_face_get_table (hb_face_t *face,
+		   hb_tag_t   tag);
+
+
+/*
+ * hb_font_funcs_t
+ */
+
+typedef struct _hb_font_funcs_t hb_font_funcs_t;
+
+hb_font_funcs_t *
+hb_font_funcs_create (void);
+
+hb_font_funcs_t *
+hb_font_funcs_reference (hb_font_funcs_t *ffuncs);
+
+unsigned int
+hb_font_funcs_get_reference_count (hb_font_funcs_t *ffuncs);
+
+void
+hb_font_funcs_destroy (hb_font_funcs_t *ffuncs);
+
+hb_font_funcs_t *
+hb_font_funcs_copy (hb_font_funcs_t *ffuncs);
+
+void
+hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs);
+
+
+/* funcs */
+
+typedef struct _hb_glyph_metrics_t
+{
+    hb_position_t x_advance;
+    hb_position_t y_advance;
+    hb_position_t x_offset;
+    hb_position_t y_offset;
+    hb_position_t width;
+    hb_position_t height;
+} hb_glyph_metrics_t;
+
+typedef hb_codepoint_t (*hb_font_get_glyph_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+						    hb_codepoint_t unicode, hb_codepoint_t variation_selector);
+typedef hb_bool_t (*hb_font_get_contour_point_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+						       unsigned int point_index,
+						       hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y);
+typedef void (*hb_font_get_glyph_metrics_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+						  hb_codepoint_t glyph, hb_glyph_metrics_t *metrics);
+typedef hb_position_t (*hb_font_get_kerning_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+						     hb_codepoint_t first_glyph, hb_codepoint_t second_glyph);
+
+
+void
+hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
+			      hb_font_get_glyph_func_t glyph_func);
+
+void
+hb_font_funcs_set_contour_point_func (hb_font_funcs_t *ffuncs,
+				      hb_font_get_contour_point_func_t contour_point_func);
+
+void
+hb_font_funcs_set_glyph_metrics_func (hb_font_funcs_t *ffuncs,
+				      hb_font_get_glyph_metrics_func_t glyph_metrics_func);
+
+void
+hb_font_funcs_set_kerning_func (hb_font_funcs_t *ffuncs,
+				hb_font_get_kerning_func_t kerning_func);
+
+
+hb_codepoint_t
+hb_font_get_glyph (hb_font_t *font, hb_face_t *face,
+		   hb_codepoint_t unicode, hb_codepoint_t variation_selector);
+
+hb_bool_t
+hb_font_get_contour_point (hb_font_t *font, hb_face_t *face,
+			   unsigned int point_index,
+			   hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y);
+
+void
+hb_font_get_glyph_metrics (hb_font_t *font, hb_face_t *face,
+			   hb_codepoint_t glyph, hb_glyph_metrics_t *metrics);
+
+hb_position_t
+hb_font_get_kerning (hb_font_t *font, hb_face_t *face,
+		     hb_codepoint_t first_glyph, hb_codepoint_t second_glyph);
+
+
+/*
+ * hb_font_t
+ */
+
+/* Fonts are very light-weight objects */
+
+hb_font_t *
+hb_font_create (void);
+
+hb_font_t *
+hb_font_reference (hb_font_t *font);
+
+unsigned int
+hb_font_get_reference_count (hb_font_t *font);
+
+void
+hb_font_destroy (hb_font_t *font);
+
+void
+hb_font_set_funcs (hb_font_t         *font,
+		   hb_font_funcs_t   *klass,
+		   hb_destroy_func_t  destroy,
+		   void              *user_data);
+
+hb_font_funcs_t *
+hb_font_get_funcs (hb_font_t       *font);
+
+
+/*
+ * We should add support for full matrices.
+ */
+void
+hb_font_set_scale (hb_font_t *font,
+		   unsigned int x_scale,
+		   unsigned int y_scale);
+
+/*
+ * A zero value means "no hinting in that direction"
+ */
+void
+hb_font_set_ppem (hb_font_t *font,
+		  unsigned int x_ppem,
+		  unsigned int y_ppem);
+
+
+HB_END_DECLS
+
+#endif /* HB_FONT_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright (C) 2009  Keith Stribley <devel@thanlwinsoft.org>
+ *
+ *  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-ft.h"
+
+#include "hb-font-private.hh"
+
+#include FT_TRUETYPE_TABLES_H
+
+static hb_codepoint_t
+hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
+		 hb_face_t *face HB_UNUSED,
+		 const void *user_data,
+		 hb_codepoint_t unicode,
+		 hb_codepoint_t variation_selector)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+
+#ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX
+  if (unlikely (variation_selector)) {
+    hb_codepoint_t glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
+    if (glyph)
+      return glyph;
+  }
+#endif
+
+  return FT_Get_Char_Index (ft_face, unicode);
+}
+
+static hb_bool_t
+hb_ft_get_contour_point (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data,
+			 unsigned int point_index,
+			 hb_codepoint_t glyph,
+			 hb_position_t *x,
+			 hb_position_t *y)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  int load_flags = FT_LOAD_DEFAULT;
+
+  /* TODO: load_flags, embolden, etc */
+
+  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+      return FALSE;
+
+  if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
+      return FALSE;
+
+  if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
+      return FALSE;
+
+  *x = ft_face->glyph->outline.points[point_index].x;
+  *y = ft_face->glyph->outline.points[point_index].y;
+
+  return TRUE;
+}
+
+static void
+hb_ft_get_glyph_metrics (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data,
+			 hb_codepoint_t glyph,
+			 hb_glyph_metrics_t *metrics)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  int load_flags = FT_LOAD_DEFAULT;
+
+  /* TODO: load_flags, embolden, etc */
+
+  metrics->x_advance = metrics->y_advance = 0;
+  metrics->x_offset = metrics->y_offset = 0;
+  metrics->width = metrics->height = 0;
+  if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
+  {
+    /* TODO: A few negations should be in order here, not sure. */
+    metrics->x_advance = ft_face->glyph->advance.x;
+    metrics->y_advance = ft_face->glyph->advance.y;
+    metrics->x_offset = ft_face->glyph->metrics.horiBearingX;
+    metrics->y_offset = ft_face->glyph->metrics.horiBearingY;
+    metrics->width = ft_face->glyph->metrics.width;
+    metrics->height = ft_face->glyph->metrics.height;
+  }
+}
+
+static hb_position_t
+hb_ft_get_kerning (hb_font_t *font HB_UNUSED,
+		   hb_face_t *face HB_UNUSED,
+		   const void *user_data,
+		   hb_codepoint_t first_glyph,
+		   hb_codepoint_t second_glyph)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  FT_Vector kerning;
+
+  /* TODO: Kern type? */
+  if (FT_Get_Kerning (ft_face, first_glyph, second_glyph, FT_KERNING_DEFAULT, &kerning))
+      return 0;
+
+  return kerning.x;
+}
+
+static hb_font_funcs_t ft_ffuncs = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  TRUE, /* immutable */
+  {
+    hb_ft_get_glyph,
+    hb_ft_get_contour_point,
+    hb_ft_get_glyph_metrics,
+    hb_ft_get_kerning
+  }
+};
+
+hb_font_funcs_t *
+hb_ft_get_font_funcs (void)
+{
+  return &ft_ffuncs;
+}
+
+
+static hb_blob_t *
+_get_table  (hb_tag_t tag, void *user_data)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  FT_Byte *buffer;
+  FT_ULong  length = 0;
+  FT_Error error;
+
+  if (unlikely (tag == HB_TAG_NONE))
+    return NULL;
+
+  error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
+  if (error)
+    return NULL;
+
+  /* TODO Use FT_Memory? */
+  buffer = (FT_Byte *) malloc (length);
+  if (buffer == NULL)
+    return NULL;
+
+  error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
+  if (error)
+    return NULL;
+
+  return hb_blob_create ((const char *) buffer, length,
+			 HB_MEMORY_MODE_WRITABLE,
+			 free, buffer);
+}
+
+
+hb_face_t *
+hb_ft_face_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy)
+{
+  hb_face_t *face;
+
+  if (ft_face->stream->read == NULL) {
+    hb_blob_t *blob;
+
+    blob = hb_blob_create ((const char *) ft_face->stream->base,
+			   (unsigned int) ft_face->stream->size,
+			   /* TODO: Check FT_FACE_FLAG_EXTERNAL_STREAM? */
+			   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
+			   destroy, ft_face);
+    face = hb_face_create_for_data (blob, ft_face->face_index);
+    hb_blob_destroy (blob);
+  } else {
+    face = hb_face_create_for_tables (_get_table, destroy, ft_face);
+  }
+
+  return face;
+}
+
+static void
+hb_ft_face_finalize (FT_Face ft_face)
+{
+  hb_face_destroy ((hb_face_t *) ft_face->generic.data);
+}
+
+hb_face_t *
+hb_ft_face_create_cached (FT_Face ft_face)
+{
+  if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
+  {
+    if (ft_face->generic.finalizer)
+      ft_face->generic.finalizer (ft_face);
+
+    ft_face->generic.data = hb_ft_face_create (ft_face, NULL);
+    ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
+  }
+
+  return hb_face_reference ((hb_face_t *) ft_face->generic.data);
+}
+
+
+hb_font_t *
+hb_ft_font_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy)
+{
+  hb_font_t *font;
+
+  font = hb_font_create ();
+  hb_font_set_funcs (font,
+		     hb_ft_get_font_funcs (),
+		     destroy, ft_face);
+  hb_font_set_scale (font,
+		     ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM) >> 16,
+		     ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM) >> 16);
+  hb_font_set_ppem (font,
+		    ft_face->size->metrics.x_ppem,
+		    ft_face->size->metrics.y_ppem);
+
+  return font;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ft.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FT_H
+#define HB_FT_H
+
+#include "hb.h"
+
+#include "hb-font.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+HB_BEGIN_DECLS
+
+hb_font_funcs_t *
+hb_ft_get_font_funcs (void);
+
+hb_face_t *
+hb_ft_face_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy);
+
+/* Note: This function is not thread-safe */
+hb_face_t *
+hb_ft_face_create_cached (FT_Face ft_face);
+
+hb_font_t *
+hb_ft_font_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy);
+
+HB_END_DECLS
+
+#endif /* HB_FT_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-glib.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-glib.h"
+
+#include "hb-unicode-private.h"
+
+#include <glib.h>
+
+static hb_codepoint_t hb_glib_get_mirroring (hb_codepoint_t unicode) { g_unichar_get_mirror_char (unicode, &unicode); return unicode; }
+static hb_category_t hb_glib_get_general_category (hb_codepoint_t unicode) { return g_unichar_type (unicode); }
+static hb_script_t hb_glib_get_script (hb_codepoint_t unicode) { return g_unichar_get_script (unicode); }
+static unsigned int hb_glib_get_combining_class (hb_codepoint_t unicode) { return g_unichar_combining_class (unicode); }
+static unsigned int hb_glib_get_eastasian_width (hb_codepoint_t unicode) { return g_unichar_iswide (unicode); }
+
+
+static hb_unicode_funcs_t glib_ufuncs = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  TRUE, /* immutable */
+  {
+    hb_glib_get_general_category,
+    hb_glib_get_combining_class,
+    hb_glib_get_mirroring,
+    hb_glib_get_script,
+    hb_glib_get_eastasian_width
+  }
+};
+
+hb_unicode_funcs_t *
+hb_glib_get_unicode_funcs (void)
+{
+  return &glib_ufuncs;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-glib.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_GLIB_H
+#define HB_GLIB_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+hb_unicode_funcs_t *
+hb_glib_get_unicode_funcs (void);
+
+HB_END_DECLS
+
+#endif /* HB_GLIB_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-graphite.cc
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2009, Martin Hosken
+ * Copyright (C) 2009, SIL International
+ *
+ *  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.
+ */
+
+#include <graphite/GrClient.h>
+#include <graphite/ITextSource.h>
+#include <graphite/GrData.h>
+#include <graphite/GrConstants.h>
+#include <graphite/Segment.h>
+#include "hb-buffer-private.hh"
+#include "hb-font-private.hh"
+#include "hb-graphite.h"
+#include <map>
+
+namespace TtfUtil
+{
+extern int FontAscent(const void *pOS2);
+extern int FontDescent(const void *pOS2);
+extern int DesignUnits(const void *pHead);
+extern bool FontOs2Style(const void *pOS2, bool &fBold, bool &fItalic);
+}
+
+typedef struct _featureSetting {
+    unsigned int id;
+    int value;
+} featureSetting;
+
+class HbGrBufferTextSrc : public gr::ITextSource
+{
+public:
+  HbGrBufferTextSrc(hb_buffer_t *buff, hb_feature_t *feats, unsigned int num_features)
+  {
+    hb_feature_t *aFeat = feats;
+    featureSetting *aNewFeat;
+
+    buffer = hb_buffer_reference(buff);
+    features = new featureSetting[num_features];
+    nFeatures = num_features;
+    aNewFeat = features;
+    for (unsigned int i = 0; i < num_features; i++, aFeat++, aNewFeat++)
+    {
+        aNewFeat->id = aFeat->tag;
+        aNewFeat->value = aFeat->value;
+    }
+  };
+  ~HbGrBufferTextSrc() { hb_buffer_destroy(buffer); delete[] features; };
+  virtual gr::UtfType utfEncodingForm() { return gr::kutf32; };
+  virtual size_t getLength() { return buffer->len; };
+  virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf32 * prgchBuffer)
+  {
+    assert(cch <= buffer->len);
+    if (cch > buffer->len)
+      return 0;
+    for (unsigned int i = ichMin; i < ichMin + cch; i++)
+      prgchBuffer[i - ichMin] = buffer->info[i].codepoint;
+    return (cch - ichMin);
+  };
+  virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf16 * prgchBuffer) { return 0 ;};
+  virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf8 * prgchBuffer) { return 0; };
+  virtual bool getRightToLeft(gr::toffset ich)
+  { return hb_buffer_get_direction(buffer) == HB_DIRECTION_RTL; };
+  virtual unsigned int getDirectionDepth(gr::toffset ich)
+  { return hb_buffer_get_direction(buffer) == HB_DIRECTION_RTL ? 1 : 0; };
+  virtual float getVerticalOffset(gr::toffset ich) { return 0; };
+  virtual gr::isocode getLanguage(gr::toffset ich)
+  {
+    gr::isocode aLang;
+    char *p = (char *)(buffer->language);
+    int i;
+    for (i = 0; i < 4; i++)
+    {
+      if (p != NULL)
+        aLang.rgch[i] = *p;
+      else
+        aLang.rgch[i] = 0;
+      if (p && *p)
+        p++;
+    }
+    return aLang;
+  }
+
+  virtual std::pair<gr::toffset, gr::toffset> propertyRange(gr::toffset ich)
+  { return std::pair<gr::toffset, gr::toffset>(0, buffer->len); };
+  virtual size_t getFontFeatures(gr::toffset ich, gr::FeatureSetting * prgfset)
+  {
+    featureSetting *aFeat = features;
+    for (unsigned int i = 0; i < nFeatures; i++, aFeat++, prgfset++)
+    {
+      prgfset->id = aFeat->id;
+      prgfset->value = aFeat->value;
+    }
+    return nFeatures;
+  }
+  virtual bool sameSegment(gr::toffset ich1, gr::toffset ich2) {return true; };
+
+private:
+  hb_buffer_t   *buffer;
+  featureSetting   *features;
+  unsigned int nFeatures;
+};
+
+class HbGrFont : public gr::Font
+{
+public:
+  HbGrFont(hb_font_t *font, hb_face_t *face) : gr::Font()
+  { m_font = hb_font_reference(font); m_face = hb_face_reference(face); initfont(); };
+  ~HbGrFont()
+  {
+    std::map<hb_tag_t,hb_blob_t *>::iterator p = m_blobs.begin();
+    while (p != m_blobs.end())
+    { hb_blob_destroy((p++)->second); }
+    hb_font_destroy(m_font);
+    hb_face_destroy(m_face);
+  };
+  HbGrFont (const HbGrFont &font) : gr::Font(font)
+  {
+    *this = font;
+    m_blobs = std::map<hb_tag_t, hb_blob_t *>(font.m_blobs);
+    std::map<hb_tag_t,hb_blob_t *>::iterator p=m_blobs.begin();
+    while (p != m_blobs.end()) { hb_blob_reference((*p++).second); }
+    hb_font_reference(m_font);
+    hb_face_reference(m_face);
+  };
+  virtual HbGrFont *copyThis() { return new HbGrFont(*this); };
+  virtual bool bold() { return m_bold; };
+  virtual bool italic() { return m_italic; };
+  virtual float ascent() { float asc; getFontMetrics(&asc, NULL, NULL); return asc; };
+  virtual float descent() { float desc; getFontMetrics(NULL, &desc, NULL); return desc; };
+  virtual float height()
+  { float asc, desc; getFontMetrics(&asc, &desc, NULL); return (asc + desc); };
+  virtual unsigned int getDPIx() { return m_font->x_ppem; };
+  virtual unsigned int getDPIy() { return m_font->y_ppem; };
+  virtual const void *getTable(gr::fontTableId32 tableID, size_t *pcbsize)
+  {
+    hb_blob_t *blob;
+    std::map<hb_tag_t,hb_blob_t *>::iterator p=m_blobs.find((hb_tag_t)tableID);
+    if (p == m_blobs.end())
+    {
+      blob = hb_face_get_table(m_face, (hb_tag_t)tableID);
+      m_blobs[(hb_tag_t)tableID] = blob;
+    }
+    else
+    { blob = p->second; }
+
+    const char *res = hb_blob_lock(blob);
+    if (pcbsize)
+      *pcbsize = hb_blob_get_length(blob);
+    hb_blob_unlock(blob);
+    return (const void *)res;
+  }
+
+  virtual void getFontMetrics(float *pAscent, float *pDescent, float *pEmSquare)
+  {
+    if (pAscent) *pAscent = 1. * m_ascent * m_font->y_ppem / m_emsquare;
+    if (pDescent) *pDescent = 1. * m_descent * m_font->y_ppem / m_emsquare;
+    if (pEmSquare) *pEmSquare = m_font->x_scale;
+  }
+  virtual void getGlyphPoint(gr::gid16 glyphID, unsigned int pointNum, gr::Point &pointReturn)
+  {
+    hb_position_t x, y;
+    hb_font_get_contour_point(m_font, m_face, pointNum, glyphID, &x, &y);
+    pointReturn.x = (float)x;
+    pointReturn.y = (float)y;
+  }
+
+  virtual void getGlyphMetrics(gr::gid16 glyphID, gr::Rect &boundingBox, gr::Point &advances)
+  {
+    hb_glyph_metrics_t metrics;
+    hb_font_get_glyph_metrics(m_font, m_face, glyphID, &metrics);
+    boundingBox.top = (metrics.y_offset + metrics.height);
+    boundingBox.bottom = metrics.y_offset;
+    boundingBox.left = metrics.x_offset;
+    boundingBox.right = (metrics.x_offset + metrics.width);
+    advances.x = metrics.x_advance;
+    advances.y = metrics.y_advance;
+//    fprintf (stderr, "%d: (%d, %d, %d, %d)+(%d, %d)\n", glyphID, metrics.x_offset, metrics.y_offset, metrics.width, metrics.height, metrics.x_advance, metrics.y_advance);
+  }
+
+private:
+  HB_INTERNAL void initfont();
+
+  hb_font_t *m_font;
+  hb_face_t *m_face;
+  float m_ascent;
+  float m_descent;
+  float m_emsquare;
+  bool m_bold;
+  bool m_italic;
+  std::map<hb_tag_t, hb_blob_t *> m_blobs;
+};
+
+void HbGrFont::initfont()
+{
+  const void *pOS2 = getTable(gr::kttiOs2, NULL);
+  const void *pHead = getTable(gr::kttiHead, NULL);
+  TtfUtil::FontOs2Style(pOS2, m_bold, m_italic);
+  m_ascent = static_cast<float>(TtfUtil::FontAscent(pOS2));
+  m_descent = static_cast<float>(TtfUtil::FontDescent(pOS2));
+  m_emsquare = static_cast<float>(TtfUtil::DesignUnits(pHead));
+}
+
+void
+hb_graphite_shape (hb_font_t    *font,
+		   hb_face_t    *face,
+		   hb_buffer_t  *buffer,
+		   hb_feature_t *features,
+		   unsigned int  num_features)
+{
+  /* create text source */
+  HbGrBufferTextSrc   textSrc(buffer, features, num_features);
+
+  /* create grfont */
+  HbGrFont          grfont(font, face);
+
+  /* create segment */
+  int *firsts;
+  bool *flags;
+  int numChars;
+  int numGlyphs;
+  gr::LayoutEnvironment layout;
+  std::pair<gr::GlyphIterator, gr::GlyphIterator>glyph_range;
+  gr::GlyphIterator iGlyph;
+  hb_codepoint_t *glyph_infos, *pGlyph;
+  hb_glyph_position_t *pPosition;
+  int cGlyph = 0;
+  int cChar = 0;
+
+  layout.setStartOfLine(0);
+  layout.setEndOfLine(0);
+  layout.setDumbFallback(true);
+  layout.setJustifier(NULL);
+  layout.setRightToLeft(false);
+
+  gr::RangeSegment pSegment(&grfont, &textSrc, &layout, (gr::toffset)0,
+        static_cast<gr::toffset>(buffer->len), (gr::Segment *)NULL);
+
+  /* fill in buffer from segment */
+  _hb_buffer_clear_output(buffer);
+  pSegment.getUniscribeClusters(NULL, 0, &numChars, NULL, 0, &numGlyphs);
+  firsts = new int[numChars];
+  flags = new bool[numGlyphs];
+  glyph_infos = new hb_codepoint_t[numGlyphs];
+  hb_buffer_ensure(buffer, numGlyphs);
+  pSegment.getUniscribeClusters(firsts, numChars, NULL, flags, numGlyphs, NULL);
+  glyph_range = pSegment.glyphs();
+  for (pGlyph = glyph_infos, iGlyph = glyph_range.first; iGlyph != glyph_range.second;
+        iGlyph++, pGlyph++)
+  { *pGlyph = iGlyph->glyphID(); }
+
+  while (cGlyph < numGlyphs)
+  {
+    if (flags[cGlyph])
+    {
+        int oldcChar = cChar++;
+        int oldcGlyph = cGlyph++;
+        while (cChar < numChars && firsts[cChar] == firsts[oldcChar]) cChar++;
+        while (cGlyph < numGlyphs && !flags[cGlyph]) cGlyph++;
+        _hb_buffer_add_output_glyphs(buffer, cChar - oldcChar, cGlyph - oldcGlyph,
+                glyph_infos + oldcGlyph, 0xFFFF, 0xFFFF);
+    }
+    else
+    { cGlyph++; }   /* This should never happen */
+  }
+
+  float curradvx = 0., curradvy = 0.;
+  for (pPosition = hb_buffer_get_glyph_positions(buffer), iGlyph = glyph_range.first;
+        iGlyph != glyph_range.second; pPosition++, iGlyph++)
+  {
+    pPosition->x_offset = iGlyph->origin() - curradvx;
+    pPosition->y_offset = iGlyph->yOffset() - curradvy;
+    pPosition->x_advance = pPosition->x_offset + iGlyph->advanceWidth();
+    pPosition->y_advance = pPosition->y_offset + iGlyph->advanceHeight();
+    if (pPosition->x_advance < 0 && iGlyph->logicalIndex() != iGlyph->attachedClusterBase()->logicalIndex())
+        pPosition->x_advance = 0;
+    curradvx += pPosition->x_advance;
+    curradvy += pPosition->y_advance;
+//    fprintf(stderr, "%d@(%f, %f)+(%f, %f)\n", iGlyph->glyphID(), iGlyph->origin(), iGlyph->yOffset(), iGlyph->advanceWidth(), iGlyph->advanceHeight());
+  }
+
+  delete[] glyph_infos;
+  delete[] firsts;
+  delete[] flags;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-graphite.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009, Martin Hosken
+ * Copyright (C) 2009, SIL International
+ *
+ *  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_GRAPHITE_H
+#define HB_GRAPHITE_H
+
+#include "hb-shape.h"
+
+
+HB_BEGIN_DECLS
+
+#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f')
+
+void hb_graphite_shape (hb_font_t    *font,
+			hb_face_t    *face,
+			hb_buffer_t  *buffer,
+			hb_feature_t *features,
+			unsigned int  num_features);
+
+HB_END_DECLS
+
+#endif /* HB_GRAPHITE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-icu.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright (C) 2009  Keith Stribley <devel@thanlwinsoft.org>
+ *
+ *  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-icu.h"
+
+#include "hb-unicode-private.h"
+
+#include <unicode/uversion.h>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+
+static hb_codepoint_t hb_icu_get_mirroring (hb_codepoint_t unicode) { return u_charMirror(unicode); }
+static unsigned int hb_icu_get_combining_class (hb_codepoint_t unicode) { return u_getCombiningClass (unicode); }
+
+static unsigned int
+hb_icu_get_eastasian_width (hb_codepoint_t unicode)
+{
+  switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
+  {
+  case U_EA_WIDE:
+  case U_EA_FULLWIDTH:
+    return 2;
+  case U_EA_NEUTRAL:
+  case U_EA_AMBIGUOUS:
+  case U_EA_HALFWIDTH:
+  case U_EA_NARROW:
+    return 1;
+  }
+  return 1;
+}
+
+static hb_category_t
+hb_icu_get_general_category (hb_codepoint_t unicode)
+{
+  switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY))
+  {
+  case U_UNASSIGNED:			return HB_CATEGORY_UNASSIGNED;
+
+  case U_UPPERCASE_LETTER:		return HB_CATEGORY_UPPERCASE_LETTER;	/* Lu */
+  case U_LOWERCASE_LETTER:		return HB_CATEGORY_LOWERCASE_LETTER;	/* Ll */
+  case U_TITLECASE_LETTER:		return HB_CATEGORY_TITLECASE_LETTER;	/* Lt */
+  case U_MODIFIER_LETTER:		return HB_CATEGORY_MODIFIER_LETTER;	/* Lm */
+  case U_OTHER_LETTER:			return HB_CATEGORY_OTHER_LETTER;	/* Lo */
+
+  case U_NON_SPACING_MARK:		return HB_CATEGORY_NON_SPACING_MARK;	/* Mn */
+  case U_ENCLOSING_MARK:		return HB_CATEGORY_ENCLOSING_MARK;	/* Me */
+  case U_COMBINING_SPACING_MARK:	return HB_CATEGORY_COMBINING_MARK;	/* Mc */
+
+  case U_DECIMAL_DIGIT_NUMBER:		return HB_CATEGORY_DECIMAL_NUMBER;	/* Nd */
+  case U_LETTER_NUMBER:			return HB_CATEGORY_LETTER_NUMBER;	/* Nl */
+  case U_OTHER_NUMBER:			return HB_CATEGORY_OTHER_NUMBER;	/* No */
+
+  case U_SPACE_SEPARATOR:		return HB_CATEGORY_SPACE_SEPARATOR;	/* Zs */
+  case U_LINE_SEPARATOR:		return HB_CATEGORY_LINE_SEPARATOR;	/* Zl */
+  case U_PARAGRAPH_SEPARATOR:		return HB_CATEGORY_PARAGRAPH_SEPARATOR;	/* Zp */
+
+  case U_CONTROL_CHAR:			return HB_CATEGORY_CONTROL;		/* Cc */
+  case U_FORMAT_CHAR:			return HB_CATEGORY_FORMAT;		/* Cf */
+  case U_PRIVATE_USE_CHAR:		return HB_CATEGORY_PRIVATE_USE;		/* Co */
+  case U_SURROGATE:			return HB_CATEGORY_SURROGATE;		/* Cs */
+
+
+  case U_DASH_PUNCTUATION:		return HB_CATEGORY_DASH_PUNCTUATION;	/* Pd */
+  case U_START_PUNCTUATION:		return HB_CATEGORY_OPEN_PUNCTUATION;	/* Ps */
+  case U_END_PUNCTUATION:		return HB_CATEGORY_CLOSE_PUNCTUATION;	/* Pe */
+  case U_CONNECTOR_PUNCTUATION:		return HB_CATEGORY_CONNECT_PUNCTUATION;	/* Pc */
+  case U_OTHER_PUNCTUATION:		return HB_CATEGORY_OTHER_PUNCTUATION;	/* Po */
+
+  case U_MATH_SYMBOL:			return HB_CATEGORY_MATH_SYMBOL;		/* Sm */
+  case U_CURRENCY_SYMBOL:		return HB_CATEGORY_CURRENCY_SYMBOL;	/* Sc */
+  case U_MODIFIER_SYMBOL:		return HB_CATEGORY_MODIFIER_SYMBOL;	/* Sk */
+  case U_OTHER_SYMBOL:			return HB_CATEGORY_OTHER_SYMBOL;	/* So */
+
+  case U_INITIAL_PUNCTUATION:		return HB_CATEGORY_INITIAL_PUNCTUATION;	/* Pi */
+  case U_FINAL_PUNCTUATION:		return HB_CATEGORY_FINAL_PUNCTUATION;	/* Pf */
+  }
+
+  return HB_CATEGORY_UNASSIGNED;
+}
+
+static hb_script_t
+hb_icu_get_script (hb_codepoint_t unicode)
+{
+  UErrorCode status = U_ZERO_ERROR;
+  UScriptCode scriptCode = uscript_getScript(unicode, &status);
+  switch ((int) scriptCode)
+  {
+#define CHECK_ICU_VERSION(major, minor) \
+	U_ICU_VERSION_MAJOR_NUM > (major) || (U_ICU_VERSION_MAJOR_NUM == (major) && U_ICU_VERSION_MINOR_NUM >= (minor))
+#define MATCH_SCRIPT(C) case USCRIPT_##C: return HB_SCRIPT_##C
+#define MATCH_SCRIPT2(C1, C2) case USCRIPT_##C1: return HB_SCRIPT_##C2
+  MATCH_SCRIPT (INVALID_CODE);
+  MATCH_SCRIPT (COMMON);             /* Zyyy */
+  MATCH_SCRIPT (INHERITED);          /* Qaai */
+  MATCH_SCRIPT (ARABIC);             /* Arab */
+  MATCH_SCRIPT (ARMENIAN);           /* Armn */
+  MATCH_SCRIPT (BENGALI);            /* Beng */
+  MATCH_SCRIPT (BOPOMOFO);           /* Bopo */
+  MATCH_SCRIPT (CHEROKEE);           /* Cher */
+  MATCH_SCRIPT (COPTIC);             /* Qaac */
+  MATCH_SCRIPT (CYRILLIC);           /* Cyrl (Cyrs) */
+  MATCH_SCRIPT (DESERET);            /* Dsrt */
+  MATCH_SCRIPT (DEVANAGARI);         /* Deva */
+  MATCH_SCRIPT (ETHIOPIC);           /* Ethi */
+  MATCH_SCRIPT (GEORGIAN);           /* Geor (Geon); Geoa) */
+  MATCH_SCRIPT (GOTHIC);             /* Goth */
+  MATCH_SCRIPT (GREEK);              /* Grek */
+  MATCH_SCRIPT (GUJARATI);           /* Gujr */
+  MATCH_SCRIPT (GURMUKHI);           /* Guru */
+  MATCH_SCRIPT (HAN);                /* Hani */
+  MATCH_SCRIPT (HANGUL);             /* Hang */
+  MATCH_SCRIPT (HEBREW);             /* Hebr */
+  MATCH_SCRIPT (HIRAGANA);           /* Hira */
+  MATCH_SCRIPT (KANNADA);            /* Knda */
+  MATCH_SCRIPT (KATAKANA);           /* Kana */
+  MATCH_SCRIPT (KHMER);              /* Khmr */
+  MATCH_SCRIPT (LAO);                /* Laoo */
+  MATCH_SCRIPT (LATIN);              /* Latn (Latf); Latg) */
+  MATCH_SCRIPT (MALAYALAM);          /* Mlym */
+  MATCH_SCRIPT (MONGOLIAN);          /* Mong */
+  MATCH_SCRIPT (MYANMAR);            /* Mymr */
+  MATCH_SCRIPT (OGHAM);              /* Ogam */
+  MATCH_SCRIPT (OLD_ITALIC);         /* Ital */
+  MATCH_SCRIPT (ORIYA);              /* Orya */
+  MATCH_SCRIPT (RUNIC);              /* Runr */
+  MATCH_SCRIPT (SINHALA);            /* Sinh */
+  MATCH_SCRIPT (SYRIAC);             /* Syrc (Syrj, Syrn); Syre) */
+  MATCH_SCRIPT (TAMIL);              /* Taml */
+  MATCH_SCRIPT (TELUGU);             /* Telu */
+  MATCH_SCRIPT (THAANA);             /* Thaa */
+  MATCH_SCRIPT (THAI);               /* Thai */
+  MATCH_SCRIPT (TIBETAN);            /* Tibt */
+  MATCH_SCRIPT (CANADIAN_ABORIGINAL);/* Cans */
+  MATCH_SCRIPT (YI);                 /* Yiii */
+  MATCH_SCRIPT (TAGALOG);            /* Tglg */
+  MATCH_SCRIPT (HANUNOO);            /* Hano */
+  MATCH_SCRIPT (BUHID);              /* Buhd */
+  MATCH_SCRIPT (TAGBANWA);           /* Tagb */
+
+  /* Unicode-4.0 additions */
+  MATCH_SCRIPT (BRAILLE);            /* Brai */
+  MATCH_SCRIPT (CYPRIOT);            /* Cprt */
+  MATCH_SCRIPT (LIMBU);              /* Limb */
+  MATCH_SCRIPT (OSMANYA);            /* Osma */
+  MATCH_SCRIPT (SHAVIAN);            /* Shaw */
+  MATCH_SCRIPT (LINEAR_B);           /* Linb */
+  MATCH_SCRIPT (TAI_LE);             /* Tale */
+  MATCH_SCRIPT (UGARITIC);           /* Ugar */
+
+  /* Unicode-4.1 additions */
+  MATCH_SCRIPT (NEW_TAI_LUE);        /* Talu */
+  MATCH_SCRIPT (BUGINESE);           /* Bugi */
+  MATCH_SCRIPT (GLAGOLITIC);         /* Glag */
+  MATCH_SCRIPT (TIFINAGH);           /* Tfng */
+  MATCH_SCRIPT (SYLOTI_NAGRI);       /* Sylo */
+  MATCH_SCRIPT (OLD_PERSIAN);        /* Xpeo */
+  MATCH_SCRIPT (KHAROSHTHI);         /* Khar */
+
+  /* Unicode-5.0 additions */
+  MATCH_SCRIPT (UNKNOWN);            /* Zzzz */
+  MATCH_SCRIPT (BALINESE);           /* Bali */
+  MATCH_SCRIPT (CUNEIFORM);          /* Xsux */
+  MATCH_SCRIPT (PHOENICIAN);         /* Phnx */
+  MATCH_SCRIPT (PHAGS_PA);           /* Phag */
+  MATCH_SCRIPT (NKO);                /* Nkoo */
+
+  /* Unicode-5.1 additions */
+  MATCH_SCRIPT (KAYAH_LI);           /* Kali */
+  MATCH_SCRIPT (LEPCHA);             /* Lepc */
+  MATCH_SCRIPT (REJANG);             /* Rjng */
+  MATCH_SCRIPT (SUNDANESE);          /* Sund */
+  MATCH_SCRIPT (SAURASHTRA);         /* Saur */
+  MATCH_SCRIPT (CHAM);               /* Cham */
+  MATCH_SCRIPT (OL_CHIKI);           /* Olck */
+  MATCH_SCRIPT (VAI);                /* Vaii */
+  MATCH_SCRIPT (CARIAN);             /* Cari */
+  MATCH_SCRIPT (LYCIAN);             /* Lyci */
+  MATCH_SCRIPT (LYDIAN);             /* Lydi */
+
+  /* Unicode-5.2 additions */
+  MATCH_SCRIPT (AVESTAN);                /* Avst */
+#if CHECK_ICU_VERSION (4, 4)
+  MATCH_SCRIPT (BAMUM);                  /* Bamu */
+#endif
+  MATCH_SCRIPT (EGYPTIAN_HIEROGLYPHS);   /* Egyp */
+  MATCH_SCRIPT (IMPERIAL_ARAMAIC);       /* Armi */
+  MATCH_SCRIPT (INSCRIPTIONAL_PAHLAVI);  /* Phli */
+  MATCH_SCRIPT (INSCRIPTIONAL_PARTHIAN); /* Prti */
+  MATCH_SCRIPT (JAVANESE);               /* Java */
+  MATCH_SCRIPT (KAITHI);                 /* Kthi */
+  MATCH_SCRIPT2(LANNA, TAI_THAM);        /* Lana */
+#if CHECK_ICU_VERSION (4, 4)
+  MATCH_SCRIPT (LISU);                   /* Lisu */
+#endif
+  MATCH_SCRIPT (MEITEI_MAYEK);           /* Mtei */
+#if CHECK_ICU_VERSION (4, 4)
+  MATCH_SCRIPT (OLD_SOUTH_ARABIAN);      /* Sarb */
+#endif
+  MATCH_SCRIPT2(ORKHON, OLD_TURKIC);     /* Orkh */
+  MATCH_SCRIPT (SAMARITAN);              /* Samr */
+  MATCH_SCRIPT (TAI_VIET);               /* Tavt */
+  }
+  return HB_SCRIPT_UNKNOWN;
+}
+
+static hb_unicode_funcs_t icu_ufuncs = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  TRUE, /* immutable */
+  {
+    hb_icu_get_general_category,
+    hb_icu_get_combining_class,
+    hb_icu_get_mirroring,
+    hb_icu_get_script,
+    hb_icu_get_eastasian_width
+  }
+};
+
+hb_unicode_funcs_t *
+hb_icu_get_unicode_funcs (void)
+{
+  return &icu_ufuncs;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-icu.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ICU_H
+#define HB_ICU_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+hb_unicode_funcs_t *
+hb_icu_get_unicode_funcs (void);
+
+HB_END_DECLS
+
+#endif /* HB_ICU_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-language.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-language.h"
+
+static const char canon_map[256] = {
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
+  '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
+  '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
+   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
+};
+
+static hb_bool_t
+lang_equal (const void *v1,
+	    const void *v2)
+{
+  const unsigned char *p1 = v1;
+  const unsigned char *p2 = v2;
+
+  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
+    {
+      p1++, p2++;
+    }
+
+  return (canon_map[*p1] == canon_map[*p2]);
+}
+
+#if 0
+static unsigned int
+lang_hash (const void *key)
+{
+  const unsigned char *p = key;
+  unsigned int h = 0;
+  while (canon_map[*p])
+    {
+      h = (h << 5) - h + canon_map[*p];
+      p++;
+    }
+
+  return h;
+}
+#endif
+
+
+hb_language_t
+hb_language_from_string (const char *str)
+{
+  static unsigned int num_langs;
+  static unsigned int num_alloced;
+  static const char **langs;
+  unsigned int i;
+  unsigned char *p;
+
+  /* TODO Use a hash table or something */
+
+  if (!str)
+    return NULL;
+
+  for (i = 0; i < num_langs; i++)
+    if (lang_equal (str, langs[i]))
+      return langs[i];
+
+  if (unlikely (num_langs == num_alloced)) {
+    unsigned int new_alloced = 2 * (8 + num_alloced);
+    const char **new_langs = realloc (langs, new_alloced * sizeof (langs[0]));
+    if (!new_langs)
+      return NULL;
+    num_alloced = new_alloced;
+    langs = new_langs;
+  }
+
+  langs[i] = strdup (str);
+  for (p = (unsigned char *) langs[i]; *p; p++)
+    *p = canon_map[*p];
+
+  num_langs++;
+
+  return (hb_language_t) langs[i];
+}
+
+const char *
+hb_language_to_string (hb_language_t language)
+{
+  return (const char *) language;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-language.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_LANGUAGE_H
+#define HB_LANGUAGE_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+typedef const void *hb_language_t;
+
+hb_language_t
+hb_language_from_string (const char *str);
+
+const char *
+hb_language_to_string (hb_language_t language);
+
+HB_END_DECLS
+
+#endif /* HB_LANGUAGE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-object-private.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 Chris Wilson
+ * Copyright (C) 2009,2010  Red Hat, 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.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris@chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_REFCOUNT_PRIVATE_H
+#define HB_REFCOUNT_PRIVATE_H
+
+#include "hb-private.h"
+
+
+
+/* Encapsulate operations on the object's reference count */
+typedef struct {
+  hb_atomic_int_t ref_count;
+} hb_reference_count_t;
+
+#define hb_reference_count_inc(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, 1)
+#define hb_reference_count_dec(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, -1)
+
+#define HB_REFERENCE_COUNT_INIT(RC, VALUE) ((RC).ref_count = (VALUE))
+
+#define HB_REFERENCE_COUNT_GET_VALUE(RC) hb_atomic_int_get ((RC).ref_count)
+#define HB_REFERENCE_COUNT_SET_VALUE(RC, VALUE) hb_atomic_int_set ((RC).ref_count, (VALUE))
+
+#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
+#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+
+#define HB_REFERENCE_COUNT_IS_INVALID(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) == HB_REFERENCE_COUNT_INVALID_VALUE)
+
+#define HB_REFERENCE_COUNT_HAS_REFERENCE(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) > 0)
+
+
+
+/* Debug */
+
+#ifndef HB_DEBUG_OBJECT
+#define HB_DEBUG_OBJECT HB_DEBUG+0
+#endif
+
+static inline void
+_hb_trace_object (const void *obj,
+		  hb_reference_count_t *ref_count,
+		  const char *function)
+{
+  if (HB_DEBUG_OBJECT)
+    fprintf (stderr, "OBJECT(%p) refcount=%d %s\n",
+	     obj,
+	     HB_REFERENCE_COUNT_GET_VALUE (*ref_count),
+	     function);
+}
+
+#define TRACE_OBJECT(obj) _hb_trace_object (obj, &obj->ref_count, __FUNCTION__)
+
+
+
+/* Object allocation and lifecycle manamgement macros */
+
+#define HB_OBJECT_IS_INERT(obj) \
+    (unlikely (HB_REFERENCE_COUNT_IS_INVALID ((obj)->ref_count)))
+
+#define HB_OBJECT_DO_INIT_EXPR(obj) \
+    HB_REFERENCE_COUNT_INIT (obj->ref_count, 1)
+
+#define HB_OBJECT_DO_INIT(obj) \
+  HB_STMT_START { \
+    HB_OBJECT_DO_INIT_EXPR (obj); \
+  } HB_STMT_END
+
+#define HB_OBJECT_DO_CREATE(Type, obj) \
+  likely (( \
+	       (void) ( \
+		 ((obj) = (Type *) calloc (1, sizeof (Type))) && \
+		 ( \
+		  HB_OBJECT_DO_INIT_EXPR (obj), \
+		  TRACE_OBJECT (obj), \
+		  TRUE \
+		 ) \
+	       ), \
+	       (obj) \
+	     ))
+
+#define HB_OBJECT_DO_REFERENCE(obj) \
+  HB_STMT_START { \
+    int old_count; \
+    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
+      return obj; \
+    TRACE_OBJECT (obj); \
+    old_count = hb_reference_count_inc (obj->ref_count); \
+    assert (old_count > 0); \
+    return obj; \
+  } HB_STMT_END
+
+#define HB_OBJECT_DO_GET_REFERENCE_COUNT(obj) \
+  HB_STMT_START { \
+    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
+      return 0; \
+    return HB_REFERENCE_COUNT_GET_VALUE (obj->ref_count); \
+  } HB_STMT_END
+
+#define HB_OBJECT_DO_DESTROY(obj) \
+  HB_STMT_START { \
+    int old_count; \
+    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
+      return; \
+    TRACE_OBJECT (obj); \
+    old_count = hb_reference_count_dec (obj->ref_count); \
+    assert (old_count > 0); \
+    if (old_count != 1) \
+      return; \
+  } HB_STMT_END
+
+
+
+#endif /* HB_REFCOUNT_PRIVATE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2007,2008,2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OPEN_FILE_PRIVATE_HH
+#define HB_OPEN_FILE_PRIVATE_HH
+
+#include "hb-open-type-private.hh"
+
+
+/*
+ *
+ * The OpenType Font File
+ *
+ */
+
+
+/*
+ * Organization of an OpenType Font
+ */
+
+struct OpenTypeFontFile;
+struct OffsetTable;
+struct TTCHeader;
+
+
+typedef struct TableDirectory
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  Tag		tag;		/* 4-byte identifier. */
+  CheckSum	checkSum;	/* CheckSum for this table. */
+  ULONG		offset;		/* Offset from beginning of TrueType font
+				 * file. */
+  ULONG		length;		/* Length of this table. */
+  public:
+  DEFINE_SIZE_STATIC (16);
+} OpenTypeTable;
+
+typedef struct OffsetTable
+{
+  friend struct OpenTypeFontFile;
+
+  inline unsigned int get_table_count (void) const
+  { return numTables; }
+  inline const TableDirectory& get_table (unsigned int i) const
+  {
+    if (unlikely (i >= numTables)) return Null(TableDirectory);
+    return tableDir[i];
+  }
+  inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
+  {
+    Tag t;
+    t.set (tag);
+    /* TODO: bsearch (need to sort in sanitize) */
+    unsigned int count = numTables;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (t == tableDir[i].tag)
+      {
+        if (table_index) *table_index = i;
+        return true;
+      }
+    }
+    if (table_index) *table_index = Index::NOT_FOUND_INDEX;
+    return false;
+  }
+  inline const TableDirectory& get_table_by_tag (hb_tag_t tag) const
+  {
+    unsigned int table_index;
+    find_table_index (tag, &table_index);
+    return get_table (table_index);
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& c->check_array (tableDir, TableDirectory::static_size, numTables);
+  }
+
+  private:
+  Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
+  USHORT	numTables;	/* Number of tables. */
+  USHORT	searchRange;	/* (Maximum power of 2 <= numTables) x 16 */
+  USHORT	entrySelector;	/* Log2(maximum power of 2 <= numTables). */
+  USHORT	rangeShift;	/* NumTables x 16-searchRange. */
+  TableDirectory tableDir[VAR];	/* TableDirectory entries. numTables items */
+  public:
+  DEFINE_SIZE_ARRAY (12, tableDir);
+} OpenTypeFontFace;
+
+
+/*
+ * TrueType Collections
+ */
+
+struct TTCHeaderVersion1
+{
+  friend struct TTCHeader;
+
+  inline unsigned int get_face_count (void) const { return table.len; }
+  inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return table.sanitize (c, this);
+  }
+
+  private:
+  Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
+  FixedVersion	version;	/* Version of the TTC Header (1.0),
+				 * 0x00010000 */
+  LongOffsetLongArrayOf<OffsetTable>
+		table;		/* Array of offsets to the OffsetTable for each font
+				 * from the beginning of the file */
+  public:
+  DEFINE_SIZE_ARRAY (12, table);
+};
+
+struct TTCHeader
+{
+  friend struct OpenTypeFontFile;
+
+  private:
+
+  inline unsigned int get_face_count (void) const
+  {
+    switch (u.header.version) {
+    case 2: /* version 2 is compatible with version 1 */
+    case 1: return u.version1.get_face_count ();
+    default:return 0;
+    }
+  }
+  inline const OpenTypeFontFace& get_face (unsigned int i) const
+  {
+    switch (u.header.version) {
+    case 2: /* version 2 is compatible with version 1 */
+    case 1: return u.version1.get_face (i);
+    default:return Null(OpenTypeFontFace);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!u.header.version.sanitize (c))) return false;
+    switch (u.header.version) {
+    case 2: /* version 2 is compatible with version 1 */
+    case 1: return u.version1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  struct {
+  Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
+  FixedVersion	version;	/* Version of the TTC Header (1.0 or 2.0),
+				 * 0x00010000 or 0x00020000 */
+  }			header;
+  TTCHeaderVersion1	version1;
+  } u;
+};
+
+
+/*
+ * OpenType Font File
+ */
+
+struct OpenTypeFontFile
+{
+  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 */
+  static const hb_tag_t TrueTag		= HB_TAG ('t','r','u','e'); /* Obsolete Apple TrueType */
+  static const hb_tag_t Typ1Tag		= HB_TAG ('t','y','p','1'); /* Obsolete Apple Type1 font in SFNT container */
+
+  inline hb_tag_t get_tag (void) const { return u.tag; }
+
+  inline unsigned int get_face_count (void) const
+  {
+    switch (u.tag) {
+    case CFFTag:	/* All the non-collection tags */
+    case TrueTag:
+    case Typ1Tag:
+    case TrueTypeTag:	return 1;
+    case TTCTag:	return u.ttcHeader.get_face_count ();
+    default:		return 0;
+    }
+  }
+  inline const OpenTypeFontFace& get_face (unsigned int i) const
+  {
+    switch (u.tag) {
+    /* Note: for non-collection SFNT data we ignore index.  This is because
+     * Apple dfont container is a container of SFNT's.  So each SFNT is a
+     * non-TTC, but the index is more than zero. */
+    case CFFTag:	/* All the non-collection tags */
+    case TrueTag:
+    case Typ1Tag:
+    case TrueTypeTag:	return u.fontFace;
+    case TTCTag:	return u.ttcHeader.get_face (i);
+    default:		return Null(OpenTypeFontFace);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!u.tag.sanitize (c))) return false;
+    switch (u.tag) {
+    case CFFTag:	/* All the non-collection tags */
+    case TrueTag:
+    case Typ1Tag:
+    case TrueTypeTag:	return u.fontFace.sanitize (c);
+    case TTCTag:	return u.ttcHeader.sanitize (c);
+    default:		return true;
+    }
+  }
+
+  private:
+  union {
+  Tag			tag;		/* 4-byte identifier. */
+  OpenTypeFontFace	fontFace;
+  TTCHeader		ttcHeader;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (4, tag);
+};
+
+
+#endif /* HB_OPEN_FILE_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OPEN_TYPES_PRIVATE_HH
+#define HB_OPEN_TYPES_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-blob.h"
+
+
+
+/*
+ * Casts
+ */
+
+/* Cast to struct T, reference to reference */
+template<typename Type, typename TObject>
+inline const Type& CastR(const TObject &X)
+{ return reinterpret_cast<const Type&> (X); }
+template<typename Type, typename TObject>
+inline Type& CastR(TObject &X)
+{ return reinterpret_cast<Type&> (X); }
+
+/* Cast to struct T, pointer to pointer */
+template<typename Type, typename TObject>
+inline const Type* CastP(const TObject *X)
+{ return reinterpret_cast<const Type*> (X); }
+template<typename Type, typename TObject>
+inline Type* CastP(TObject *X)
+{ return reinterpret_cast<Type*> (X); }
+
+/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory
+ * location pointed to by P plus Ofs bytes. */
+template<typename Type>
+inline const Type& StructAtOffset(const void *P, unsigned int offset)
+{ return * reinterpret_cast<const Type*> ((const char *) P + offset); }
+template<typename Type>
+inline Type& StructAtOffset(void *P, unsigned int offset)
+{ return * reinterpret_cast<Type*> ((char *) P + offset); }
+
+/* StructAfter<T>(X) returns the struct T& that is placed after X.
+ * Works with X of variable size also.  X must implement get_size() */
+template<typename Type, typename TObject>
+inline const Type& StructAfter(const TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+template<typename Type, typename TObject>
+inline Type& StructAfter(TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+
+
+
+/*
+ * Size checking
+ */
+
+/* Check _assertion in a method environment */
+#define _DEFINE_SIZE_ASSERTION(_assertion) \
+  inline void _size_assertion (void) const \
+  { ASSERT_STATIC (_assertion); }
+/* Check that _code compiles in a method environment */
+#define _DEFINE_COMPILES_ASSERTION(_code) \
+  inline void _compiles_assertion (void) const \
+  { _code; }
+
+
+#define DEFINE_SIZE_STATIC(size) \
+  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size)); \
+  static const unsigned int static_size = (size); \
+  static const unsigned int min_size = (size)
+
+/* Size signifying variable-sized array */
+#define VAR 1
+
+#define DEFINE_SIZE_UNION(size, _member) \
+  _DEFINE_SIZE_ASSERTION (this->u._member.static_size == (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_MIN(size) \
+  _DEFINE_SIZE_ASSERTION (sizeof (*this) >= (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY(size, array) \
+  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
+  _DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
+  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
+  _DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
+  static const unsigned int min_size = (size)
+
+
+
+/*
+ * Null objects
+ */
+
+/* Global nul-content Null pool.  Enlarge as necessary. */
+static const void *_NullPool[64 / sizeof (void *)];
+
+/* Generic nul-content Null objects. */
+template <typename Type>
+static inline const Type& Null () {
+  ASSERT_STATIC (Type::min_size <= sizeof (_NullPool));
+  return *CastP<Type> (_NullPool);
+}
+
+/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
+#define DEFINE_NULL_DATA(Type, data) \
+static const char _Null##Type[Type::min_size + 1] = data; /* +1 is for nul-termination in data */ \
+template <> \
+inline const Type& Null<Type> () { \
+  return *CastP<Type> (_Null##Type); \
+} /* The following line really exists such that we end in a place needing semicolon */ \
+ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
+
+/* Accessor macro. */
+#define Null(Type) Null<Type>()
+
+
+/*
+ * Trace
+ */
+
+
+template <int max_depth>
+struct hb_trace_t {
+  explicit hb_trace_t (unsigned int *pdepth, const char *what, const char *function, const void *obj) : pdepth(pdepth) {
+    if (*pdepth < max_depth)
+      fprintf (stderr, "%s(%p) %-*d-> %s\n", what, obj, *pdepth, *pdepth, function);
+    if (max_depth) ++*pdepth;
+  }
+  ~hb_trace_t (void) { if (max_depth) --*pdepth; }
+
+  private:
+  unsigned int *pdepth;
+};
+template <> /* Optimize when tracing is disabled */
+struct hb_trace_t<0> {
+  explicit hb_trace_t (unsigned int *pdepth HB_UNUSED, const char *what HB_UNUSED, const char *function HB_UNUSED, const void *obj HB_UNUSED) {}
+};
+
+
+
+/*
+ * Sanitize
+ */
+
+#ifndef HB_DEBUG_SANITIZE
+#define HB_DEBUG_SANITIZE HB_DEBUG+0
+#endif
+
+
+#define TRACE_SANITIZE() \
+	hb_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", HB_FUNC, this); \
+
+
+struct hb_sanitize_context_t
+{
+  inline void init (hb_blob_t *blob)
+  {
+    this->blob = hb_blob_reference (blob);
+    this->start = hb_blob_lock (blob);
+    this->end = this->start + hb_blob_get_length (blob);
+    this->writable = hb_blob_is_writable (blob);
+    this->edit_count = 0;
+    this->debug_depth = 0;
+
+    if (HB_DEBUG_SANITIZE)
+      fprintf (stderr, "sanitize %p init [%p..%p] (%u bytes)\n",
+	       this->blob, this->start, this->end, this->end - this->start);
+  }
+
+  inline void finish (void)
+  {
+    if (HB_DEBUG_SANITIZE)
+      fprintf (stderr, "sanitize %p fini [%p..%p] %u edit requests\n",
+	       this->blob, this->start, this->end, this->edit_count);
+
+    hb_blob_unlock (this->blob);
+    hb_blob_destroy (this->blob);
+    this->blob = NULL;
+    this->start = this->end = NULL;
+  }
+
+  inline bool check_range (const void *base, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool ret = this->start <= p &&
+	       p <= this->end &&
+	       (unsigned int) (this->end - p) >= len;
+
+    if (HB_DEBUG_SANITIZE && (int) this->debug_depth < (int) HB_DEBUG_SANITIZE) \
+      fprintf (stderr, "SANITIZE(%p) %-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s\n", \
+	       p,
+	       this->debug_depth, this->debug_depth,
+	       p, p + len, len,
+	       this->start, this->end,
+	       ret ? "pass" : "FAIL");
+
+    return likely (ret);
+  }
+
+  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool overflows = len >= ((unsigned int) -1) / record_size;
+
+    if (HB_DEBUG_SANITIZE && (int) this->debug_depth < (int) HB_DEBUG_SANITIZE)
+      fprintf (stderr, "SANITIZE(%p) %-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s\n", \
+	       p,
+	       this->debug_depth, this->debug_depth,
+	       p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
+	       this->start, this->end,
+	       !overflows ? "does not overflow" : "OVERFLOWS FAIL");
+
+    return likely (!overflows && this->check_range (base, record_size * len));
+  }
+
+  template <typename Type>
+  inline bool check_struct (const Type *obj) const
+  {
+    return likely (this->check_range (obj, obj->min_size));
+  }
+
+  inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
+  {
+    const char *p = (const char *) base;
+    this->edit_count++;
+
+    if (HB_DEBUG_SANITIZE && (int) this->debug_depth < (int) HB_DEBUG_SANITIZE)
+      fprintf (stderr, "SANITIZE(%p) %-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s\n", \
+	       p,
+	       this->debug_depth, this->debug_depth,
+	       this->edit_count,
+	       p, p + len, len,
+	       this->start, this->end,
+	       this->writable ? "granted" : "REJECTED");
+
+    return this->writable;
+  }
+
+  unsigned int debug_depth;
+  const char *start, *end;
+  bool writable;
+  unsigned int edit_count;
+  hb_blob_t *blob;
+};
+
+
+
+/* Template to sanitize an object. */
+template <typename Type>
+struct Sanitizer
+{
+  static hb_blob_t *sanitize (hb_blob_t *blob) {
+    hb_sanitize_context_t c[1] = {{0}};
+    bool sane;
+
+    /* TODO is_sane() stuff */
+
+    if (!blob)
+      return hb_blob_create_empty ();
+
+  retry:
+    if (HB_DEBUG_SANITIZE)
+      fprintf (stderr, "Sanitizer %p start %s\n", blob, HB_FUNC);
+
+    c->init (blob);
+
+    if (unlikely (!c->start)) {
+      c->finish ();
+      return blob;
+    }
+
+    Type *t = CastP<Type> (const_cast<char *> (c->start));
+
+    sane = t->sanitize (c);
+    if (sane) {
+      if (c->edit_count) {
+	if (HB_DEBUG_SANITIZE)
+	  fprintf (stderr, "Sanitizer %p passed first round with %d edits; doing a second round %s\n",
+		   blob, c->edit_count, HB_FUNC);
+
+        /* sanitize again to ensure no toe-stepping */
+        c->edit_count = 0;
+	sane = t->sanitize (c);
+	if (c->edit_count) {
+	  if (HB_DEBUG_SANITIZE)
+	    fprintf (stderr, "Sanitizer %p requested %d edits in second round; FAILLING %s\n",
+		     blob, c->edit_count, HB_FUNC);
+	  sane = false;
+	}
+      }
+      c->finish ();
+    } else {
+      unsigned int edit_count = c->edit_count;
+      c->finish ();
+      if (edit_count && !hb_blob_is_writable (blob) && hb_blob_try_writable (blob)) {
+        /* ok, we made it writable by relocating.  try again */
+	if (HB_DEBUG_SANITIZE)
+	  fprintf (stderr, "Sanitizer %p retry %s\n", blob, HB_FUNC);
+        goto retry;
+      }
+    }
+
+    if (HB_DEBUG_SANITIZE)
+      fprintf (stderr, "Sanitizer %p %s %s\n", blob, sane ? "passed" : "FAILED", HB_FUNC);
+    if (sane)
+      return blob;
+    else {
+      hb_blob_destroy (blob);
+      return hb_blob_create_empty ();
+    }
+  }
+
+  static const Type* lock_instance (hb_blob_t *blob) {
+    const char *base = hb_blob_lock (blob);
+    return unlikely (!base) ? &Null(Type) : CastP<Type> (base);
+  }
+};
+
+
+
+
+/*
+ *
+ * The OpenType Font File: Data Types
+ */
+
+
+/* "The following data types are used in the OpenType font file.
+ *  All OpenType fonts use Motorola-style byte ordering (Big Endian):" */
+
+/*
+ * Int types
+ */
+
+
+template <typename Type, int Bytes> class BEInt;
+
+/* LONGTERMTODO: On machines allowing unaligned access, we can make the
+ * following tighter by using byteswap instructions on ints directly. */
+template <typename Type>
+class BEInt<Type, 2>
+{
+  public:
+  inline class BEInt<Type,2>& operator = (Type i) { hb_be_uint16_put (v,i); return *this; }
+  inline operator Type () const { return hb_be_uint16_get (v); }
+  inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_cmp (v, o.v); }
+  inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); }
+  private: uint8_t v[2];
+};
+template <typename Type>
+class BEInt<Type, 4>
+{
+  public:
+  inline class BEInt<Type,4>& operator = (Type i) { hb_be_uint32_put (v,i); return *this; }
+  inline operator Type () const { return hb_be_uint32_get (v); }
+  inline bool operator == (const BEInt<Type, 4>& o) const { return hb_be_uint32_cmp (v, o.v); }
+  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>
+struct IntType
+{
+  inline void set (Type i) { v = i; }
+  inline operator Type(void) const { return v; }
+  inline bool operator == (const IntType<Type> &o) const { return v == o.v; }
+  inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return likely (c->check_struct (this));
+  }
+  protected:
+  BEInt<Type, sizeof (Type)> v;
+  public:
+  DEFINE_SIZE_STATIC (sizeof (Type));
+};
+
+typedef IntType<uint16_t> USHORT;	/* 16-bit unsigned integer. */
+typedef IntType<int16_t>  SHORT;	/* 16-bit signed integer. */
+typedef IntType<uint32_t> ULONG;	/* 32-bit unsigned integer. */
+typedef IntType<int32_t>  LONG;		/* 32-bit signed integer. */
+
+/* Date represented in number of seconds since 12:00 midnight, January 1,
+ * 1904. The value is represented as a signed 64-bit integer. */
+struct LONGDATETIME
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return likely (c->check_struct (this));
+  }
+  private:
+  LONG major;
+  ULONG minor;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+/* Array of four uint8s (length = 32 bits) used to identify a script, language
+ * system, feature, or baseline */
+struct Tag : ULONG
+{
+  /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
+  inline operator const char* (void) const { return reinterpret_cast<const char *> (&this->v); }
+  inline operator char* (void) { return reinterpret_cast<char *> (&this->v); }
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+DEFINE_NULL_DATA (Tag, "    ");
+
+/* Glyph index number, same as uint16 (length = 16 bits) */
+typedef USHORT GlyphID;
+
+/* Script/language-system/feature index */
+struct Index : USHORT {
+  static const unsigned int NOT_FOUND_INDEX = 0xFFFF;
+};
+DEFINE_NULL_DATA (Index, "\xff\xff");
+
+/* Offset to a table, same as uint16 (length = 16 bits), Null offset = 0x0000 */
+typedef USHORT Offset;
+
+/* LongOffset to a table, same as uint32 (length = 32 bits), Null offset = 0x00000000 */
+typedef ULONG LongOffset;
+
+
+/* CheckSum */
+struct CheckSum : ULONG
+{
+  static uint32_t CalcTableChecksum (ULONG *Table, uint32_t Length)
+  {
+    uint32_t Sum = 0L;
+    ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size;
+
+    while (Table < EndPtr)
+      Sum += *Table++;
+    return Sum;
+  }
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+
+/*
+ * Version Numbers
+ */
+
+struct FixedVersion
+{
+  inline operator uint32_t (void) const { return (major << 16) + minor; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  USHORT major;
+  USHORT minor;
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+
+
+/*
+ * Template subclasses of Offset and LongOffset that do the dereferencing.
+ * Use: (base+offset)
+ */
+
+template <typename OffsetType, typename Type>
+struct GenericOffsetTo : OffsetType
+{
+  inline const Type& operator () (const void *base) const
+  {
+    unsigned int offset = *this;
+    if (unlikely (!offset)) return Null(Type);
+    return StructAtOffset<Type> (base, offset);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    if (unlikely (!c->check_struct (this))) return false;
+    unsigned int offset = *this;
+    if (unlikely (!offset)) return true;
+    Type &obj = StructAtOffset<Type> (base, offset);
+    return likely (obj.sanitize (c)) || neuter (c);
+  }
+  template <typename T>
+  inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
+    TRACE_SANITIZE ();
+    if (unlikely (!c->check_struct (this))) return false;
+    unsigned int offset = *this;
+    if (unlikely (!offset)) return true;
+    Type &obj = StructAtOffset<Type> (base, offset);
+    return likely (obj.sanitize (c, user_data)) || neuter (c);
+  }
+
+  private:
+  /* Set the offset to Null */
+  inline bool neuter (hb_sanitize_context_t *c) {
+    if (c->can_edit (this, this->static_size)) {
+      this->set (0); /* 0 is Null offset */
+      return true;
+    }
+    return false;
+  }
+};
+template <typename Base, typename OffsetType, typename Type>
+inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); }
+
+template <typename Type>
+struct OffsetTo : GenericOffsetTo<Offset, Type> {};
+
+template <typename Type>
+struct LongOffsetTo : GenericOffsetTo<LongOffset, Type> {};
+
+
+/*
+ * Array Types
+ */
+
+template <typename LenType, typename Type>
+struct GenericArrayOf
+{
+  const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
+  {
+    unsigned int count = len;
+    if (unlikely (start_offset > count))
+      count = 0;
+    else
+      count -= start_offset;
+    count = MIN (count, *pcount);
+    *pcount = count;
+    return array + start_offset;
+  }
+
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len)) return Null(Type);
+    return array[i];
+  }
+  inline unsigned int get_size () const
+  { return len.static_size + len * Type::static_size; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!sanitize_shallow (c))) return false;
+    /* Note: for structs that do not reference other structs,
+     * we do not need to call their sanitize() as we already did
+     * a bound check on the aggregate array size, hence the return.
+     */
+    return true;
+    /* We do keep this code though to make sure the structs pointed
+     * to do have a simple sanitize(), ie. they do not reference
+     * other structs. */
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      if (array[i].sanitize (c))
+        return false;
+    return true;
+  }
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    if (unlikely (!sanitize_shallow (c))) return false;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!array[i].sanitize (c, base)))
+        return false;
+    return true;
+  }
+  template <typename T>
+  inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
+    TRACE_SANITIZE ();
+    if (unlikely (!sanitize_shallow (c))) return false;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!array[i].sanitize (c, base, user_data)))
+        return false;
+    return true;
+  }
+
+  private:
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& c->check_array (this, Type::static_size, len);
+  }
+
+  public:
+  LenType len;
+  Type array[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (sizeof (LenType), array);
+};
+
+/* An array with a USHORT number of elements. */
+template <typename Type>
+struct ArrayOf : GenericArrayOf<USHORT, Type> {};
+
+/* An array with a ULONG number of elements. */
+template <typename Type>
+struct LongArrayOf : GenericArrayOf<ULONG, Type> {};
+
+/* Array of Offset's */
+template <typename Type>
+struct OffsetArrayOf : ArrayOf<OffsetTo<Type> > {};
+
+/* Array of LongOffset's */
+template <typename Type>
+struct LongOffsetArrayOf : ArrayOf<LongOffsetTo<Type> > {};
+
+/* LongArray of LongOffset's */
+template <typename Type>
+struct LongOffsetLongArrayOf : LongArrayOf<LongOffsetTo<Type> > {};
+
+/* Array of offsets relative to the beginning of the array itself. */
+template <typename Type>
+struct OffsetListOf : OffsetArrayOf<Type>
+{
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= this->len)) return Null(Type);
+    return this+this->array[i];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return OffsetArrayOf<Type>::sanitize (c, this);
+  }
+  template <typename T>
+  inline bool sanitize (hb_sanitize_context_t *c, T user_data) {
+    TRACE_SANITIZE ();
+    return OffsetArrayOf<Type>::sanitize (c, this, user_data);
+  }
+};
+
+
+/* An array with a USHORT number of elements,
+ * starting at second element. */
+template <typename Type>
+struct HeadlessArrayOf
+{
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len || !i)) return Null(Type);
+    return array[i-1];
+  }
+  inline unsigned int get_size () const
+  { return len.static_size + (len ? len - 1 : 0) * Type::static_size; }
+
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) {
+    return c->check_struct (this)
+	&& c->check_array (this, Type::static_size, len);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!sanitize_shallow (c))) return false;
+    /* Note: for structs that do not reference other structs,
+     * we do not need to call their sanitize() as we already did
+     * a bound check on the aggregate array size, hence the return.
+     */
+    return true;
+    /* We do keep this code though to make sure the structs pointed
+     * to do have a simple sanitize(), ie. they do not reference
+     * other structs. */
+    unsigned int count = len ? len - 1 : 0;
+    Type *a = array;
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!a[i].sanitize (c)))
+        return false;
+    return true;
+  }
+
+  USHORT len;
+  Type array[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (sizeof (USHORT), array);
+};
+
+
+#endif /* HB_OPEN_TYPE_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-head-private.hh
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_HEAD_PRIVATE_HH
+#define HB_OT_HEAD_PRIVATE_HH
+
+#include "hb-open-type-private.hh"
+
+/*
+ * head
+ */
+
+#define HB_OT_TAG_head HB_TAG('h','e','a','d')
+
+struct head
+{
+  static const hb_tag_t Tag	= HB_OT_TAG_head;
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    /* Shall we check for magicNumber here?  Who cares? */
+    return c->check_struct (this) && likely (version.major == 1);
+  }
+
+  FixedVersion	version;		/* Version of the head table--currently
+					 * 0x00010000 for version 1.0. */
+  FixedVersion	fontRevision;		/* Set by font manufacturer. */
+  ULONG		checkSumAdjustment;	/* To compute: set it to 0, sum the
+					 * entire font as ULONG, then store
+					 * 0xB1B0AFBA - sum. */
+  ULONG		magicNumber;		/* Set to 0x5F0F3CF5. */
+  USHORT	flags;			/* Bit 0: Baseline for font at y=0;
+					 * Bit 1: Left sidebearing point at x=0;
+					 * Bit 2: Instructions may depend on point size;
+					 * Bit 3: Force ppem to integer values for all
+					 *   internal scaler math; may use fractional
+					 *   ppem sizes if this bit is clear;
+					 * Bit 4: Instructions may alter advance width
+					 *   (the advance widths might not scale linearly);
+
+					 * Bits 5-10: These should be set according to
+					 *   Apple's specification. However, they are not
+					 *   implemented in OpenType.
+					 * Bit 5: This bit should be set in fonts that are
+					 *   intended to e laid out vertically, and in
+					 *   which the glyphs have been drawn such that an
+					 *   x-coordinate of 0 corresponds to the desired
+					 *   vertical baseline.
+					 * Bit 6: This bit must be set to zero.
+					 * Bit 7: This bit should be set if the font
+					 *   requires layout for correct linguistic
+					 *   rendering (e.g. Arabic fonts).
+					 * Bit 8: This bit should be set for a GX font
+					 *   which has one or more metamorphosis effects
+					 *   designated as happening by default.
+					 * Bit 9: This bit should be set if the font
+					 *   contains any strong right-to-left glyphs.
+					 * Bit 10: This bit should be set if the font
+					 *   contains Indic-style rearrangement effects.
+
+					 * Bit 11: Font data is 'lossless,' as a result
+					 *   of having been compressed and decompressed
+					 *   with the Agfa MicroType Express engine.
+					 * Bit 12: Font converted (produce compatible metrics)
+					 * Bit 13: Font optimized for ClearType™.
+					 *   Note, fonts that rely on embedded bitmaps (EBDT)
+					 *   for rendering should not be considered optimized
+					 *   for ClearType, and therefore should keep this bit
+					 *   cleared.
+					 * Bit 14: Reserved, set to 0
+					 * Bit 15: Reserved, set to 0. */
+  USHORT	unitsPerEm;		/* Valid range is from 16 to 16384. This value
+					 * should be a power of 2 for fonts that have
+					 * TrueType outlines. */
+  LONGDATETIME	created;		/* Number of seconds since 12:00 midnight,
+					   January 1, 1904. 64-bit integer */
+  LONGDATETIME	modified;		/* Number of seconds since 12:00 midnight,
+					   January 1, 1904. 64-bit integer */
+  SHORT		xMin;			/* For all glyph bounding boxes. */
+  SHORT		yMin;			/* For all glyph bounding boxes. */
+  SHORT		xMax;			/* For all glyph bounding boxes. */
+  SHORT		yMax;			/* For all glyph bounding boxes. */
+  USHORT	macStyle;		/* Bit 0: Bold (if set to 1);
+					 * Bit 1: Italic (if set to 1)
+					 * Bit 2: Underline (if set to 1)
+					 * Bit 3: Outline (if set to 1)
+					 * Bit 4: Shadow (if set to 1)
+					 * Bit 5: Condensed (if set to 1)
+					 * Bit 6: Extended (if set to 1)
+					 * Bits 7-15: Reserved (set to 0). */
+  USHORT	lowestRecPPEM;		/* Smallest readable size in pixels. */
+  SHORT		fontDirectionHint;	/* Deprecated (Set to 2).
+					 * 0: Fully mixed directional glyphs;
+					 * 1: Only strongly left to right;
+					 * 2: Like 1 but also contains neutrals;
+					 * -1: Only strongly right to left;
+					 * -2: Like -1 but also contains neutrals. */
+  SHORT		indexToLocFormat;	/* 0 for short offsets, 1 for long. */
+  SHORT		glyphDataFormat;	/* 0 for current format. */
+  public:
+  DEFINE_SIZE_STATIC (54);
+};
+
+
+#endif /* HB_OT_HEAD_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2007,2008,2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
+#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
+
+#include "hb-ot-layout-private.hh"
+
+#include "hb-open-type-private.hh"
+
+
+#define NO_CONTEXT		((unsigned int) 0x110000)
+#define NOT_COVERED		((unsigned int) 0x110000)
+#define MAX_NESTING_LEVEL	8
+
+
+/*
+ *
+ * OpenType Layout Common Table Formats
+ *
+ */
+
+
+/*
+ * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
+ */
+
+template <typename Type>
+struct Record
+{
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& offset.sanitize (c, base);
+  }
+
+  Tag		tag;		/* 4-byte Tag identifier */
+  OffsetTo<Type>
+		offset;		/* Offset from beginning of object holding
+				 * the Record */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+template <typename Type>
+struct RecordArrayOf : ArrayOf<Record<Type> > {
+  inline const Tag& get_tag (unsigned int i) const
+  {
+    if (unlikely (i >= this->len)) return Null(Tag);
+    return (*this)[i].tag;
+  }
+  inline unsigned int get_tags (unsigned int start_offset,
+				unsigned int *record_count /* IN/OUT */,
+				hb_tag_t     *record_tags /* OUT */) const
+  {
+    if (record_count) {
+      const Record<Type> *array = this->sub_array (start_offset, record_count);
+      unsigned int count = *record_count;
+      for (unsigned int i = 0; i < count; i++)
+	record_tags[i] = array[i].tag;
+    }
+    return this->len;
+  }
+  inline bool find_index (hb_tag_t tag, unsigned int *index) const
+  {
+    Tag t;
+    t.set (tag);
+    /* TODO: bsearch (need to sort in sanitize) */
+    const Record<Type> *a = this->array;
+    unsigned int count = this->len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (t == a[i].tag)
+      {
+        if (index) *index = i;
+        return true;
+      }
+    }
+    if (index) *index = Index::NOT_FOUND_INDEX;
+    return false;
+  }
+};
+
+template <typename Type>
+struct RecordListOf : RecordArrayOf<Type>
+{
+  inline const Type& operator [] (unsigned int i) const
+  { return this+RecordArrayOf<Type>::operator [](i).offset; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return RecordArrayOf<Type>::sanitize (c, this);
+  }
+};
+
+
+struct IndexArray : ArrayOf<Index>
+{
+  inline unsigned int get_indexes (unsigned int start_offset,
+				   unsigned int *_count /* IN/OUT */,
+				   unsigned int *_indexes /* OUT */) const
+  {
+    if (_count) {
+      const USHORT *array = this->sub_array (start_offset, _count);
+      unsigned int count = *_count;
+      for (unsigned int i = 0; i < count; i++)
+	_indexes[i] = array[i];
+    }
+    return this->len;
+  }
+};
+
+
+struct Script;
+struct LangSys;
+struct Feature;
+
+
+struct LangSys
+{
+  inline unsigned int get_feature_count (void) const
+  { return featureIndex.len; }
+  inline hb_tag_t get_feature_index (unsigned int i) const
+  { return featureIndex[i]; }
+  inline unsigned int get_feature_indexes (unsigned int start_offset,
+					   unsigned int *feature_count /* IN/OUT */,
+					   unsigned int *feature_indexes /* OUT */) const
+  { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
+
+  inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; }
+  inline unsigned int get_required_feature_index (void) const
+  {
+    if (reqFeatureIndex == 0xffff)
+      return Index::NOT_FOUND_INDEX;
+   return reqFeatureIndex;;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& featureIndex.sanitize (c);
+  }
+
+  Offset	lookupOrder;	/* = Null (reserved for an offset to a
+				 * reordering table) */
+  USHORT	reqFeatureIndex;/* Index of a feature required for this
+				 * language system--if no required features
+				 * = 0xFFFF */
+  IndexArray	featureIndex;	/* Array of indices into the FeatureList */
+  public:
+  DEFINE_SIZE_ARRAY (6, featureIndex);
+};
+DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF");
+
+
+struct Script
+{
+  inline unsigned int get_lang_sys_count (void) const
+  { return langSys.len; }
+  inline const Tag& get_lang_sys_tag (unsigned int i) const
+  { return langSys.get_tag (i); }
+  inline unsigned int get_lang_sys_tags (unsigned int start_offset,
+					 unsigned int *lang_sys_count /* IN/OUT */,
+					 hb_tag_t     *lang_sys_tags /* OUT */) const
+  { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); }
+  inline const LangSys& get_lang_sys (unsigned int i) const
+  {
+    if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys ();
+    return this+langSys[i].offset;
+  }
+  inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
+  { return langSys.find_index (tag, index); }
+
+  inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
+  inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return defaultLangSys.sanitize (c, this)
+	&& langSys.sanitize (c, this);
+  }
+
+  private:
+  OffsetTo<LangSys>
+		defaultLangSys;	/* Offset to DefaultLangSys table--from
+				 * beginning of Script table--may be Null */
+  RecordArrayOf<LangSys>
+		langSys;	/* Array of LangSysRecords--listed
+				 * alphabetically by LangSysTag */
+  public:
+  DEFINE_SIZE_ARRAY (4, langSys);
+};
+
+typedef RecordListOf<Script> ScriptList;
+
+
+struct Feature
+{
+  inline unsigned int get_lookup_count (void) const
+  { return lookupIndex.len; }
+  inline hb_tag_t get_lookup_index (unsigned int i) const
+  { return lookupIndex[i]; }
+  inline unsigned int get_lookup_indexes (unsigned int start_index,
+					  unsigned int *lookup_count /* IN/OUT */,
+					  unsigned int *lookup_tags /* OUT */) const
+  { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& lookupIndex.sanitize (c);
+  }
+
+  /* LONGTERMTODO: implement get_feature_parameters() */
+  /* LONGTERMTODO: implement FeatureSize and other special features? */
+  Offset	featureParams;	/* Offset to Feature Parameters table (if one
+				 * has been defined for the feature), relative
+				 * to the beginning of the Feature Table; = Null
+				 * if not required */
+  IndexArray	 lookupIndex;	/* Array of LookupList indices */
+  public:
+  DEFINE_SIZE_ARRAY (4, lookupIndex);
+};
+
+typedef RecordListOf<Feature> FeatureList;
+
+
+struct LookupFlag : USHORT
+{
+  enum {
+    RightToLeft		= 0x0001u,
+    IgnoreBaseGlyphs	= 0x0002u,
+    IgnoreLigatures	= 0x0004u,
+    IgnoreMarks		= 0x0008u,
+    IgnoreFlags		= 0x000Eu,
+    UseMarkFilteringSet	= 0x0010u,
+    Reserved		= 0x00E0u,
+    MarkAttachmentType	= 0xFF00u
+  };
+  public:
+  DEFINE_SIZE_STATIC (2);
+};
+
+struct Lookup
+{
+  inline unsigned int get_subtable_count (void) const { return subTable.len; }
+
+  inline unsigned int get_type (void) const { return lookupType; }
+  inline unsigned int get_flag (void) const
+  {
+    unsigned int flag = lookupFlag;
+    if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
+    {
+      const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      flag += (markFilteringSet << 16);
+    }
+    return flag;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    /* Real sanitize of the subtables is done by GSUB/GPOS/... */
+    if (!(c->check_struct (this)
+       && subTable.sanitize (c))) return false;
+    if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
+    {
+      USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      if (!markFilteringSet.sanitize (c)) return false;
+    }
+    return true;
+  }
+
+  USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
+  USHORT	lookupFlag;		/* Lookup qualifiers */
+  ArrayOf<Offset>
+		subTable;		/* Array of SubTables */
+  USHORT	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
+					 * structure. This field is only present if bit
+					 * UseMarkFilteringSet of lookup flags is set. */
+  public:
+  DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX);
+};
+
+typedef OffsetListOf<Lookup> LookupList;
+
+
+/*
+ * Coverage Table
+ */
+
+struct CoverageFormat1
+{
+  friend struct Coverage;
+
+  private:
+  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    if (unlikely (glyph_id > 0xFFFF))
+      return NOT_COVERED;
+    GlyphID gid;
+    gid.set (glyph_id);
+    /* TODO: bsearch (need to sort in sanitize) */
+    unsigned int num_glyphs = glyphArray.len;
+    for (unsigned int i = 0; i < num_glyphs; i++)
+      if (gid == glyphArray[i])
+        return i;
+    return NOT_COVERED;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return glyphArray.sanitize (c);
+  }
+
+  private:
+  USHORT	coverageFormat;	/* Format identifier--format = 1 */
+  ArrayOf<GlyphID>
+		glyphArray;	/* Array of GlyphIDs--in numerical order */
+  public:
+  DEFINE_SIZE_ARRAY (4, glyphArray);
+};
+
+struct CoverageRangeRecord
+{
+  friend struct CoverageFormat2;
+
+  private:
+  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    if (glyph_id >= start && glyph_id <= end)
+      return (unsigned int) startCoverageIndex + (glyph_id - start);
+    return NOT_COVERED;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  GlyphID	start;			/* First GlyphID in the range */
+  GlyphID	end;			/* Last GlyphID in the range */
+  USHORT	startCoverageIndex;	/* Coverage Index of first GlyphID in
+					 * range */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+DEFINE_NULL_DATA (CoverageRangeRecord, "\000\001");
+
+struct CoverageFormat2
+{
+  friend struct Coverage;
+
+  private:
+  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    /* TODO: bsearch (need to sort in sanitize) */
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      unsigned int coverage = rangeRecord[i].get_coverage (glyph_id);
+      if (coverage != NOT_COVERED)
+        return coverage;
+    }
+    return NOT_COVERED;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return rangeRecord.sanitize (c);
+  }
+
+  private:
+  USHORT	coverageFormat;	/* Format identifier--format = 2 */
+  ArrayOf<CoverageRangeRecord>
+		rangeRecord;	/* Array of glyph ranges--ordered by
+				 * Start GlyphID. rangeCount entries
+				 * long */
+  public:
+  DEFINE_SIZE_ARRAY (4, rangeRecord);
+};
+
+struct Coverage
+{
+  inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); }
+
+  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage(glyph_id);
+    case 2: return u.format2.get_coverage(glyph_id);
+    default:return NOT_COVERED;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  CoverageFormat1	format1;
+  CoverageFormat2	format2;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * Class Definition Table
+ */
+
+struct ClassDefFormat1
+{
+  friend struct ClassDef;
+
+  private:
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
+  {
+    if ((unsigned int) (glyph_id - startGlyph) < classValue.len)
+      return classValue[glyph_id - startGlyph];
+    return 0;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& classValue.sanitize (c);
+  }
+
+  USHORT	classFormat;		/* Format identifier--format = 1 */
+  GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
+  ArrayOf<USHORT>
+		classValue;		/* Array of Class Values--one per GlyphID */
+  public:
+  DEFINE_SIZE_ARRAY (6, classValue);
+};
+
+struct ClassRangeRecord
+{
+  friend struct ClassDefFormat2;
+
+  private:
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
+  {
+    if (glyph_id >= start && glyph_id <= end)
+      return classValue;
+    return 0;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  GlyphID	start;		/* First GlyphID in the range */
+  GlyphID	end;		/* Last GlyphID in the range */
+  USHORT	classValue;	/* Applied to all glyphs in the range */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+DEFINE_NULL_DATA (ClassRangeRecord, "\000\001");
+
+struct ClassDefFormat2
+{
+  friend struct ClassDef;
+
+  private:
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
+  {
+    /* TODO: bsearch (need to sort in sanitize) */
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      int classValue = rangeRecord[i].get_class (glyph_id);
+      if (classValue > 0)
+        return classValue;
+    }
+    return 0;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return rangeRecord.sanitize (c);
+  }
+
+  USHORT	classFormat;	/* Format identifier--format = 2 */
+  ArrayOf<ClassRangeRecord>
+		rangeRecord;	/* Array of glyph ranges--ordered by
+				 * Start GlyphID */
+  public:
+  DEFINE_SIZE_ARRAY (4, rangeRecord);
+};
+
+struct ClassDef
+{
+  inline hb_ot_layout_class_t operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); }
+
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_class(glyph_id);
+    case 2: return u.format2.get_class(glyph_id);
+    default:return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  ClassDefFormat1	format1;
+  ClassDefFormat2	format2;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * Device Tables
+ */
+
+struct Device
+{
+  /* XXX speed up */
+
+  inline hb_position_t get_x_delta (hb_ot_layout_context_t *c) const
+  { return c->font->x_ppem ? get_delta (c->font->x_ppem) * (uint64_t) c->font->x_scale / c->font->x_ppem : 0; }
+
+  inline hb_position_t get_y_delta (hb_ot_layout_context_t *c) const
+  { return c->font->y_ppem ? get_delta (c->font->y_ppem) * (uint64_t) c->font->y_scale / c->font->y_ppem : 0; }
+
+  inline int get_delta (unsigned int ppem_size) const
+  {
+    unsigned int f = deltaFormat;
+    if (unlikely (f < 1 || f > 3))
+      return 0;
+
+    if (ppem_size < startSize || ppem_size > endSize)
+      return 0;
+
+    unsigned int s = ppem_size - startSize;
+
+    unsigned int byte = deltaValue[s >> (4 - f)];
+    unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)));
+    unsigned int mask = (0xFFFF >> (16 - (1 << f)));
+
+    int delta = bits & mask;
+
+    if ((unsigned int) delta >= ((mask + 1) >> 1))
+      delta -= mask + 1;
+
+    return delta;
+  }
+
+  inline unsigned int get_size () const
+  {
+    unsigned int f = deltaFormat;
+    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
+    return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& c->check_range (this, this->get_size ());
+  }
+
+  private:
+  USHORT	startSize;		/* Smallest size to correct--in ppem */
+  USHORT	endSize;		/* Largest size to correct--in ppem */
+  USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
+					 * 1	Signed 2-bit value, 8 values per uint16
+					 * 2	Signed 4-bit value, 4 values per uint16
+					 * 3	Signed 8-bit value, 2 values per uint16
+					 */
+  USHORT	deltaValue[VAR];	/* Array of compressed data */
+  public:
+  DEFINE_SIZE_ARRAY (6, deltaValue);
+};
+
+
+#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-private.hh
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2007,2008,2009  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_GDEF_PRIVATE_HH
+#define HB_OT_LAYOUT_GDEF_PRIVATE_HH
+
+#include "hb-ot-layout-common-private.hh"
+
+#include "hb-font-private.hh"
+
+
+/*
+ * Attachment List Table
+ */
+
+typedef ArrayOf<USHORT> AttachPoint;	/* Array of contour point indices--in
+					 * increasing numerical order */
+
+struct AttachList
+{
+  inline unsigned int get_attach_points (hb_codepoint_t glyph_id,
+					 unsigned int start_offset,
+					 unsigned int *point_count /* IN/OUT */,
+					 unsigned int *point_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage) (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (point_count)
+	*point_count = 0;
+      return 0;
+    }
+
+    const AttachPoint &points = this+attachPoint[index];
+
+    if (point_count) {
+      const USHORT *array = points.sub_array (start_offset, point_count);
+      unsigned int count = *point_count;
+      for (unsigned int i = 0; i < count; i++)
+	point_array[i] = array[i];
+    }
+
+    return points.len;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& attachPoint.sanitize (c, this);
+  }
+
+  private:
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table -- from
+					 * beginning of AttachList table */
+  OffsetArrayOf<AttachPoint>
+		attachPoint;		/* Array of AttachPoint tables
+					 * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, attachPoint);
+};
+
+/*
+ * Ligature Caret Table
+ */
+
+struct CaretValueFormat1
+{
+  friend struct CaretValue;
+
+  private:
+  inline int get_caret_value (hb_ot_layout_context_t *c, hb_codepoint_t glyph_id HB_UNUSED) const
+  {
+    /* TODO vertical */
+    return c->scale_x (coordinate);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  USHORT	caretValueFormat;	/* Format identifier--format = 1 */
+  SHORT		coordinate;		/* X or Y value, in design units */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat2
+{
+  friend struct CaretValue;
+
+  private:
+  inline int get_caret_value (hb_ot_layout_context_t *c, hb_codepoint_t glyph_id) const
+  {
+    /* TODO vertical */
+    hb_position_t x, y;
+    if (hb_font_get_contour_point (c->font, c->face, caretValuePoint, glyph_id, &x, &y))
+      return x;
+    else
+      return 0;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  USHORT	caretValueFormat;	/* Format identifier--format = 2 */
+  USHORT	caretValuePoint;	/* Contour point index on glyph */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat3
+{
+  friend struct CaretValue;
+
+  inline int get_caret_value (hb_ot_layout_context_t *c, hb_codepoint_t glyph_id HB_UNUSED) const
+  {
+    /* TODO vertical */
+    return c->scale_x (coordinate) + ((this+deviceTable).get_x_delta (c));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& deviceTable.sanitize (c, this);
+  }
+
+  private:
+  USHORT	caretValueFormat;	/* Format identifier--format = 3 */
+  SHORT		coordinate;		/* X or Y value, in design units */
+  OffsetTo<Device>
+		deviceTable;		/* Offset to Device table for X or Y
+					 * value--from beginning of CaretValue
+					 * table */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct CaretValue
+{
+  inline int get_caret_value (hb_ot_layout_context_t *c, hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_caret_value (c, glyph_id);
+    case 2: return u.format2.get_caret_value (c, glyph_id);
+    case 3: return u.format3.get_caret_value (c, glyph_id);
+    default:return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    case 3: return u.format3.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  CaretValueFormat1	format1;
+  CaretValueFormat2	format2;
+  CaretValueFormat3	format3;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+struct LigGlyph
+{
+  inline unsigned int get_lig_carets (hb_ot_layout_context_t *c,
+				      hb_codepoint_t glyph_id,
+				      unsigned int start_offset,
+				      unsigned int *caret_count /* IN/OUT */,
+				      int *caret_array /* OUT */) const
+  {
+    if (caret_count) {
+      const OffsetTo<CaretValue> *array = carets.sub_array (start_offset, caret_count);
+      unsigned int count = *caret_count;
+      for (unsigned int i = 0; i < count; i++)
+	caret_array[i] = (this+array[i]).get_caret_value (c, glyph_id);
+    }
+
+    return carets.len;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return carets.sanitize (c, this);
+  }
+
+  private:
+  OffsetArrayOf<CaretValue>
+		carets;			/* Offset array of CaretValue tables
+					 * --from beginning of LigGlyph table
+					 * --in increasing coordinate order */
+  public:
+  DEFINE_SIZE_ARRAY (2, carets);
+};
+
+struct LigCaretList
+{
+  inline unsigned int get_lig_carets (hb_ot_layout_context_t *c,
+				      hb_codepoint_t glyph_id,
+				      unsigned int start_offset,
+				      unsigned int *caret_count /* IN/OUT */,
+				      int *caret_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage) (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (caret_count)
+	*caret_count = 0;
+      return 0;
+    }
+    const LigGlyph &lig_glyph = this+ligGlyph[index];
+    return lig_glyph.get_lig_carets (c, glyph_id, start_offset, caret_count, caret_array);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& ligGlyph.sanitize (c, this);
+  }
+
+  private:
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of LigCaretList table */
+  OffsetArrayOf<LigGlyph>
+		ligGlyph;		/* Array of LigGlyph tables
+					 * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, ligGlyph);
+};
+
+
+struct MarkGlyphSetsFormat1
+{
+  inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  LongOffsetArrayOf<Coverage>
+		coverage;		/* Array of long offsets to mark set
+					 * coverage tables */
+  public:
+  DEFINE_SIZE_ARRAY (4, coverage);
+};
+
+struct MarkGlyphSets
+{
+  inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.covers (set_index, glyph_id);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  MarkGlyphSetsFormat1	format1;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * GDEF
+ */
+
+struct GDEF
+{
+  static const hb_tag_t Tag	= HB_OT_TAG_GDEF;
+
+  enum {
+    UnclassifiedGlyph	= 0,
+    BaseGlyph		= 1,
+    LigatureGlyph	= 2,
+    MarkGlyph		= 3,
+    ComponentGlyph	= 4
+  };
+
+  inline bool has_glyph_classes () const { return glyphClassDef != 0; }
+  inline hb_ot_layout_class_t get_glyph_class (hb_codepoint_t glyph) const
+  { return (this+glyphClassDef).get_class (glyph); }
+
+  inline bool has_mark_attachment_types () const { return markAttachClassDef != 0; }
+  inline hb_ot_layout_class_t get_mark_attachment_type (hb_codepoint_t glyph) const
+  { return (this+markAttachClassDef).get_class (glyph); }
+
+  inline bool has_attach_points () const { return attachList != 0; }
+  inline unsigned int get_attach_points (hb_codepoint_t glyph_id,
+					 unsigned int start_offset,
+					 unsigned int *point_count /* IN/OUT */,
+					 unsigned int *point_array /* OUT */) const
+  { return (this+attachList).get_attach_points (glyph_id, start_offset, point_count, point_array); }
+
+  inline bool has_lig_carets () const { return ligCaretList != 0; }
+  inline unsigned int get_lig_carets (hb_ot_layout_context_t *c,
+				      hb_codepoint_t glyph_id,
+				      unsigned int start_offset,
+				      unsigned int *caret_count /* IN/OUT */,
+				      int *caret_array /* OUT */) const
+  { return (this+ligCaretList).get_lig_carets (c, glyph_id, start_offset, caret_count, caret_array); }
+
+  inline bool has_mark_sets () const { return version >= 0x00010002 && markGlyphSetsDef[0] != 0; }
+  inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return version >= 0x00010002 && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return version.sanitize (c) && likely (version.major == 1)
+	&& glyphClassDef.sanitize (c, this)
+	&& attachList.sanitize (c, this)
+	&& ligCaretList.sanitize (c, this)
+	&& markAttachClassDef.sanitize (c, this)
+	&& (version < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this));
+  }
+
+  private:
+  FixedVersion	version;		/* Version of the GDEF table--currently
+					 * 0x00010002 */
+  OffsetTo<ClassDef>
+		glyphClassDef;		/* Offset to class definition table
+					 * for glyph type--from beginning of
+					 * GDEF header (may be Null) */
+  OffsetTo<AttachList>
+		attachList;		/* Offset to list of glyphs with
+					 * attachment points--from beginning
+					 * of GDEF header (may be Null) */
+  OffsetTo<LigCaretList>
+		ligCaretList;		/* Offset to list of positioning points
+					 * for ligature carets--from beginning
+					 * of GDEF header (may be Null) */
+  OffsetTo<ClassDef>
+		markAttachClassDef;	/* Offset to class definition table for
+					 * mark attachment type--from beginning
+					 * of GDEF header (may be Null) */
+  OffsetTo<MarkGlyphSets>
+		markGlyphSetsDef[VAR];	/* Offset to the table of mark set
+					 * definitions--from beginning of GDEF
+					 * header (may be NULL).  Introduced
+					 * in version 00010002. */
+  public:
+  DEFINE_SIZE_ARRAY (12, markGlyphSetsDef);
+};
+
+
+#endif /* HB_OT_LAYOUT_GDEF_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-private.hh
@@ -0,0 +1,1630 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
+#define HB_OT_LAYOUT_GPOS_PRIVATE_HH
+
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+
+#define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
+
+/* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
+
+typedef USHORT Value;
+
+typedef Value ValueRecord[VAR];
+
+struct ValueFormat : USHORT
+{
+  enum
+  {
+    xPlacement	= 0x0001,	/* Includes horizontal adjustment for placement */
+    yPlacement	= 0x0002,	/* Includes vertical adjustment for placement */
+    xAdvance	= 0x0004,	/* Includes horizontal adjustment for advance */
+    yAdvance	= 0x0008,	/* Includes vertical adjustment for advance */
+    xPlaDevice	= 0x0010,	/* Includes horizontal Device table for placement */
+    yPlaDevice	= 0x0020,	/* Includes vertical Device table for placement */
+    xAdvDevice	= 0x0040,	/* Includes horizontal Device table for advance */
+    yAdvDevice	= 0x0080,	/* Includes vertical Device table for advance */
+    ignored	= 0x0F00,	/* Was used in TrueType Open for MM fonts */
+    reserved	= 0xF000,	/* For future use */
+
+    devices	= 0x00F0	/* Mask for having any Device table */
+  };
+
+/* All fields are options.  Only those available advance the value pointer. */
+#if 0
+  SHORT		xPlacement;		/* Horizontal adjustment for
+					 * placement--in design units */
+  SHORT		yPlacement;		/* Vertical adjustment for
+					 * placement--in design units */
+  SHORT		xAdvance;		/* Horizontal adjustment for
+					 * advance--in design units (only used
+					 * for horizontal writing) */
+  SHORT		yAdvance;		/* Vertical adjustment for advance--in
+					 * design units (only used for vertical
+					 * writing) */
+  Offset	xPlaDevice;		/* Offset to Device table for
+					 * horizontal placement--measured from
+					 * beginning of PosTable (may be NULL) */
+  Offset	yPlaDevice;		/* Offset to Device table for vertical
+					 * placement--measured from beginning
+					 * of PosTable (may be NULL) */
+  Offset	xAdvDevice;		/* Offset to Device table for
+					 * horizontal advance--measured from
+					 * beginning of PosTable (may be NULL) */
+  Offset	yAdvDevice;		/* Offset to Device table for vertical
+					 * advance--measured from beginning of
+					 * PosTable (may be NULL) */
+#endif
+
+  inline unsigned int get_len () const
+  { return _hb_popcount32 ((unsigned int) *this); }
+  inline unsigned int get_size () const
+  { return get_len () * Value::static_size; }
+
+  void apply_value (hb_ot_layout_context_t       *layout,
+		    const void                   *base,
+		    const Value                  *values,
+		    hb_internal_glyph_position_t &glyph_pos) const
+  {
+    unsigned int x_ppem, y_ppem;
+    unsigned int format = *this;
+
+    if (!format) return;
+
+    /* design units -> fractional pixel */
+    if (format & xPlacement) glyph_pos.x_offset  += layout->scale_x (get_short (values++));
+    if (format & yPlacement) glyph_pos.y_offset  += layout->scale_y (get_short (values++));
+    if (format & xAdvance)   glyph_pos.x_advance += layout->scale_x (get_short (values++));
+    if (format & yAdvance)   glyph_pos.y_advance += layout->scale_y (get_short (values++));
+
+    if (!has_device ()) return;
+
+    x_ppem = layout->font->x_ppem;
+    y_ppem = layout->font->y_ppem;
+
+    if (!x_ppem && !y_ppem) return;
+
+    /* pixel -> fractional pixel */
+    if (format & xPlaDevice) {
+      if (x_ppem) glyph_pos.x_offset  += (base + get_device (values++)).get_x_delta (layout); else values++;
+    }
+    if (format & yPlaDevice) {
+      if (y_ppem) glyph_pos.y_offset  += (base + get_device (values++)).get_y_delta (layout); else values++;
+    }
+    if (format & xAdvDevice) {
+      if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (layout); else values++;
+    }
+    if (format & yAdvDevice) {
+      if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_y_delta (layout); else values++;
+    }
+  }
+
+  private:
+  inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
+    unsigned int format = *this;
+
+    if (format & xPlacement) values++;
+    if (format & yPlacement) values++;
+    if (format & xAdvance)   values++;
+    if (format & yAdvance)   values++;
+
+    if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+
+    return true;
+  }
+
+  static inline OffsetTo<Device>& get_device (Value* value)
+  { return *CastP<OffsetTo<Device> > (value); }
+  static inline const OffsetTo<Device>& get_device (const Value* value)
+  { return *CastP<OffsetTo<Device> > (value); }
+
+  static inline const SHORT& get_short (const Value* value)
+  { return *CastP<SHORT> (value); }
+
+  public:
+
+  inline bool has_device () const {
+    unsigned int format = *this;
+    return (format & devices) != 0;
+  }
+
+  inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
+    TRACE_SANITIZE ();
+    return c->check_range (values, get_size ())
+	&& (!has_device () || sanitize_value_devices (c, base, values));
+  }
+
+  inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
+    TRACE_SANITIZE ();
+    unsigned int len = get_len ();
+
+    if (!c->check_array (values, get_size (), count)) return false;
+
+    if (!has_device ()) return true;
+
+    for (unsigned int i = 0; i < count; i++) {
+      if (!sanitize_value_devices (c, base, values))
+        return false;
+      values += len;
+    }
+
+    return true;
+  }
+
+  /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
+  inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
+    TRACE_SANITIZE ();
+
+    if (!has_device ()) return true;
+
+    for (unsigned int i = 0; i < count; i++) {
+      if (!sanitize_value_devices (c, base, values))
+        return false;
+      values += stride;
+    }
+
+    return true;
+  }
+};
+
+
+struct AnchorFormat1
+{
+  friend struct Anchor;
+
+  private:
+  inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
+			  hb_position_t *x, hb_position_t *y) const
+  {
+      *x = layout->scale_x (xCoordinate);
+      *y = layout->scale_y (yCoordinate);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  SHORT		xCoordinate;		/* Horizontal value--in design units */
+  SHORT		yCoordinate;		/* Vertical value--in design units */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct AnchorFormat2
+{
+  friend struct Anchor;
+
+  private:
+  inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
+			  hb_position_t *x, hb_position_t *y) const
+  {
+      unsigned int x_ppem = layout->font->x_ppem;
+      unsigned int y_ppem = layout->font->y_ppem;
+      hb_position_t cx, cy;
+      hb_bool_t ret;
+
+      if (x_ppem || y_ppem)
+	ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
+      *x = x_ppem && ret ? cx : layout->scale_x (xCoordinate);
+      *y = y_ppem && ret ? cy : layout->scale_y (yCoordinate);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  SHORT		xCoordinate;		/* Horizontal value--in design units */
+  SHORT		yCoordinate;		/* Vertical value--in design units */
+  USHORT	anchorPoint;		/* Index to glyph contour point */
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct AnchorFormat3
+{
+  friend struct Anchor;
+
+  private:
+  inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
+			  hb_position_t *x, hb_position_t *y) const
+  {
+      *x = layout->scale_x (xCoordinate);
+      *y = layout->scale_y (yCoordinate);
+
+      /* pixel -> fractional pixel */
+      if (layout->font->x_ppem)
+	*x += (this+xDeviceTable).get_x_delta (layout);
+      if (layout->font->y_ppem)
+	*y += (this+yDeviceTable).get_x_delta (layout);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& xDeviceTable.sanitize (c, this)
+	&& yDeviceTable.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 3 */
+  SHORT		xCoordinate;		/* Horizontal value--in design units */
+  SHORT		yCoordinate;		/* Vertical value--in design units */
+  OffsetTo<Device>
+		xDeviceTable;		/* Offset to Device table for X
+					 * coordinate-- from beginning of
+					 * Anchor table (may be NULL) */
+  OffsetTo<Device>
+		yDeviceTable;		/* Offset to Device table for Y
+					 * coordinate-- from beginning of
+					 * Anchor table (may be NULL) */
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+struct Anchor
+{
+  inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
+			  hb_position_t *x, hb_position_t *y) const
+  {
+    *x = *y = 0;
+    switch (u.format) {
+    case 1: u.format1.get_anchor (layout, glyph_id, x, y); return;
+    case 2: u.format2.get_anchor (layout, glyph_id, x, y); return;
+    case 3: u.format3.get_anchor (layout, glyph_id, x, y); return;
+    default:						    return;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    case 3: return u.format3.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  AnchorFormat1		format1;
+  AnchorFormat2		format2;
+  AnchorFormat3		format3;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+struct AnchorMatrix
+{
+  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
+    if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
+    return this+matrix[row * cols + col];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
+    TRACE_SANITIZE ();
+    if (!c->check_struct (this)) return false;
+    if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
+    unsigned int count = rows * cols;
+    if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
+    for (unsigned int i = 0; i < count; i++)
+      if (!matrix[i].sanitize (c, this)) return false;
+    return true;
+  }
+
+  USHORT	rows;			/* Number of rows */
+  private:
+  OffsetTo<Anchor>
+		matrix[VAR];		/* Matrix of offsets to Anchor tables--
+					 * from beginning of AnchorMatrix table */
+  public:
+  DEFINE_SIZE_ARRAY (2, matrix);
+};
+
+
+struct MarkRecord
+{
+  friend struct MarkArray;
+
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& markAnchor.sanitize (c, base);
+  }
+
+  private:
+  USHORT	klass;			/* Class defined for this mark */
+  OffsetTo<Anchor>
+		markAnchor;		/* Offset to Anchor table--from
+					 * beginning of MarkArray table */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage order */
+{
+  inline bool apply (hb_apply_context_t *c,
+		     unsigned int mark_index, unsigned int glyph_index,
+		     const AnchorMatrix &anchors, unsigned int class_count,
+		     unsigned int glyph_pos) const
+  {
+    TRACE_APPLY ();
+    const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
+    unsigned int mark_class = record.klass;
+
+    const Anchor& mark_anchor = this + record.markAnchor;
+    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
+
+    hb_position_t mark_x, mark_y, base_x, base_y;
+
+    mark_anchor.get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &mark_x, &mark_y);
+    glyph_anchor.get_anchor (c->layout, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
+
+    hb_internal_glyph_position_t &o = c->buffer->pos[c->buffer->i];
+    o.x_advance = 0;
+    o.y_advance = 0;
+    o.x_offset  = base_x - mark_x;
+    o.y_offset  = base_y - mark_y;
+    o.back      = c->buffer->i - glyph_pos;
+
+    c->buffer->i++;
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return ArrayOf<MarkRecord>::sanitize (c, this);
+  }
+};
+
+
+/* Lookups */
+
+struct SinglePosFormat1
+{
+  friend struct SinglePos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    valueFormat.apply_value (c->layout, this, values, c->buffer->pos[c->buffer->i]);
+
+    c->buffer->i++;
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& coverage.sanitize (c, this)
+	&& valueFormat.sanitize_value (c, this, values);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of subtable */
+  ValueFormat	valueFormat;		/* Defines the types of data in the
+					 * ValueRecord */
+  ValueRecord	values;			/* Defines positioning
+					 * value(s)--applied to all glyphs in
+					 * the Coverage table */
+  public:
+  DEFINE_SIZE_ARRAY (6, values);
+};
+
+struct SinglePosFormat2
+{
+  friend struct SinglePos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    if (likely (index >= valueCount))
+      return false;
+
+    valueFormat.apply_value (c->layout, this,
+			     &values[index * valueFormat.get_len ()],
+			     c->buffer->pos[c->buffer->i]);
+
+    c->buffer->i++;
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& coverage.sanitize (c, this)
+	&& valueFormat.sanitize_values (c, this, values, valueCount);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of subtable */
+  ValueFormat	valueFormat;		/* Defines the types of data in the
+					 * ValueRecord */
+  USHORT	valueCount;		/* Number of ValueRecords */
+  ValueRecord	values;			/* Array of ValueRecords--positioning
+					 * values applied to glyphs */
+  public:
+  DEFINE_SIZE_ARRAY (8, values);
+};
+
+struct SinglePos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    case 2: return u.format2.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  SinglePosFormat1	format1;
+  SinglePosFormat2	format2;
+  } u;
+};
+
+
+struct PairValueRecord
+{
+  friend struct PairSet;
+
+  private:
+  GlyphID	secondGlyph;		/* GlyphID of second glyph in the
+					 * pair--first glyph is listed in the
+					 * Coverage table */
+  ValueRecord	values;			/* Positioning data for the first glyph
+					 * followed by for second glyph */
+  public:
+  DEFINE_SIZE_ARRAY (2, values);
+};
+
+struct PairSet
+{
+  friend struct PairPosFormat1;
+
+  inline bool apply (hb_apply_context_t *c,
+		     const ValueFormat *valueFormats,
+		     unsigned int pos) const
+  {
+    TRACE_APPLY ();
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
+
+    unsigned int count = len;
+    const PairValueRecord *record = CastP<PairValueRecord> (array);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (c->buffer->info[pos].codepoint == record->secondGlyph)
+      {
+	valueFormats[0].apply_value (c->layout, this, &record->values[0], c->buffer->pos[c->buffer->i]);
+	valueFormats[1].apply_value (c->layout, this, &record->values[len1], c->buffer->pos[pos]);
+	if (len2)
+	  pos++;
+	c->buffer->i = pos;
+	return true;
+      }
+      record = &StructAtOffset<PairValueRecord> (record, record_size);
+    }
+
+    return false;
+  }
+
+  struct sanitize_closure_t {
+    void *base;
+    ValueFormat *valueFormats;
+    unsigned int len1; /* valueFormats[0].get_len() */
+    unsigned int stride; /* 1 + len1 + len2 */
+  };
+
+  inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
+    TRACE_SANITIZE ();
+    if (!(c->check_struct (this)
+       && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
+
+    unsigned int count = len;
+    PairValueRecord *record = CastP<PairValueRecord> (array);
+    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);
+  }
+
+  private:
+  USHORT	len;			/* Number of PairValueRecords */
+  USHORT	array[VAR];		/* Array of PairValueRecords--ordered
+					 * by GlyphID of the second glyph */
+  public:
+  DEFINE_SIZE_ARRAY (2, array);
+};
+
+struct PairPosFormat1
+{
+  friend struct PairPos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+    if (unlikely (c->buffer->i + 2 > end))
+      return false;
+
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    unsigned int j = c->buffer->i + 1;
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
+    {
+      if (unlikely (j == end))
+	return false;
+      j++;
+    }
+
+    return (this+pairSet[index]).apply (c, &valueFormat1, j);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+
+    unsigned int len1 = valueFormat1.get_len ();
+    unsigned int len2 = valueFormat2.get_len ();
+    PairSet::sanitize_closure_t closure = {
+      this,
+      &valueFormat1,
+      len1,
+      1 + len1 + len2
+    };
+
+    return c->check_struct (this)
+	&& coverage.sanitize (c, this)
+	&& pairSet.sanitize (c, this, &closure);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of subtable */
+  ValueFormat	valueFormat1;		/* Defines the types of data in
+					 * ValueRecord1--for the first glyph
+					 * in the pair--may be zero (0) */
+  ValueFormat	valueFormat2;		/* Defines the types of data in
+					 * ValueRecord2--for the second glyph
+					 * in the pair--may be zero (0) */
+  OffsetArrayOf<PairSet>
+		pairSet;		/* Array of PairSet tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (10, pairSet);
+};
+
+struct PairPosFormat2
+{
+  friend struct PairPos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+    if (unlikely (c->buffer->i + 2 > end))
+      return false;
+
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    unsigned int j = c->buffer->i + 1;
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
+    {
+      if (unlikely (j == end))
+	return false;
+      j++;
+    }
+
+    unsigned int len1 = valueFormat1.get_len ();
+    unsigned int len2 = valueFormat2.get_len ();
+    unsigned int record_len = len1 + len2;
+
+    unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->i].codepoint);
+    unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
+      return false;
+
+    const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
+    valueFormat1.apply_value (c->layout, this, v, c->buffer->pos[c->buffer->i]);
+    valueFormat2.apply_value (c->layout, this, v + len1, c->buffer->pos[j]);
+
+    if (len2)
+      j++;
+    c->buffer->i = j;
+
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!(c->check_struct (this)
+       && coverage.sanitize (c, this)
+       && classDef1.sanitize (c, this)
+       && classDef2.sanitize (c, this))) return false;
+
+    unsigned int len1 = valueFormat1.get_len ();
+    unsigned int len2 = valueFormat2.get_len ();
+    unsigned int stride = len1 + len2;
+    unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
+    unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
+    return c->check_array (values, record_size, count) &&
+	   valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+	   valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of subtable */
+  ValueFormat	valueFormat1;		/* ValueRecord definition--for the
+					 * first glyph of the pair--may be zero
+					 * (0) */
+  ValueFormat	valueFormat2;		/* ValueRecord definition--for the
+					 * second glyph of the pair--may be
+					 * zero (0) */
+  OffsetTo<ClassDef>
+		classDef1;		/* Offset to ClassDef table--from
+					 * beginning of PairPos subtable--for
+					 * the first glyph of the pair */
+  OffsetTo<ClassDef>
+		classDef2;		/* Offset to ClassDef table--from
+					 * beginning of PairPos subtable--for
+					 * the second glyph of the pair */
+  USHORT	class1Count;		/* Number of classes in ClassDef1
+					 * table--includes Class0 */
+  USHORT	class2Count;		/* Number of classes in ClassDef2
+					 * table--includes Class0 */
+  ValueRecord	values;			/* Matrix of value pairs:
+					 * class1-major, class2-minor,
+					 * Each entry has value1 and value2 */
+  public:
+  DEFINE_SIZE_ARRAY (16, values);
+};
+
+struct PairPos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    case 2: return u.format2.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  PairPosFormat1	format1;
+  PairPosFormat2	format2;
+  } u;
+};
+
+
+struct EntryExitRecord
+{
+  friend struct CursivePosFormat1;
+
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    return entryAnchor.sanitize (c, base)
+	&& exitAnchor.sanitize (c, base);
+  }
+
+  private:
+  OffsetTo<Anchor>
+		entryAnchor;		/* Offset to EntryAnchor table--from
+					 * beginning of CursivePos
+					 * subtable--may be NULL */
+  OffsetTo<Anchor>
+		exitAnchor;		/* Offset to ExitAnchor table--from
+					 * beginning of CursivePos
+					 * subtable--may be NULL */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CursivePosFormat1
+{
+  friend struct CursivePos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    /* Now comes the messiest part of the whole OpenType
+       specification.  At first glance, cursive connections seem easy
+       to understand, but there are pitfalls!  The reason is that
+       the specs don't mention how to compute the advance values
+       resp. glyph offsets.  I was told it would be an omission, to
+       be fixed in the next OpenType version...  Again many thanks to
+       Andrei Burago <andreib@microsoft.com> for clarifications.
+
+       Consider the following example:
+
+			|  xadv1    |
+			 +---------+
+			 |         |
+		   +-----+--+ 1    |
+		   |     | .|      |
+		   |    0+--+------+
+		   |   2    |
+		   |        |
+		  0+--------+
+		  |  xadv2   |
+
+	 glyph1: advance width = 12
+		 anchor point = (3,1)
+
+	 glyph2: advance width = 11
+		 anchor point = (9,4)
+
+	 LSB is 1 for both glyphs (so the boxes drawn above are glyph
+	 bboxes).  Writing direction is R2L; `0' denotes the glyph's
+	 coordinate origin.
+
+       Now the surprising part: The advance width of the *left* glyph
+       (resp. of the *bottom* glyph) will be modified, no matter
+       whether the writing direction is L2R or R2L (resp. T2B or
+       B2T)!  This assymetry is caused by the fact that the glyph's
+       coordinate origin is always the lower left corner for all
+       writing directions.
+
+       Continuing the above example, we can compute the new
+       (horizontal) advance width of glyph2 as
+
+	 9 - 3 = 6  ,
+
+       and the new vertical offset of glyph2 as
+
+	 1 - 4 = -3  .
+
+
+       Vertical writing direction is far more complicated:
+
+       a) Assuming that we recompute the advance height of the lower glyph:
+
+				    --
+			 +---------+
+		--       |         |
+		   +-----+--+ 1    | yadv1
+		   |     | .|      |
+	     yadv2 |    0+--+------+        -- BSB1  --
+		   |   2    |       --      --        y_offset
+		   |        |
+     BSB2 --      0+--------+                        --
+	  --    --
+
+	 glyph1: advance height = 6
+		 anchor point = (3,1)
+
+	 glyph2: advance height = 7
+		 anchor point = (9,4)
+
+	 TSB is 1 for both glyphs; writing direction is T2B.
+
+
+	   BSB1     = yadv1 - (TSB1 + ymax1)
+	   BSB2     = yadv2 - (TSB2 + ymax2)
+	   y_offset = y2 - y1
+
+	 vertical advance width of glyph2
+	   = y_offset + BSB2 - BSB1
+	   = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
+	   = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
+	   = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
+
+
+       b) Assuming that we recompute the advance height of the upper glyph:
+
+				    --      --
+			 +---------+        -- TSB1
+	  --    --       |         |
+     TSB2 --       +-----+--+ 1    | yadv1   ymax1
+		   |     | .|      |
+	     yadv2 |    0+--+------+        --       --
+      ymax2        |   2    |       --                y_offset
+		   |        |
+	  --      0+--------+                        --
+		--
+
+	 glyph1: advance height = 6
+		 anchor point = (3,1)
+
+	 glyph2: advance height = 7
+		 anchor point = (9,4)
+
+	 TSB is 1 for both glyphs; writing direction is T2B.
+
+	 y_offset = y2 - y1
+
+	 vertical advance width of glyph2
+	   = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
+	   = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
+
+
+       Comparing a) with b) shows that b) is easier to compute.  I'll wait
+       for a reply from Andrei to see what should really be implemented...
+
+       Since horizontal advance widths or vertical advance heights
+       can be used alone but not together, no ambiguity occurs.        */
+
+    struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &c->layout->info.gpos;
+    hb_codepoint_t last_pos = gpi->last;
+    gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
+
+    /* We don't handle mark glyphs here. */
+    if (c->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
+      return false;
+
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const EntryExitRecord &record = entryExitRecord[index];
+
+    if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
+      goto end;
+
+    hb_position_t entry_x, entry_y;
+    (this+record.entryAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &entry_x, &entry_y);
+
+    /* TODO vertical */
+
+    if (c->buffer->direction == HB_DIRECTION_RTL)
+    {
+      /* advance is absolute, not relative */
+      c->buffer->pos[c->buffer->i].x_advance = entry_x - gpi->anchor_x;
+    }
+    else
+    {
+      /* advance is absolute, not relative */
+      c->buffer->pos[last_pos].x_advance = gpi->anchor_x - entry_x;
+    }
+
+    if  (c->lookup_flag & LookupFlag::RightToLeft)
+    {
+      c->buffer->pos[last_pos].cursive_chain = last_pos - c->buffer->i;
+      c->buffer->pos[last_pos].y_offset = entry_y - gpi->anchor_y;
+    }
+    else
+    {
+      c->buffer->pos[c->buffer->i].cursive_chain = c->buffer->i - last_pos;
+      c->buffer->pos[c->buffer->i].y_offset = gpi->anchor_y - entry_y;
+    }
+
+  end:
+    if (record.exitAnchor)
+    {
+      gpi->last = c->buffer->i;
+      (this+record.exitAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &gpi->anchor_x, &gpi->anchor_y);
+    }
+
+    c->buffer->i++;
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& entryExitRecord.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of subtable */
+  ArrayOf<EntryExitRecord>
+		entryExitRecord;	/* Array of EntryExit records--in
+					 * Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (6, entryExitRecord);
+};
+
+struct CursivePos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  CursivePosFormat1	format1;
+  } u;
+};
+
+
+typedef AnchorMatrix BaseArray;		/* base-major--
+					 * in order of BaseCoverage Index--,
+					 * mark-minor--
+					 * ordered by class--zero-based. */
+
+struct MarkBasePosFormat1
+{
+  friend struct MarkBasePos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (mark_index == NOT_COVERED))
+      return false;
+
+    /* now we search backwards for a non-mark glyph */
+    unsigned int property;
+    unsigned int j = c->buffer->i;
+    do
+    {
+      if (unlikely (!j))
+	return false;
+      j--;
+    } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
+
+    /* The following assertion is too strong, so we've disabled it. */
+    if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
+      return false;
+
+    unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
+    if (base_index == NOT_COVERED)
+      return false;
+
+    return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+        && markCoverage.sanitize (c, this)
+	&& baseCoverage.sanitize (c, this)
+	&& markArray.sanitize (c, this)
+	&& baseArray.sanitize (c, this, (unsigned int) classCount);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		markCoverage;		/* Offset to MarkCoverage table--from
+					 * beginning of MarkBasePos subtable */
+  OffsetTo<Coverage>
+		baseCoverage;		/* Offset to BaseCoverage table--from
+					 * beginning of MarkBasePos subtable */
+  USHORT	classCount;		/* Number of classes defined for marks */
+  OffsetTo<MarkArray>
+		markArray;		/* Offset to MarkArray table--from
+					 * beginning of MarkBasePos subtable */
+  OffsetTo<BaseArray>
+		baseArray;		/* Offset to BaseArray table--from
+					 * beginning of MarkBasePos subtable */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkBasePos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  MarkBasePosFormat1	format1;
+  } u;
+};
+
+
+typedef AnchorMatrix LigatureAttach;	/* component-major--
+					 * in order of writing direction--,
+					 * mark-minor--
+					 * ordered by class--zero-based. */
+
+typedef OffsetListOf<LigatureAttach> LigatureArray;
+					/* Array of LigatureAttach
+					 * tables ordered by
+					 * LigatureCoverage Index */
+
+struct MarkLigPosFormat1
+{
+  friend struct MarkLigPos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (mark_index == NOT_COVERED))
+      return false;
+
+    /* now we search backwards for a non-mark glyph */
+    unsigned int property;
+    unsigned int j = c->buffer->i;
+    do
+    {
+      if (unlikely (!j))
+	return false;
+      j--;
+    } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
+
+    /* The following assertion is too strong, so we've disabled it. */
+    if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
+      return false;
+
+    unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
+    if (lig_index == NOT_COVERED)
+      return false;
+
+    const LigatureArray& lig_array = this+ligatureArray;
+    const LigatureAttach& lig_attach = lig_array[lig_index];
+
+    /* Find component to attach to */
+    unsigned int comp_count = lig_attach.rows;
+    if (unlikely (!comp_count))
+      return false;
+    unsigned int comp_index;
+    /* We must now check whether the ligature ID of the current mark glyph
+     * is identical to the ligature ID of the found ligature.  If yes, we
+     * can directly use the component index.  If not, we attach the mark
+     * glyph to the last component of the ligature. */
+    if (c->buffer->info[j].lig_id && c->buffer->info[j].lig_id == c->buffer->info[c->buffer->i].lig_id && c->buffer->info[c->buffer->i].component)
+    {
+      comp_index = c->buffer->info[c->buffer->i].component - 1;
+      if (comp_index >= comp_count)
+	comp_index = comp_count - 1;
+    }
+    else
+      comp_index = comp_count - 1;
+
+    return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+        && markCoverage.sanitize (c, this)
+	&& ligatureCoverage.sanitize (c, this)
+	&& markArray.sanitize (c, this)
+	&& ligatureArray.sanitize (c, this, (unsigned int) classCount);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		markCoverage;		/* Offset to Mark Coverage table--from
+					 * beginning of MarkLigPos subtable */
+  OffsetTo<Coverage>
+		ligatureCoverage;	/* Offset to Ligature Coverage
+					 * table--from beginning of MarkLigPos
+					 * subtable */
+  USHORT	classCount;		/* Number of defined mark classes */
+  OffsetTo<MarkArray>
+		markArray;		/* Offset to MarkArray table--from
+					 * beginning of MarkLigPos subtable */
+  OffsetTo<LigatureArray>
+		ligatureArray;		/* Offset to LigatureArray table--from
+					 * beginning of MarkLigPos subtable */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkLigPos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  MarkLigPosFormat1	format1;
+  } u;
+};
+
+
+typedef AnchorMatrix Mark2Array;	/* mark2-major--
+					 * in order of Mark2Coverage Index--,
+					 * mark1-minor--
+					 * ordered by class--zero-based. */
+
+struct MarkMarkPosFormat1
+{
+  friend struct MarkMarkPos;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (mark1_index == NOT_COVERED))
+      return false;
+
+    /* now we search backwards for a suitable mark glyph until a non-mark glyph */
+    unsigned int property;
+    unsigned int j = c->buffer->i;
+    do
+    {
+      if (unlikely (!j))
+	return false;
+      j--;
+    } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property));
+
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+      return false;
+
+    /* Two marks match only if they belong to the same base, or same component
+     * of the same ligature.  That is, the component numbers must match, and
+     * if those are non-zero, the ligid number should also match. */
+    if ((c->buffer->info[j].component != c->buffer->info[c->buffer->i].component) ||
+	(c->buffer->info[j].component && c->buffer->info[j].lig_id != c->buffer->info[c->buffer->i].lig_id))
+      return false;
+
+    unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
+    if (mark2_index == NOT_COVERED)
+      return false;
+
+    return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this)
+	&& mark1Coverage.sanitize (c, this)
+	&& mark2Coverage.sanitize (c, this)
+	&& mark1Array.sanitize (c, this)
+	&& mark2Array.sanitize (c, this, (unsigned int) classCount);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		mark1Coverage;		/* Offset to Combining Mark1 Coverage
+					 * table--from beginning of MarkMarkPos
+					 * subtable */
+  OffsetTo<Coverage>
+		mark2Coverage;		/* Offset to Combining Mark2 Coverage
+					 * table--from beginning of MarkMarkPos
+					 * subtable */
+  USHORT	classCount;		/* Number of defined mark classes */
+  OffsetTo<MarkArray>
+		mark1Array;		/* Offset to Mark1Array table--from
+					 * beginning of MarkMarkPos subtable */
+  OffsetTo<Mark2Array>
+		mark2Array;		/* Offset to Mark2Array table--from
+					 * beginning of MarkMarkPos subtable */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkMarkPos
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  MarkMarkPosFormat1	format1;
+  } u;
+};
+
+
+static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+
+struct ContextPos : Context
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return Context::apply (c, position_lookup);
+  }
+};
+
+struct ChainContextPos : ChainContext
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return ChainContext::apply (c, position_lookup);
+  }
+};
+
+
+struct ExtensionPos : Extension
+{
+  friend struct PosLookupSubTable;
+
+  private:
+  inline const struct PosLookupSubTable& get_subtable (void) const
+  {
+    unsigned int offset = get_offset ();
+    if (unlikely (!offset)) return Null(PosLookupSubTable);
+    return StructAtOffset<PosLookupSubTable> (this, offset);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const;
+
+  inline bool sanitize (hb_sanitize_context_t *c);
+};
+
+
+
+/*
+ * PosLookup
+ */
+
+
+struct PosLookupSubTable
+{
+  friend struct PosLookup;
+
+  enum {
+    Single		= 1,
+    Pair		= 2,
+    Cursive		= 3,
+    MarkBase		= 4,
+    MarkLig		= 5,
+    MarkMark		= 6,
+    Context		= 7,
+    ChainContext	= 8,
+    Extension		= 9
+  };
+
+  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
+  {
+    TRACE_APPLY ();
+    switch (lookup_type) {
+    case Single:		return u.single.apply (c);
+    case Pair:			return u.pair.apply (c);
+    case Cursive:		return u.cursive.apply (c);
+    case MarkBase:		return u.markBase.apply (c);
+    case MarkLig:		return u.markLig.apply (c);
+    case MarkMark:		return u.markMark.apply (c);
+    case Context:		return u.c.apply (c);
+    case ChainContext:		return u.chainContext.apply (c);
+    case Extension:		return u.extension.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
+    TRACE_SANITIZE ();
+    switch (lookup_type) {
+    case Single:		return u.single.sanitize (c);
+    case Pair:			return u.pair.sanitize (c);
+    case Cursive:		return u.cursive.sanitize (c);
+    case MarkBase:		return u.markBase.sanitize (c);
+    case MarkLig:		return u.markLig.sanitize (c);
+    case MarkMark:		return u.markMark.sanitize (c);
+    case Context:		return u.c.sanitize (c);
+    case ChainContext:		return u.chainContext.sanitize (c);
+    case Extension:		return u.extension.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		sub_format;
+  SinglePos		single;
+  PairPos		pair;
+  CursivePos		cursive;
+  MarkBasePos		markBase;
+  MarkLigPos		markLig;
+  MarkMarkPos		markMark;
+  ContextPos		c;
+  ChainContextPos	chainContext;
+  ExtensionPos		extension;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, sub_format);
+};
+
+
+struct PosLookup : Lookup
+{
+  inline const PosLookupSubTable& get_subtable (unsigned int i) const
+  { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
+
+  inline bool apply_once (hb_ot_layout_context_t *layout,
+			  hb_buffer_t *buffer,
+			  hb_mask_t lookup_mask,
+			  unsigned int context_length,
+			  unsigned int nesting_level_left) const
+  {
+    unsigned int lookup_type = get_type ();
+    hb_apply_context_t c[1] = {{0}};
+
+    c->layout = layout;
+    c->buffer = buffer;
+    c->lookup_mask = lookup_mask;
+    c->context_length = context_length;
+    c->nesting_level_left = nesting_level_left;
+    c->lookup_flag = get_flag ();
+
+    if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
+      return false;
+
+    for (unsigned int i = 0; i < get_subtable_count (); i++)
+      if (get_subtable (i).apply (c, lookup_type))
+	return true;
+
+    return false;
+  }
+
+   inline bool apply_string (hb_ot_layout_context_t *layout,
+			     hb_buffer_t *buffer,
+			     hb_mask_t    mask) const
+  {
+    bool ret = false;
+
+    if (unlikely (!buffer->len))
+      return false;
+
+    layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
+
+    buffer->i = 0;
+    while (buffer->i < buffer->len)
+    {
+      bool done;
+      if (buffer->info[buffer->i].mask & mask)
+      {
+	  done = apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL);
+	  ret |= done;
+      }
+      else
+      {
+          done = false;
+	  /* Contrary to properties defined in GDEF, user-defined properties
+	     will always stop a possible cursive positioning.                */
+	  layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
+      }
+
+      if (!done)
+	buffer->i++;
+    }
+
+    return ret;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!Lookup::sanitize (c))) return false;
+    OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
+    return list.sanitize (c, this, get_type ());
+  }
+};
+
+typedef OffsetListOf<PosLookup> PosLookupList;
+
+/*
+ * GPOS
+ */
+
+struct GPOS : GSUBGPOS
+{
+  static const hb_tag_t Tag	= HB_OT_TAG_GPOS;
+
+  inline const PosLookup& get_lookup (unsigned int i) const
+  { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
+
+  inline bool position_lookup (hb_ot_layout_context_t *layout,
+			       hb_buffer_t  *buffer,
+			       unsigned int  lookup_index,
+			       hb_mask_t     mask) const
+  { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+    OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
+    return list.sanitize (c, this);
+  }
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+
+/* Out-of-class implementation for methods recursing */
+
+inline bool ExtensionPos::apply (hb_apply_context_t *c) const
+{
+  TRACE_APPLY ();
+  return get_subtable ().apply (c, get_type ());
+}
+
+inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
+{
+  TRACE_SANITIZE ();
+  if (unlikely (!Extension::sanitize (c))) return false;
+  unsigned int offset = get_offset ();
+  if (unlikely (!offset)) return true;
+  return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
+}
+
+static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+{
+  const GPOS &gpos = *(c->layout->face->ot_layout->gpos);
+  const PosLookup &l = gpos.get_lookup (lookup_index);
+
+  if (unlikely (c->nesting_level_left == 0))
+    return false;
+
+  if (unlikely (c->context_length < 1))
+    return false;
+
+  return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
+}
+
+
+#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-private.hh
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
+#define HB_OT_LAYOUT_GSUB_PRIVATE_HH
+
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+
+struct SingleSubstFormat1
+{
+  friend struct SingleSubst;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    glyph_id += deltaGlyphID;
+    c->buffer->replace_glyph (glyph_id);
+
+    /* We inherit the old glyph class to the substituted glyph */
+    if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
+      _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
+
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& deltaGlyphID.sanitize (c);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of Substitution table */
+  SHORT		deltaGlyphID;		/* Add to original GlyphID to get
+					 * substitute GlyphID */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct SingleSubstFormat2
+{
+  friend struct SingleSubst;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    if (unlikely (index >= substitute.len))
+      return false;
+
+    glyph_id = substitute[index];
+    c->buffer->replace_glyph (glyph_id);
+
+    /* We inherit the old glyph class to the substituted glyph */
+    if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
+      _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
+
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& substitute.sanitize (c);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of Substitution table */
+  ArrayOf<GlyphID>
+		substitute;		/* Array of substitute
+					 * GlyphIDs--ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, substitute);
+};
+
+struct SingleSubst
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    case 2: return u.format2.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  SingleSubstFormat1	format1;
+  SingleSubstFormat2	format2;
+  } u;
+};
+
+
+struct Sequence
+{
+  friend struct MultipleSubstFormat1;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    if (unlikely (!substitute.len))
+      return false;
+
+    c->buffer->add_output_glyphs_be16 (1,
+				       substitute.len, (const uint16_t *) substitute.array,
+				       0xFFFF, 0xFFFF);
+
+    /* This is a guess only ... */
+    if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
+    {
+      unsigned int property = c->property;
+      if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
+        property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
+
+      unsigned int count = substitute.len;
+      for (unsigned int n = 0; n < count; n++)
+	_hb_ot_layout_set_glyph_property (c->layout->face, substitute[n], property);
+    }
+
+    return true;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return substitute.sanitize (c);
+  }
+
+  private:
+  ArrayOf<GlyphID>
+		substitute;		/* String of GlyphIDs to substitute */
+  public:
+  DEFINE_SIZE_ARRAY (2, substitute);
+};
+
+struct MultipleSubstFormat1
+{
+  friend struct MultipleSubst;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    return (this+sequence[index]).apply (c);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& sequence.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of Substitution table */
+  OffsetArrayOf<Sequence>
+		sequence;		/* Array of Sequence tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, sequence);
+};
+
+struct MultipleSubst
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  MultipleSubstFormat1	format1;
+  } u;
+};
+
+
+typedef ArrayOf<GlyphID> AlternateSet;	/* Array of alternate GlyphIDs--in
+					 * arbitrary order */
+
+struct AlternateSubstFormat1
+{
+  friend struct AlternateSubst;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
+    hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
+
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const AlternateSet &alt_set = this+alternateSet[index];
+
+    if (unlikely (!alt_set.len))
+      return false;
+
+    /* Note: This breaks badly if two features enabled this lookup together. */
+    unsigned int shift = _hb_ctz (lookup_mask);
+    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
+
+    if (unlikely (alt_index > alt_set.len || alt_index == 0))
+      return false;
+
+    glyph_id = alt_set[alt_index - 1];
+
+    c->buffer->replace_glyph (glyph_id);
+
+    /* We inherit the old glyph class to the substituted glyph */
+    if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
+      _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
+
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& alternateSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of Substitution table */
+  OffsetArrayOf<AlternateSet>
+		alternateSet;		/* Array of AlternateSet tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, alternateSet);
+};
+
+struct AlternateSubst
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  AlternateSubstFormat1	format1;
+  } u;
+};
+
+
+struct Ligature
+{
+  friend struct LigatureSet;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, bool is_mark) const
+  {
+    TRACE_APPLY ();
+    unsigned int i, j;
+    unsigned int count = component.len;
+    unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+    if (unlikely (c->buffer->i + count > end))
+      return false;
+
+    for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
+    {
+      unsigned int property;
+      while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property))
+      {
+	if (unlikely (j + count - i == end))
+	  return false;
+	j++;
+      }
+
+      if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+	is_mark = false;
+
+      if (likely (c->buffer->info[j].codepoint != component[i]))
+        return false;
+    }
+    /* This is just a guess ... */
+    if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
+      _hb_ot_layout_set_glyph_class (c->layout->face, ligGlyph,
+				     is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
+					     : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
+
+    if (j == c->buffer->i + i) /* No input glyphs skipped */
+      /* We don't use a new ligature ID if there are no skipped
+	 glyphs and the ligature already has an ID. */
+      c->buffer->add_output_glyphs_be16 (i,
+					 1, (const uint16_t *) &ligGlyph,
+					 0,
+					 c->buffer->info[c->buffer->i].lig_id && !c->buffer->info[c->buffer->i].component ?
+					 0xFFFF : c->buffer->allocate_lig_id ());
+    else
+    {
+      unsigned int lig_id = c->buffer->allocate_lig_id ();
+      c->buffer->add_output_glyph (ligGlyph, 0xFFFF, lig_id);
+
+      /* Now we must do a second loop to copy the skipped glyphs to
+	 `out' and assign component values to it.  We start with the
+	 glyph after the first component.  Glyphs between component
+	 i and i+1 belong to component i.  Together with the lig_id
+	 value it is later possible to check whether a specific
+	 component value really belongs to a given ligature. */
+
+      for ( i = 1; i < count; i++ )
+      {
+	while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, NULL))
+	  c->buffer->add_output_glyph (c->buffer->info[c->buffer->i].codepoint, i, lig_id);
+
+	(c->buffer->i)++;
+      }
+    }
+
+    return true;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return ligGlyph.sanitize (c)
+        && component.sanitize (c);
+  }
+
+  private:
+  GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
+  HeadlessArrayOf<GlyphID>
+		component;		/* Array of component GlyphIDs--start
+					 * with the second  component--ordered
+					 * in writing direction */
+  public:
+  DEFINE_SIZE_ARRAY (4, component);
+};
+
+struct LigatureSet
+{
+  friend struct LigatureSubstFormat1;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, bool is_mark) const
+  {
+    TRACE_APPLY ();
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+    {
+      const Ligature &lig = this+ligature[i];
+      if (lig.apply (c, is_mark))
+        return true;
+    }
+
+    return false;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return ligature.sanitize (c, this);
+  }
+
+  private:
+  OffsetArrayOf<Ligature>
+		ligature;		/* Array LigatureSet tables
+					 * ordered by preference */
+  public:
+  DEFINE_SIZE_ARRAY (2, ligature);
+};
+
+struct LigatureSubstFormat1
+{
+  friend struct LigatureSubst;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
+
+    bool first_is_mark = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const LigatureSet &lig_set = this+ligatureSet[index];
+    return lig_set.apply (c, first_is_mark);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& ligatureSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of Substitution table */
+  OffsetArrayOf<LigatureSet>
+		ligatureSet;		/* Array LigatureSet tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, ligatureSet);
+};
+
+struct LigatureSubst
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  LigatureSubstFormat1	format1;
+  } u;
+};
+
+
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+
+struct ContextSubst : Context
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return Context::apply (c, substitute_lookup);
+  }
+};
+
+struct ChainContextSubst : ChainContext
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return ChainContext::apply (c, substitute_lookup);
+  }
+};
+
+
+struct ExtensionSubst : Extension
+{
+  friend struct SubstLookupSubTable;
+  friend struct SubstLookup;
+
+  private:
+  inline const struct SubstLookupSubTable& get_subtable (void) const
+  {
+    unsigned int offset = get_offset ();
+    if (unlikely (!offset)) return Null(SubstLookupSubTable);
+    return StructAtOffset<SubstLookupSubTable> (this, offset);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const;
+
+  inline bool sanitize (hb_sanitize_context_t *c);
+
+  inline bool is_reverse (void) const;
+};
+
+
+struct ReverseChainSingleSubstFormat1
+{
+  friend struct ReverseChainSingleSubst;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    if (unlikely (c->context_length != NO_CONTEXT))
+      return false; /* No chaining to this type */
+
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+
+    if (match_backtrack (c,
+			 backtrack.len, (USHORT *) backtrack.array,
+			 match_coverage, this) &&
+        match_lookahead (c,
+			 lookahead.len, (USHORT *) lookahead.array,
+			 match_coverage, this,
+			 1))
+    {
+      c->buffer->info[c->buffer->i].codepoint = substitute[index];
+      c->buffer->i--; /* Reverse! */
+      return true;
+    }
+
+    return false;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!(coverage.sanitize (c, this)
+       && backtrack.sanitize (c, this)))
+      return false;
+    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    if (!lookahead.sanitize (c, this))
+      return false;
+    ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    return substitute.sanitize (c);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of table */
+  OffsetArrayOf<Coverage>
+		backtrack;		/* Array of coverage tables
+					 * in backtracking sequence, in  glyph
+					 * sequence order */
+  OffsetArrayOf<Coverage>
+		lookaheadX;		/* Array of coverage tables
+					 * in lookahead sequence, in glyph
+					 * sequence order */
+  ArrayOf<GlyphID>
+		substituteX;		/* Array of substitute
+					 * GlyphIDs--ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_MIN (10);
+};
+
+struct ReverseChainSingleSubst
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT				format;		/* Format identifier */
+  ReverseChainSingleSubstFormat1	format1;
+  } u;
+};
+
+
+
+/*
+ * SubstLookup
+ */
+
+struct SubstLookupSubTable
+{
+  friend struct SubstLookup;
+
+  enum {
+    Single		= 1,
+    Multiple		= 2,
+    Alternate		= 3,
+    Ligature		= 4,
+    Context		= 5,
+    ChainContext	= 6,
+    Extension		= 7,
+    ReverseChainSingle	= 8
+  };
+
+  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
+  {
+    TRACE_APPLY ();
+    switch (lookup_type) {
+    case Single:		return u.single.apply (c);
+    case Multiple:		return u.multiple.apply (c);
+    case Alternate:		return u.alternate.apply (c);
+    case Ligature:		return u.ligature.apply (c);
+    case Context:		return u.c.apply (c);
+    case ChainContext:		return u.chainContext.apply (c);
+    case Extension:		return u.extension.apply (c);
+    case ReverseChainSingle:	return u.reverseChainContextSingle.apply (c);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
+    TRACE_SANITIZE ();
+    switch (lookup_type) {
+    case Single:		return u.single.sanitize (c);
+    case Multiple:		return u.multiple.sanitize (c);
+    case Alternate:		return u.alternate.sanitize (c);
+    case Ligature:		return u.ligature.sanitize (c);
+    case Context:		return u.c.sanitize (c);
+    case ChainContext:		return u.chainContext.sanitize (c);
+    case Extension:		return u.extension.sanitize (c);
+    case ReverseChainSingle:	return u.reverseChainContextSingle.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT			sub_format;
+  SingleSubst			single;
+  MultipleSubst			multiple;
+  AlternateSubst		alternate;
+  LigatureSubst			ligature;
+  ContextSubst			c;
+  ChainContextSubst		chainContext;
+  ExtensionSubst		extension;
+  ReverseChainSingleSubst	reverseChainContextSingle;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, sub_format);
+};
+
+
+struct SubstLookup : Lookup
+{
+  inline const SubstLookupSubTable& get_subtable (unsigned int i) const
+  { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
+
+  inline static bool lookup_type_is_reverse (unsigned int lookup_type)
+  { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
+
+  inline bool is_reverse (void) const
+  {
+    unsigned int type = get_type ();
+    if (unlikely (type == SubstLookupSubTable::Extension))
+      return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
+    return lookup_type_is_reverse (type);
+  }
+
+
+  inline bool apply_once (hb_ot_layout_context_t *layout,
+			  hb_buffer_t *buffer,
+			  hb_mask_t lookup_mask,
+			  unsigned int context_length,
+			  unsigned int nesting_level_left) const
+  {
+    unsigned int lookup_type = get_type ();
+    hb_apply_context_t c[1] = {{0}};
+
+    c->layout = layout;
+    c->buffer = buffer;
+    c->lookup_mask = lookup_mask;
+    c->context_length = context_length;
+    c->nesting_level_left = nesting_level_left;
+    c->lookup_flag = get_flag ();
+
+    if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
+      return false;
+
+    if (unlikely (lookup_type == SubstLookupSubTable::Extension))
+    {
+      /* The spec says all subtables should have the same type.
+       * This is specially important if one has a reverse type!
+       *
+       * This is rather slow to do this here for every glyph,
+       * but it's easiest, and who uses extension lookups anyway?!*/
+      unsigned int count = get_subtable_count ();
+      unsigned int type = get_subtable(0).u.extension.get_type ();
+      for (unsigned int i = 1; i < count; i++)
+        if (get_subtable(i).u.extension.get_type () != type)
+	  return false;
+    }
+
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (get_subtable (i).apply (c, lookup_type))
+	return true;
+
+    return false;
+  }
+
+  inline bool apply_string (hb_ot_layout_context_t *layout,
+			    hb_buffer_t *buffer,
+			    hb_mask_t    mask) const
+  {
+    bool ret = false;
+
+    if (unlikely (!buffer->len))
+      return false;
+
+    if (likely (!is_reverse ()))
+    {
+	/* in/out forward substitution */
+	buffer->clear_output ();
+	buffer->i = 0;
+	while (buffer->i < buffer->len)
+	{
+	  if ((buffer->info[buffer->i].mask & mask) &&
+	      apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
+	    ret = true;
+	  else
+	    buffer->next_glyph ();
+
+	}
+	if (ret)
+	  buffer->swap ();
+    }
+    else
+    {
+	/* in-place backward substitution */
+	buffer->i = buffer->len - 1;
+	do
+	{
+	  if ((buffer->info[buffer->i].mask & mask) &&
+	      apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
+	    ret = true;
+	  else
+	    buffer->i--;
+
+	}
+	while ((int) buffer->i >= 0);
+    }
+
+    return ret;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!Lookup::sanitize (c))) return false;
+    OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
+    return list.sanitize (c, this, get_type ());
+  }
+};
+
+typedef OffsetListOf<SubstLookup> SubstLookupList;
+
+/*
+ * GSUB
+ */
+
+struct GSUB : GSUBGPOS
+{
+  static const hb_tag_t Tag	= HB_OT_TAG_GSUB;
+
+  inline const SubstLookup& get_lookup (unsigned int i) const
+  { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
+
+  inline bool substitute_lookup (hb_ot_layout_context_t *layout,
+				 hb_buffer_t  *buffer,
+			         unsigned int  lookup_index,
+				 hb_mask_t     mask) const
+  { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+    OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
+    return list.sanitize (c, this);
+  }
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+
+/* Out-of-class implementation for methods recursing */
+
+inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
+{
+  TRACE_APPLY ();
+  return get_subtable ().apply (c, get_type ());
+}
+
+inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
+{
+  TRACE_SANITIZE ();
+  if (unlikely (!Extension::sanitize (c))) return false;
+  unsigned int offset = get_offset ();
+  if (unlikely (!offset)) return true;
+  return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
+}
+
+inline bool ExtensionSubst::is_reverse (void) const
+{
+  unsigned int type = get_type ();
+  if (unlikely (type == SubstLookupSubTable::Extension))
+    return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
+  return SubstLookup::lookup_type_is_reverse (type);
+}
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+{
+  const GSUB &gsub = *(c->layout->face->ot_layout->gsub);
+  const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+  if (unlikely (c->nesting_level_left == 0))
+    return false;
+
+  if (unlikely (c->context_length < 1))
+    return false;
+
+  return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
+}
+
+
+#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -0,0 +1,942 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
+#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
+
+#include "hb-buffer-private.hh"
+#include "hb-ot-layout-gdef-private.hh"
+
+
+#ifndef HB_DEBUG_APPLY
+#define HB_DEBUG_APPLY HB_DEBUG+0
+#endif
+
+#define TRACE_APPLY() \
+	hb_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", HB_FUNC, this); \
+
+
+struct hb_apply_context_t
+{
+  unsigned int debug_depth;
+  hb_ot_layout_context_t *layout;
+  hb_buffer_t *buffer;
+  hb_mask_t lookup_mask;
+  unsigned int context_length;
+  unsigned int nesting_level_left;
+  unsigned int lookup_flag;
+  unsigned int property; /* propety of first glyph (TODO remove) */
+};
+
+
+
+typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
+
+struct ContextFuncs
+{
+  match_func_t match;
+  apply_lookup_func_t apply;
+};
+
+
+static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
+{
+  return glyph_id == value;
+}
+
+static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
+{
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  return class_def.get_class (glyph_id) == value;
+}
+
+static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
+{
+  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+  return (data+coverage) (glyph_id) != NOT_COVERED;
+}
+
+
+static inline bool match_input (hb_apply_context_t *c,
+				unsigned int count, /* Including the first glyph (not matched) */
+				const USHORT input[], /* Array of input values--start with second glyph */
+				match_func_t match_func,
+				const void *match_data,
+				unsigned int *context_length_out)
+{
+  unsigned int i, j;
+  unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+  if (unlikely (c->buffer->i + count > end))
+    return false;
+
+  for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
+  {
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
+    {
+      if (unlikely (j + count - i == end))
+	return false;
+      j++;
+    }
+
+    if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data)))
+      return false;
+  }
+
+  *context_length_out = j - c->buffer->i;
+
+  return true;
+}
+
+static inline bool match_backtrack (hb_apply_context_t *c,
+				    unsigned int count,
+				    const USHORT backtrack[],
+				    match_func_t match_func,
+				    const void *match_data)
+{
+  if (unlikely (c->buffer->out_len < count))
+    return false;
+
+  for (unsigned int i = 0, j = c->buffer->out_len - 1; i < count; i++, j--)
+  {
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->out_info[j], c->lookup_flag, NULL))
+    {
+      if (unlikely (j + 1 == count - i))
+	return false;
+      j--;
+    }
+
+    if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data)))
+      return false;
+  }
+
+  return true;
+}
+
+static inline bool match_lookahead (hb_apply_context_t *c,
+				    unsigned int count,
+				    const USHORT lookahead[],
+				    match_func_t match_func,
+				    const void *match_data,
+				    unsigned int offset)
+{
+  unsigned int i, j;
+  unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+  if (unlikely (c->buffer->i + offset + count > end))
+    return false;
+
+  for (i = 0, j = c->buffer->i + offset; i < count; i++, j++)
+  {
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
+    {
+      if (unlikely (j + count - i == end))
+	return false;
+      j++;
+    }
+
+    if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data)))
+      return false;
+  }
+
+  return true;
+}
+
+
+struct LookupRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return c->check_struct (this);
+  }
+
+  USHORT	sequenceIndex;		/* Index into current glyph
+					 * sequence--first glyph = 0 */
+  USHORT	lookupListIndex;	/* Lookup to apply to that
+					 * position--zero--based */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+static inline bool apply_lookup (hb_apply_context_t *c,
+				 unsigned int count, /* Including the first glyph */
+				 unsigned int lookupCount,
+				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
+				 apply_lookup_func_t apply_func)
+{
+  unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+  if (unlikely (c->buffer->i + count > end))
+    return false;
+
+  /* TODO We don't support lookupRecord arrays that are not increasing:
+   *      Should be easy for in_place ones at least. */
+
+  /* Note: If sublookup is reverse, i will underflow after the first loop
+   * and we jump out of it.  Not entirely disastrous.  So we don't check
+   * for reverse lookup here.
+   */
+  for (unsigned int i = 0; i < count; /* NOP */)
+  {
+    while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, NULL))
+    {
+      if (unlikely (c->buffer->i == end))
+	return true;
+      /* No lookup applied for this index */
+      c->buffer->next_glyph ();
+    }
+
+    if (lookupCount && i == lookupRecord->sequenceIndex)
+    {
+      unsigned int old_pos = c->buffer->i;
+
+      /* Apply a lookup */
+      bool done = apply_func (c, lookupRecord->lookupListIndex);
+
+      lookupRecord++;
+      lookupCount--;
+      /* Err, this is wrong if the lookup jumped over some glyphs */
+      i += c->buffer->i - old_pos;
+      if (unlikely (c->buffer->i == end))
+	return true;
+
+      if (!done)
+	goto not_applied;
+    }
+    else
+    {
+    not_applied:
+      /* No lookup applied for this index */
+      c->buffer->next_glyph ();
+      i++;
+    }
+  }
+
+  return true;
+}
+
+
+/* Contextual lookups */
+
+struct ContextLookupContext
+{
+  ContextFuncs funcs;
+  const void *match_data;
+};
+
+static inline bool context_lookup (hb_apply_context_t *c,
+				   unsigned int inputCount, /* Including the first glyph (not matched) */
+				   const USHORT input[], /* Array of input values--start with second glyph */
+				   unsigned int lookupCount,
+				   const LookupRecord lookupRecord[],
+				   ContextLookupContext &lookup_context)
+{
+  hb_apply_context_t new_context = *c;
+  return match_input (c,
+		      inputCount, input,
+		      lookup_context.funcs.match, lookup_context.match_data,
+		      &new_context.context_length)
+      && apply_lookup (&new_context,
+		       inputCount,
+		       lookupCount, lookupRecord,
+		       lookup_context.funcs.apply);
+}
+
+struct Rule
+{
+  friend struct RuleSet;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+  {
+    TRACE_APPLY ();
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return context_lookup (c,
+			   inputCount, input,
+			   lookupCount, lookupRecord,
+			   lookup_context);
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return inputCount.sanitize (c)
+	&& lookupCount.sanitize (c)
+	&& c->check_range (input,
+				 input[0].static_size * inputCount
+				 + lookupRecordX[0].static_size * lookupCount);
+  }
+
+  private:
+  USHORT	inputCount;		/* Total number of glyphs in input
+					 * glyph sequence--includes the  first
+					 * glyph */
+  USHORT	lookupCount;		/* Number of LookupRecords */
+  USHORT	input[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);
+};
+
+struct RuleSet
+{
+  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+  {
+    TRACE_APPLY ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+    {
+      if ((this+rule[i]).apply (c, lookup_context))
+        return true;
+    }
+
+    return false;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return rule.sanitize (c, this);
+  }
+
+  private:
+  OffsetArrayOf<Rule>
+		rule;			/* Array of Rule tables
+					 * ordered by preference */
+  public:
+  DEFINE_SIZE_ARRAY (2, rule);
+};
+
+
+struct ContextFormat1
+{
+  friend struct Context;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const RuleSet &rule_set = this+ruleSet[index];
+    struct ContextLookupContext lookup_context = {
+      {match_glyph, apply_func},
+      NULL
+    };
+    return rule_set.apply (c, lookup_context);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& ruleSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of table */
+  OffsetArrayOf<RuleSet>
+		ruleSet;		/* Array of RuleSet tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, ruleSet);
+};
+
+
+struct ContextFormat2
+{
+  friend struct Context;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const ClassDef &class_def = this+classDef;
+    index = class_def (c->buffer->info[c->buffer->i].codepoint);
+    const RuleSet &rule_set = this+ruleSet[index];
+    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
+     * them across subrule lookups.  Not sure it's worth it.
+     */
+    struct ContextLookupContext lookup_context = {
+      {match_class, apply_func},
+      &class_def
+    };
+    return rule_set.apply (c, lookup_context);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+        && classDef.sanitize (c, this)
+	&& ruleSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of table */
+  OffsetTo<ClassDef>
+		classDef;		/* Offset to glyph ClassDef table--from
+					 * beginning of table */
+  OffsetArrayOf<RuleSet>
+		ruleSet;		/* Array of RuleSet tables
+					 * ordered by class */
+  public:
+  DEFINE_SIZE_ARRAY (8, ruleSet);
+};
+
+
+struct ContextFormat3
+{
+  friend struct Context;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextLookupContext lookup_context = {
+      {match_coverage, apply_func},
+      this
+    };
+    return context_lookup (c,
+			   glyphCount, (const USHORT *) (coverage + 1),
+			   lookupCount, lookupRecord,
+			   lookup_context);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!c->check_struct (this)) return false;
+    unsigned int count = glyphCount;
+    if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
+    for (unsigned int i = 0; i < count; i++)
+      if (!coverage[i].sanitize (c, this)) return false;
+    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
+    return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 3 */
+  USHORT	glyphCount;		/* Number of glyphs in the input glyph
+					 * sequence */
+  USHORT	lookupCount;		/* Number of LookupRecords */
+  OffsetTo<Coverage>
+		coverage[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);
+};
+
+struct Context
+{
+  protected:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return u.format1.apply (c, apply_func);
+    case 2: return u.format2.apply (c, apply_func);
+    case 3: return u.format3.apply (c, apply_func);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return false;
+    switch (u.format) {
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    case 3: return u.format3.sanitize (c);
+    default:return true;
+    }
+  }
+
+  private:
+  union {
+  USHORT		format;		/* Format identifier */
+  ContextFormat1	format1;
+  ContextFormat2	format2;
+  ContextFormat3	format3;
+  } u;
+};
+
+
+/* Chaining Contextual lookups */
+
+struct ChainContextLookupContext
+{
+  ContextFuncs funcs;
+  const void *match_data[3];
+};
+
+static inline bool chain_context_lookup (hb_apply_context_t *c,
+					 unsigned int backtrackCount,
+					 const USHORT backtrack[],
+					 unsigned int inputCount, /* Including the first glyph (not matched) */
+					 const USHORT input[], /* Array of input values--start with second glyph */
+					 unsigned int lookaheadCount,
+					 const USHORT lookahead[],
+					 unsigned int lookupCount,
+					 const LookupRecord lookupRecord[],
+					 ChainContextLookupContext &lookup_context)
+{
+  /* First guess */
+  if (unlikely (c->buffer->out_len < backtrackCount ||
+		c->buffer->i + inputCount + lookaheadCount > c->buffer->len ||
+		inputCount + lookaheadCount > c->context_length))
+    return false;
+
+  hb_apply_context_t new_context = *c;
+  return match_backtrack (c,
+			  backtrackCount, backtrack,
+			  lookup_context.funcs.match, lookup_context.match_data[0])
+      && match_input (c,
+		      inputCount, input,
+		      lookup_context.funcs.match, lookup_context.match_data[1],
+		      &new_context.context_length)
+      && match_lookahead (c,
+			  lookaheadCount, lookahead,
+			  lookup_context.funcs.match, lookup_context.match_data[2],
+			  new_context.context_length)
+      && apply_lookup (&new_context,
+		       inputCount,
+		       lookupCount, lookupRecord,
+		       lookup_context.funcs.apply);
+}
+
+struct ChainRule
+{
+  friend struct ChainRuleSet;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+  {
+    TRACE_APPLY ();
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    return chain_context_lookup (c,
+				 backtrack.len, backtrack.array,
+				 input.len, input.array,
+				 lookahead.len, lookahead.array,
+				 lookup.len, lookup.array,
+				 lookup_context);
+    return false;
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!backtrack.sanitize (c)) return false;
+    HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    if (!input.sanitize (c)) return false;
+    ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    if (!lookahead.sanitize (c)) return false;
+    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    return lookup.sanitize (c);
+  }
+
+  private:
+  ArrayOf<USHORT>
+		backtrack;		/* Array of backtracking values
+					 * (to be matched before the input
+					 * sequence) */
+  HeadlessArrayOf<USHORT>
+		inputX;			/* Array of input values (start with
+					 * second glyph) */
+  ArrayOf<USHORT>
+		lookaheadX;		/* Array of lookahead values's (to be
+					 * matched after the input sequence) */
+  ArrayOf<LookupRecord>
+		lookupX;		/* Array of LookupRecords--in
+					 * design order) */
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+struct ChainRuleSet
+{
+  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+  {
+    TRACE_APPLY ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+    {
+      if ((this+rule[i]).apply (c, lookup_context))
+        return true;
+    }
+
+    return false;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return rule.sanitize (c, this);
+  }
+
+  private:
+  OffsetArrayOf<ChainRule>
+		rule;			/* Array of ChainRule tables
+					 * ordered by preference */
+  public:
+  DEFINE_SIZE_ARRAY (2, rule);
+};
+
+struct ChainContextFormat1
+{
+  friend struct ChainContext;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const ChainRuleSet &rule_set = this+ruleSet[index];
+    struct ChainContextLookupContext lookup_context = {
+      {match_glyph, apply_func},
+      {NULL, NULL, NULL}
+    };
+    return rule_set.apply (c, lookup_context);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& ruleSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 1 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of table */
+  OffsetArrayOf<ChainRuleSet>
+		ruleSet;		/* Array of ChainRuleSet tables
+					 * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (6, ruleSet);
+};
+
+struct ChainContextFormat2
+{
+  friend struct ChainContext;
+
+  private:
+  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  {
+    TRACE_APPLY ();
+    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+    if (likely (index == NOT_COVERED))
+      return false;
+
+    const ClassDef &backtrack_class_def = this+backtrackClassDef;
+    const ClassDef &input_class_def = this+inputClassDef;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    index = input_class_def (c->buffer->info[c->buffer->i].codepoint);
+    const ChainRuleSet &rule_set = this+ruleSet[index];
+    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
+     * them across subrule lookups.  Not sure it's worth it.
+     */
+    struct ChainContextLookupContext lookup_context = {
+      {match_class, apply_func},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+    return rule_set.apply (c, lookup_context);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return coverage.sanitize (c, this)
+	&& backtrackClassDef.sanitize (c, this)
+	&& inputClassDef.sanitize (c, this)
+	&& lookaheadClassDef.sanitize (c, this)
+	&& ruleSet.sanitize (c, this);
+  }
+
+  private:
+  USHORT	format;			/* Format identifier--format = 2 */
+  OffsetTo<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of table */
+  OffsetTo<ClassDef>
+		backtrackClassDef;	/* Offset to glyph ClassDef table
+					 * containing backtrack sequence
+					 * data--from beginning of table */
+  OffsetTo<ClassDef>
+		inputClassDef;		/* Offset to glyph ClassDef
+					 * table containing input sequence
+					 * data--from beginning of table */
+  OffsetTo<ClassDef>
+		lookaheadClassDef;	/* Offset to glyph ClassDef table
+					 * containing lookahead sequence
+					 * data--from beginning of table */