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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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