summaryrefslogtreecommitdiffstats
path: root/third_party/harfbuzz-ng
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-05 20:38:50 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-05 20:38:50 +0000
commit29c2daeafac577bd80c7b37891e3bbae7c542b5e (patch)
tree3ba8adb929982562db6c0b37c15c00ada32b41da /third_party/harfbuzz-ng
parent678a7d737ffd81978c981d692972695e4ad7f394 (diff)
downloadchromium_src-29c2daeafac577bd80c7b37891e3bbae7c542b5e.zip
chromium_src-29c2daeafac577bd80c7b37891e3bbae7c542b5e.tar.gz
chromium_src-29c2daeafac577bd80c7b37891e3bbae7c542b5e.tar.bz2
harfbuzz: check in harfbuzz-ng, add gyp define to use it
This checks in harbuzz-ng b0d396aa88b3cdf8cea896bfeeba197656e1cdb1. Setting use_harfbuzz_ng to 1 in gyp will cause us to build harfbuzz-ng in place of harfbuzz. So far this currently fails to compile due to all our other code relying on the old API. BUG=68551 TEST=./build/gyp_chromium -Duse_harfbuzz_ng=1, verify build breaks Review URL: http://codereview.chromium.org/6052008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70539 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/harfbuzz-ng')
-rw-r--r--third_party/harfbuzz-ng/COPYING29
-rw-r--r--third_party/harfbuzz-ng/README9
-rw-r--r--third_party/harfbuzz-ng/README.chromium9
-rw-r--r--third_party/harfbuzz-ng/harfbuzz.gyp73
-rw-r--r--third_party/harfbuzz-ng/src/Makefile.am147
-rwxr-xr-xthird_party/harfbuzz-ng/src/check-c-linkage-decls.sh18
-rwxr-xr-xthird_party/harfbuzz-ng/src/check-header-guards.sh20
-rwxr-xr-xthird_party/harfbuzz-ng/src/check-internal-symbols.sh28
-rwxr-xr-xthird_party/harfbuzz-ng/src/check-libstdc++.sh27
-rwxr-xr-xthird_party/harfbuzz-ng/src/gen-arabic-joining-table.py83
-rw-r--r--third_party/harfbuzz-ng/src/hb-blob-private.h59
-rw-r--r--third_party/harfbuzz-ng/src/hb-blob.c362
-rw-r--r--third_party/harfbuzz-ng/src/hb-blob.h89
-rw-r--r--third_party/harfbuzz-ng/src/hb-buffer-private.hh146
-rw-r--r--third_party/harfbuzz-ng/src/hb-buffer.cc630
-rw-r--r--third_party/harfbuzz-ng/src/hb-buffer.h163
-rw-r--r--third_party/harfbuzz-ng/src/hb-common.c47
-rw-r--r--third_party/harfbuzz-ng/src/hb-common.h106
-rw-r--r--third_party/harfbuzz-ng/src/hb-font-private.h97
-rw-r--r--third_party/harfbuzz-ng/src/hb-font.cc583
-rw-r--r--third_party/harfbuzz-ng/src/hb-font.h270
-rw-r--r--third_party/harfbuzz-ng/src/hb-ft.c262
-rw-r--r--third_party/harfbuzz-ng/src/hb-ft.h58
-rw-r--r--third_party/harfbuzz-ng/src/hb-glib.c64
-rw-r--r--third_party/harfbuzz-ng/src/hb-glib.h41
-rw-r--r--third_party/harfbuzz-ng/src/hb-graphite.cc310
-rw-r--r--third_party/harfbuzz-ng/src/hb-graphite.h47
-rw-r--r--third_party/harfbuzz-ng/src/hb-icu.c260
-rw-r--r--third_party/harfbuzz-ng/src/hb-icu.h41
-rw-r--r--third_party/harfbuzz-ng/src/hb-language.c120
-rw-r--r--third_party/harfbuzz-ng/src/hb-language.h46
-rw-r--r--third_party/harfbuzz-ng/src/hb-object-private.h141
-rw-r--r--third_party/harfbuzz-ng/src/hb-open-file-private.hh258
-rw-r--r--third_party/harfbuzz-ng/src/hb-open-type-private.hh727
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-head-private.hh146
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh604
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-gdef-private.hh429
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-gpos-private.hh1588
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-gsub-private.hh931
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh981
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout-private.hh114
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout.cc485
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-layout.h196
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-map-private.hh143
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-map.cc194
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-table.h674
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc198
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape-complex-private.hh97
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape-private.hh76
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape.cc382
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-shape.h46
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-tag.c703
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot-tag.h54
-rw-r--r--third_party/harfbuzz-ng/src/hb-ot.h39
-rw-r--r--third_party/harfbuzz-ng/src/hb-private.h270
-rw-r--r--third_party/harfbuzz-ng/src/hb-shape.cc65
-rw-r--r--third_party/harfbuzz-ng/src/hb-shape.h54
-rw-r--r--third_party/harfbuzz-ng/src/hb-unicode-private.h64
-rw-r--r--third_party/harfbuzz-ng/src/hb-unicode.c364
-rw-r--r--third_party/harfbuzz-ng/src/hb-unicode.h294
-rw-r--r--third_party/harfbuzz-ng/src/hb.h41
-rw-r--r--third_party/harfbuzz-ng/src/main.cc196
-rw-r--r--third_party/harfbuzz-ng/src/test.c94
63 files changed, 14892 insertions, 0 deletions
diff --git a/third_party/harfbuzz-ng/COPYING b/third_party/harfbuzz-ng/COPYING
new file mode 100644
index 0000000..3fb9e51
--- /dev/null
+++ b/third_party/harfbuzz-ng/COPYING
@@ -0,0 +1,29 @@
+HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
+
+Copyright (C) 2010 Google, Inc.
+Copyright (C) 2006 Behdad Esfahbod
+Copyright (C) 2009 Keith Stribley
+Copyright (C) 2009 Martin Hosken and SIL International
+Copyright (C) 2007 Chris Wilson
+Copyright (C) 2004,2007,2008,2009,2010 Red Hat, Inc.
+Copyright (C) 1998-2004 David Turner and Werner Lemberg
+For full copyright notices consult the individual files in the package.
+
+
+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.
diff --git a/third_party/harfbuzz-ng/README b/third_party/harfbuzz-ng/README
new file mode 100644
index 0000000..06aad34
--- /dev/null
+++ b/third_party/harfbuzz-ng/README
@@ -0,0 +1,9 @@
+This is HarfBuzz, a text shaping library.
+
+Bug reports on these files should be sent to the HarfBuzz mailing list as
+listed on http://harfbuzz.org/
+
+For license information, see the file COPYING.
+
+Behdad Esfahbod
+18 November 2010
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
new file mode 100644
index 0000000..e656355
--- /dev/null
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -0,0 +1,9 @@
+Name: HarfBuzz
+URL: http://freedesktop.org/wiki/Software/HarfBuzz
+
+This is harfbuzz-ng, a new implementation of harfbuzz with a different
+API from the old one.
+
+This code was taken from b0d396aa88b3cdf8cea896bfeeba197656e1cdb1
+(git://anongit.freedesktop.org/harfbuzz). I also deleted all
+unneeded files from the root directory (but left src/ alone).
diff --git a/third_party/harfbuzz-ng/harfbuzz.gyp b/third_party/harfbuzz-ng/harfbuzz.gyp
new file mode 100644
index 0000000..08bbb14
--- /dev/null
+++ b/third_party/harfbuzz-ng/harfbuzz.gyp
@@ -0,0 +1,73 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'harfbuzz',
+ 'type': '<(library)',
+ 'sources': [
+ 'src/hb-blob-private.h',
+ 'src/hb-blob.c',
+ 'src/hb-blob.h',
+ 'src/hb-buffer-private.hh',
+ 'src/hb-buffer.cc',
+ 'src/hb-buffer.h',
+ 'src/hb-common.c',
+ 'src/hb-common.h',
+ 'src/hb-font-private.h',
+ 'src/hb-font.cc',
+ 'src/hb-font.h',
+ 'src/hb-ft.c',
+ 'src/hb-icu.c',
+ 'src/hb-language.c',
+ 'src/hb-language.h',
+ 'src/hb-object-private.h',
+ 'src/hb-open-file-private.hh',
+ 'src/hb-open-type-private.hh',
+ 'src/hb-ot-head-private.hh',
+ 'src/hb-ot-layout-common-private.hh',
+ 'src/hb-ot-layout-gdef-private.hh',
+ 'src/hb-ot-layout-gpos-private.hh',
+ 'src/hb-ot-layout-gsub-private.hh',
+ 'src/hb-ot-layout-gsubgpos-private.hh',
+ 'src/hb-ot-layout-private.hh',
+ 'src/hb-ot-layout.cc',
+ 'src/hb-ot-map-private.hh',
+ 'src/hb-ot-map.cc',
+ 'src/hb-ot-shape-complex-arabic-table.h',
+ 'src/hb-ot-shape-complex-arabic.cc',
+ 'src/hb-ot-shape-complex-private.hh',
+ 'src/hb-ot-shape-private.hh',
+ 'src/hb-ot-shape.cc',
+ 'src/hb-ot-tag.c',
+ 'src/hb-private.h',
+ 'src/hb-shape.cc',
+ 'src/hb-shape.h',
+ 'src/hb-unicode-private.h',
+ 'src/hb-unicode.c',
+ 'src/hb-unicode.h',
+ 'src/hb.h',
+ ],
+ 'include_dirs': [
+ 'src',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'src',
+ ],
+ },
+ 'dependencies': [
+ '../../build/linux/system.gyp:freetype2',
+ '../../third_party/icu/icu.gyp:icuuc',
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/third_party/harfbuzz-ng/src/Makefile.am b/third_party/harfbuzz-ng/src/Makefile.am
new file mode 100644
index 0000000..13107b9
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/Makefile.am
@@ -0,0 +1,147 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+
+# 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-common.c \
+ hb-font.cc \
+ hb-font-private.h \
+ 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-map.cc \
+ hb-ot-map-private.hh \
+ hb-ot-shape.cc \
+ hb-ot-shape-complex-arabic.cc \
+ hb-ot-shape-complex-arabic-table.h \
+ hb-ot-shape-complex-private.hh \
+ hb-ot-shape-private.hh \
+ hb-ot-tag.c \
+ $(NULL)
+HBHEADERS += \
+ hb-ot.h \
+ hb-ot-layout.h \
+ hb-ot-shape.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.c \
+ $(NULL)
+HBHEADERS += \
+ hb-ft.h \
+ $(NULL)
+endif
+
+if HAVE_GRAPHITE
+HBCFLAGS += $(GRAPHITE_CFLAGS)
+HBLIBS += $(GRAPHITE_LIBS)
+HBSOURCES += \
+ hb-graphite.cc \
+ $(NULL)
+HBHEADERS += \
+ hb-graphite.h \
+ $(NULL)
+endif
+
+CXXLINK = $(LINK)
+libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
+libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
+libharfbuzz_la_LIBADD = $(HBLIBS)
+pkginclude_HEADERS = $(HBHEADERS)
+
+
+GENERATORS = \
+ gen-arabic-joining-table.py \
+ $(NULL)
+
+EXTRA_DIST += $(GENERATORS)
+
+noinst_PROGRAMS = main test
+
+main_SOURCES = main.cc
+main_CPPFLAGS = $(HBCFLAGS)
+main_LDADD = libharfbuzz.la $(HBLIBS)
+
+test_SOURCES = test.c
+test_CPPFLAGS = $(HBCFLAGS)
+test_LDADD = libharfbuzz.la $(HBLIBS)
+
+TESTS = \
+ check-c-linkage-decls.sh \
+ check-header-guards.sh \
+ check-internal-symbols.sh \
+ $(NULL)
+
+if HAVE_ICU
+else
+if HAVE_GRAPHITE
+else
+TESTS += check-libstdc++.sh
+endif
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/third_party/harfbuzz-ng/src/check-c-linkage-decls.sh b/third_party/harfbuzz-ng/src/check-c-linkage-decls.sh
new file mode 100755
index 0000000..711d089
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/check-c-linkage-decls.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+cd "$srcdir"
+
+for x in hb-*.c hb-*.cc hb-*.h hb-*.hh ; do
+ if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
+ echo "Ouch, file $x does not HB_BEGIN_DECLS / HB_END_DECLS"
+ stat=1
+ fi
+done
+
+exit $stat
diff --git a/third_party/harfbuzz-ng/src/check-header-guards.sh b/third_party/harfbuzz-ng/src/check-header-guards.sh
new file mode 100755
index 0000000..c966cd2
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/check-header-guards.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+cd "$srcdir"
+
+for x in hb-*.h hb-*.hh ; do
+ tag=`echo "$x" | tr 'a-z.-' 'A-Z_'`
+ lines=`grep "\<$tag\>" "$x" | wc -l`
+ if test "x$lines" != x3; then
+ echo "Ouch, header file $x does not have correct preprocessor guards"
+ stat=1
+ fi
+done
+
+exit $stat
diff --git a/third_party/harfbuzz-ng/src/check-internal-symbols.sh b/third_party/harfbuzz-ng/src/check-internal-symbols.sh
new file mode 100755
index 0000000..124a7b0
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/check-internal-symbols.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which nm 2>/dev/null >/dev/null; then
+ :
+else
+ echo "check-internal-symbols.sh: 'nm' 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 exposing internal symbols"
+ if nm $so | grep ' T ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then
+ echo "Ouch, internal symbols exposed"
+ stat=1
+ fi
+else
+ echo "check-internal-symbols.sh: libharfbuzz.so not found; skipping test"
+fi
+
+exit $stat
diff --git a/third_party/harfbuzz-ng/src/check-libstdc++.sh b/third_party/harfbuzz-ng/src/check-libstdc++.sh
new file mode 100755
index 0000000..c0abcbe4
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/check-libstdc++.sh
@@ -0,0 +1,27 @@
+#!/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=.
+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
diff --git a/third_party/harfbuzz-ng/src/gen-arabic-joining-table.py b/third_party/harfbuzz-ng/src/gen-arabic-joining-table.py
new file mode 100755
index 0000000..08e54db
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/gen-arabic-joining-table.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+
+import sys
+
+header = sys.stdin.readline (), sys.stdin.readline ()
+while sys.stdin.readline ().find ('##################') < 0:
+ pass
+
+
+print "/* == Start of generated table == */"
+print "/*"
+print " * The following table is generated by running:"
+print " *"
+print " * ./gen-arabic-joining-table.py < ArabicShaping.txt"
+print " *"
+print " * on the ArabicShaping.txt file with the header:"
+print " *"
+for line in header:
+ print " * %s" % (line.strip())
+print " */"
+
+print "static const uint8_t joining_table[] ="
+print "{"
+
+
+min_u = 0x110000
+max_u = 0
+num = 0
+last = -1
+block = ''
+for line in sys.stdin:
+
+ if line[0] == '#':
+ if line.find (" characters"):
+ block = line[2:].strip ()
+ continue
+
+ fields = [x.strip () for x in line.split (';')]
+ if len (fields) == 1:
+ continue
+
+ u = int (fields[0], 16)
+ if u == 0x200C or u == 0x200D:
+ continue
+ if u < last:
+ raise Exception ("Input data character not sorted", u)
+ min_u = min (min_u, u)
+ max_u = max (max_u, u)
+ num += 1
+
+ if block:
+ print "\n /* %s */\n" % block
+ block = ''
+
+ if last != -1:
+ last += 1
+ while last < u:
+ print " JOINING_TYPE_X, /* %04X */" % last
+ last += 1
+ else:
+ last = u
+
+ if fields[3] in ["ALAPH", "DALATH RISH"]:
+ value = "JOINING_GROUP_" + fields[3].replace(' ', '_')
+ else:
+ value = "JOINING_TYPE_" + fields[2]
+ print " %s, /* %s */" % (value, '; '.join(fields))
+
+print
+print " JOINING_TYPE_X /* dummy */"
+print "};"
+print
+
+print "#define JOINING_TABLE_FIRST 0x%04x" % min_u
+print "#define JOINING_TABLE_LAST 0x%04x" % max_u
+print
+
+print "/* == End of generated table == */"
+
+occupancy = num * 100 / (max_u - min_u + 1)
+# Maintain at least 40% occupancy in the table */
+if occupancy < 40:
+ raise Exception ("Table too sparse, please investigate: ", occupancy)
diff --git a/third_party/harfbuzz-ng/src/hb-blob-private.h b/third_party/harfbuzz-ng/src/hb-blob-private.h
new file mode 100644
index 0000000..92109ed
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-blob-private.h
@@ -0,0 +1,59 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-blob.c b/third_party/harfbuzz-ng/src/hb-blob.c
new file mode 100644
index 0000000..37e7787
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-blob.c
@@ -0,0 +1,362 @@
+/*
+ * 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 */
+
+#include <stdio.h>
+#include <errno.h>
+
+HB_BEGIN_DECLS
+
+
+#ifndef HB_DEBUG_BLOB
+#define HB_DEBUG_BLOB (HB_DEBUG+0)
+#endif
+
+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);
+
+ (void) (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);
+
+ (void) (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) {
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: failed to get pagesize: %s\n", blob, __FUNCTION__, strerror (errno)));
+ return FALSE;
+ }
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: pagesize is %lu\n", blob, __FUNCTION__, (unsigned long) 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;
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: calling mprotect on [%p..%p] (%lu bytes)\n",
+ blob, __FUNCTION__,
+ addr, addr+length, (unsigned long) length));
+ if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: %s\n", blob, __FUNCTION__, strerror (errno)));
+ return FALSE;
+ }
+
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: successfully made [%p..%p] (%lu bytes) writable\n",
+ blob, __FUNCTION__,
+ addr, addr+length, (unsigned long) length));
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static void
+try_writable_inplace_locked (hb_blob_t *blob)
+{
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: making writable\n", blob, __FUNCTION__));
+
+ if (_try_make_writable_inplace_unix_locked (blob)) {
+ (void) (HB_DEBUG_BLOB &&
+ fprintf (stderr, "%p %s: making writable -> succeeded\n", blob, __FUNCTION__));
+ blob->mode = HB_MEMORY_MODE_WRITABLE;
+ } else {
+ (void) (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;
+
+ (void) (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) {
+ (void) (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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-blob.h b/third_party/harfbuzz-ng/src/hb-blob.h
new file mode 100644
index 0000000..dbbfc90
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-blob.h
@@ -0,0 +1,89 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-buffer-private.hh b/third_party/harfbuzz-ng/src/hb-buffer-private.hh
new file mode 100644
index 0000000..a129165
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-buffer-private.hh
@@ -0,0 +1,146 @@
+/*
+ * 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_HH
+#define HB_BUFFER_PRIVATE_HH
+
+#include "hb-private.h"
+#include "hb-buffer.h"
+#include "hb-unicode-private.h"
+
+HB_BEGIN_DECLS
+
+
+ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
+ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
+
+typedef struct _hb_segment_properties_t {
+ hb_direction_t direction;
+ hb_script_t script;
+ hb_language_t language;
+} hb_segment_properties_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_replace_glyphs_be16 (hb_buffer_t *buffer,
+ unsigned int num_in,
+ unsigned int num_out,
+ const uint16_t *glyph_data_be);
+
+HB_INTERNAL void
+_hb_buffer_replace_glyph (hb_buffer_t *buffer,
+ hb_codepoint_t glyph_index);
+
+HB_INTERNAL void
+_hb_buffer_next_glyph (hb_buffer_t *buffer);
+
+
+HB_INTERNAL void
+_hb_buffer_reset_masks (hb_buffer_t *buffer,
+ hb_mask_t mask);
+
+HB_INTERNAL void
+_hb_buffer_add_masks (hb_buffer_t *buffer,
+ hb_mask_t mask);
+
+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; /* Unicode functions */
+ hb_segment_properties_t props; /* Script, language, direction */
+
+ /* 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_glyph_info_t *info;
+ hb_glyph_info_t *out_info;
+ hb_glyph_position_t *pos;
+
+ /* Other stuff */
+
+ unsigned int serial;
+
+
+ /* Methods */
+ inline unsigned int next_serial (void) { return serial++; }
+ 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 replace_glyphs_be16 (unsigned int num_in,
+ unsigned int num_out,
+ const uint16_t *glyph_data_be)
+ { _hb_buffer_replace_glyphs_be16 (this, num_in, num_out, glyph_data_be); }
+ inline void replace_glyph (hb_codepoint_t glyph_index)
+ { _hb_buffer_replace_glyph (this, glyph_index); }
+
+ inline void reset_masks (hb_mask_t mask)
+ {
+ for (unsigned int i = 0; i < len; i++)
+ info[i].mask = mask;
+ }
+ inline void add_masks (hb_mask_t mask)
+ {
+ for (unsigned int i = 0; i < len; i++)
+ info[i].mask |= mask;
+ }
+ 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_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-buffer.cc b/third_party/harfbuzz-ng/src/hb-buffer.cc
new file mode 100644
index 0000000..03e8e1a
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-buffer.cc
@@ -0,0 +1,630 @@
+/*
+ * 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>
+
+HB_BEGIN_DECLS
+
+
+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_glyph_position_t *new_pos;
+ hb_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_glyph_position_t *) realloc (buffer->pos, new_allocated * sizeof (buffer->pos[0]));
+ new_info = (hb_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_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 inline 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_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->props.direction = direction;
+}
+
+hb_direction_t
+hb_buffer_get_direction (hb_buffer_t *buffer)
+{
+ return buffer->props.direction;
+}
+
+void
+hb_buffer_set_script (hb_buffer_t *buffer,
+ hb_script_t script)
+{
+ buffer->props.script = script;
+}
+
+hb_script_t
+hb_buffer_get_script (hb_buffer_t *buffer)
+{
+ return buffer->props.script;
+}
+
+void
+hb_buffer_set_language (hb_buffer_t *buffer,
+ hb_language_t language)
+{
+ buffer->props.language = language;
+}
+
+hb_language_t
+hb_buffer_get_language (hb_buffer_t *buffer)
+{
+ return buffer->props.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->serial = 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_glyph_info_t *glyph;
+
+ if (unlikely (!_hb_buffer_ensure (buffer, buffer->len + 1))) return;
+
+ glyph = &buffer->info[buffer->len];
+
+ memset (glyph, 0, sizeof (*glyph));
+ glyph->codepoint = codepoint;
+ glyph->mask = mask;
+ glyph->cluster = cluster;
+
+ 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_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_glyph_info_t *tmp_string;
+ tmp_string = buffer->info;
+ buffer->info = buffer->out_info;
+ buffer->out_info = tmp_string;
+ buffer->pos = (hb_glyph_position_t *) buffer->out_info;
+ }
+
+ tmp = buffer->len;
+ buffer->len = buffer->out_len;
+ buffer->out_len = tmp;
+
+ buffer->i = 0;
+}
+
+void
+_hb_buffer_replace_glyphs_be16 (hb_buffer_t *buffer,
+ unsigned int num_in,
+ unsigned int num_out,
+ const uint16_t *glyph_data_be)
+{
+ 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;
+ }
+
+ hb_glyph_info_t orig_info = buffer->info[buffer->i];
+
+ for (unsigned int i = 0; i < num_out; i++)
+ {
+ hb_glyph_info_t *info = &buffer->out_info[buffer->out_len + i];
+ *info = orig_info;
+ info->codepoint = hb_be_uint16 (glyph_data_be[i]);
+ }
+
+ buffer->i += num_in;
+ buffer->out_len += num_out;
+}
+
+void
+_hb_buffer_replace_glyph (hb_buffer_t *buffer,
+ hb_codepoint_t glyph_index)
+{
+ hb_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;
+
+ 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_reset_masks (hb_buffer_t *buffer,
+ hb_mask_t mask)
+{
+ unsigned int count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ buffer->info[i].mask = mask;
+}
+
+void
+_hb_buffer_add_masks (hb_buffer_t *buffer,
+ hb_mask_t mask)
+{
+ unsigned int count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ buffer->info[i].mask |= mask;
+}
+
+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;
+ value &= mask;
+
+ if (!mask)
+ return;
+
+ 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;
+ }
+
+ /* XXX can't bsearch since .cluster may not be sorted. */
+ /* 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_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_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
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-buffer.h b/third_party/harfbuzz-ng/src/hb-buffer.h
new file mode 100644
index 0000000..0185415
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-buffer.h
@@ -0,0 +1,163 @@
+/*
+ * 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;
+ hb_var_int_t var1;
+ hb_var_int_t var2;
+} 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;
+ hb_var_int_t var;
+} 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-common.c b/third_party/harfbuzz-ng/src/hb-common.c
new file mode 100644
index 0000000..74f8933
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-common.c
@@ -0,0 +1,47 @@
+/*
+ * 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_BEGIN_DECLS
+
+
+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);
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-common.h b/third_party/harfbuzz-ng/src/hb-common.h
new file mode 100644
index 0000000..1dd02cb
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-common.h
@@ -0,0 +1,106 @@
+/*
+ * 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 __cplusplus
+# define HB_BEGIN_DECLS extern "C" {
+# define HB_END_DECLS }
+# else /* !__cplusplus */
+# define HB_BEGIN_DECLS
+# define HB_END_DECLS
+# endif /* !__cplusplus */
+
+HB_BEGIN_DECLS
+
+
+#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
+
+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))
+
+
+typedef union _hb_var_int_t {
+ uint32_t u32;
+ int32_t i32;
+ uint16_t u16[2];
+ int16_t i16[2];
+ uint8_t u8[4];
+ int8_t i8[4];
+} hb_var_int_t;
+
+
+HB_END_DECLS
+
+#endif /* HB_COMMON_H */
diff --git a/third_party/harfbuzz-ng/src/hb-font-private.h b/third_party/harfbuzz-ng/src/hb-font-private.h
new file mode 100644
index 0000000..b147bce
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-font-private.h
@@ -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"
+
+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_glyph_advance_func_t get_glyph_advance;
+ hb_font_get_glyph_extents_func_t get_glyph_extents;
+ hb_font_get_contour_point_func_t get_contour_point;
+ 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-font.cc b/third_party/harfbuzz-ng/src/hb-font.cc
new file mode 100644
index 0000000..63631a9
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-font.cc
@@ -0,0 +1,583 @@
+/*
+ * 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.h"
+#include "hb-blob-private.h"
+#include "hb-open-file-private.hh"
+
+#include "hb-ot-layout-private.hh"
+
+#include <string.h>
+
+HB_BEGIN_DECLS
+
+
+/*
+ * 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 void
+hb_font_get_glyph_advance_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_position_t *x_advance HB_UNUSED,
+ hb_position_t *y_advance HB_UNUSED)
+{ }
+
+static void
+hb_font_get_glyph_extents_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_extents_t *extents HB_UNUSED)
+{ }
+
+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 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_glyph_advance_nil,
+ hb_font_get_glyph_extents_nil,
+ hb_font_get_contour_point_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;
+}
+
+hb_bool_t
+hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->immutable;
+}
+
+
+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_glyph_advance_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_advance_func_t glyph_advance_func)
+{
+ if (ffuncs->immutable)
+ return;
+
+ ffuncs->v.get_glyph_advance = glyph_advance_func ? glyph_advance_func : hb_font_get_glyph_advance_nil;
+}
+
+void
+hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_extents_func_t glyph_extents_func)
+{
+ if (ffuncs->immutable)
+ return;
+
+ ffuncs->v.get_glyph_extents = glyph_extents_func ? glyph_extents_func : hb_font_get_glyph_extents_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_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_font_get_glyph_func_t
+hb_font_funcs_get_glyph_func (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->v.get_glyph;
+}
+
+hb_font_get_glyph_advance_func_t
+hb_font_funcs_get_glyph_advance_func (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->v.get_glyph_advance;
+}
+
+hb_font_get_glyph_extents_func_t
+hb_font_funcs_get_glyph_extents_func (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->v.get_glyph_extents;
+}
+
+hb_font_get_contour_point_func_t
+hb_font_funcs_get_contour_point_func (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->v.get_contour_point;
+}
+
+hb_font_get_kerning_func_t
+hb_font_funcs_get_kerning_func (hb_font_funcs_t *ffuncs)
+{
+ return ffuncs->v.get_kerning;
+}
+
+
+
+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);
+}
+
+void
+hb_font_get_glyph_advance (hb_font_t *font, hb_face_t *face,
+ hb_codepoint_t glyph,
+ hb_position_t *x_advance, hb_position_t *y_advance)
+{
+ *x_advance = *y_advance = 0;
+ return font->klass->v.get_glyph_advance (font, face, font->user_data,
+ glyph, x_advance, y_advance);
+}
+
+void
+hb_font_get_glyph_extents (hb_font_t *font, hb_face_t *face,
+ hb_codepoint_t glyph, hb_glyph_extents_t *extents)
+{
+ memset (extents, 0, sizeof (*extents));
+ return font->klass->v.get_glyph_extents (font, face, font->user_data,
+ glyph, extents);
+}
+
+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);
+}
+
+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 = 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_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
+
+ 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;
+}
+
+unsigned int
+hb_face_get_upem (hb_face_t *face)
+{
+ return (face->head_table ? face->head_table : &Null(head))->get_upem ();
+}
+
+
+/*
+ * 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_unset_funcs (hb_font_t *font,
+ hb_font_funcs_t **klass,
+ hb_destroy_func_t *destroy,
+ void **user_data)
+{
+ /* None of the input arguments can be NULL. */
+
+ *klass = font->klass;
+ *destroy = font->destroy;
+ *user_data = font->user_data;
+
+ if (HB_OBJECT_IS_INERT (font))
+ return;
+
+ font->klass = NULL;
+ font->destroy = NULL;
+ font->user_data = NULL;
+}
+
+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_get_scale (hb_font_t *font,
+ unsigned int *x_scale,
+ unsigned int *y_scale)
+{
+ if (x_scale) *x_scale = font->x_scale;
+ if (y_scale) *y_scale = font->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;
+}
+
+void
+hb_font_get_ppem (hb_font_t *font,
+ unsigned int *x_ppem,
+ unsigned int *y_ppem)
+{
+ if (x_ppem) *x_ppem = font->x_ppem;
+ if (y_ppem) *y_ppem = font->y_ppem;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-font.h b/third_party/harfbuzz-ng/src/hb-font.h
new file mode 100644
index 0000000..f33e56f
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-font.h
@@ -0,0 +1,270 @@
+/*
+ * 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);
+
+/* XXX
+ *
+ * I have two major concerns about this API as it is right now:
+ *
+ * - Jonathan Kew convinced me to make it return NULL if table not found (280af1bd),
+ * however, that is WRONG IMO. The API should not differentiate between a non-existing
+ * table vs a zero-length table vs a very short table. It only leads to implementations
+ * that check for non-NULL and assume that they've got a usable table going on... This
+ * actually happened with Firefox.
+ *
+ * - It has to be renamed to reference_table() since unlike any other _get_ API, a reference
+ * ownership transfer happens and the user is responsible to destroy the result.
+ */
+hb_blob_t *
+hb_face_get_table (hb_face_t *face,
+ hb_tag_t tag);
+
+unsigned int
+hb_face_get_upem (hb_face_t *face);
+
+
+/*
+ * 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);
+
+hb_bool_t
+hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
+
+/* funcs */
+
+typedef struct _hb_glyph_extents_t
+{
+ hb_position_t x_bearing;
+ hb_position_t y_bearing;
+ hb_position_t width;
+ hb_position_t height;
+} hb_glyph_extents_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 void (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+ hb_codepoint_t glyph,
+ hb_position_t *x_advance, hb_position_t *y_advance);
+typedef void (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, hb_face_t *face, const void *user_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents);
+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 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_glyph_advance_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_advance_func_t glyph_advance_func);
+
+void
+hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_extents_func_t glyph_extents_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_kerning_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_kerning_func_t kerning_func);
+
+
+/* These never return NULL. Return fallback defaults instead. */
+
+hb_font_get_glyph_func_t
+hb_font_funcs_get_glyph_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_glyph_advance_func_t
+hb_font_funcs_get_glyph_advance_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_glyph_extents_func_t
+hb_font_funcs_get_glyph_extents_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_contour_point_func_t
+hb_font_funcs_get_contour_point_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_kerning_func_t
+hb_font_funcs_get_kerning_func (hb_font_funcs_t *ffuncs);
+
+
+hb_codepoint_t
+hb_font_get_glyph (hb_font_t *font, hb_face_t *face,
+ hb_codepoint_t unicode, hb_codepoint_t variation_selector);
+
+void
+hb_font_get_glyph_advance (hb_font_t *font, hb_face_t *face,
+ hb_codepoint_t glyph,
+ hb_position_t *x_advance, hb_position_t *y_advance);
+
+void
+hb_font_get_glyph_extents (hb_font_t *font, hb_face_t *face,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents);
+
+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);
+
+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);
+
+/* Returns what was set and unsets it, but doesn't destroy(user_data).
+ * This is useful for wrapping / chaining font_funcs_t's.
+ *
+ * The client is responsible for:
+ *
+ * - Take ownership of the reference on the returned klass,
+ *
+ * - Calling "destroy(user_data)" exactly once if returned destroy func
+ * is not NULL and the returned info is not needed anymore.
+ */
+void
+hb_font_unset_funcs (hb_font_t *font,
+ hb_font_funcs_t **klass,
+ hb_destroy_func_t *destroy,
+ void **user_data);
+
+
+/*
+ * We should add support for full matrices.
+ */
+void
+hb_font_set_scale (hb_font_t *font,
+ unsigned int x_scale,
+ unsigned int y_scale);
+
+void
+hb_font_get_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);
+
+void
+hb_font_get_ppem (hb_font_t *font,
+ unsigned int *x_ppem,
+ unsigned int *y_ppem);
+
+
+HB_END_DECLS
+
+#endif /* HB_FONT_H */
diff --git a/third_party/harfbuzz-ng/src/hb-ft.c b/third_party/harfbuzz-ng/src/hb-ft.c
new file mode 100644
index 0000000..e696fe8
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ft.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Keith Stribley
+ *
+ * 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.h"
+
+#include FT_TRUETYPE_TABLES_H
+
+HB_BEGIN_DECLS
+
+
+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 void
+hb_ft_get_glyph_advance (hb_font_t *font HB_UNUSED,
+ hb_face_t *face HB_UNUSED,
+ const void *user_data,
+ hb_codepoint_t glyph,
+ hb_position_t *x_advance,
+ hb_position_t *y_advance)
+{
+ FT_Face ft_face = (FT_Face) user_data;
+ int load_flags = FT_LOAD_DEFAULT;
+
+ /* TODO: load_flags, embolden, etc */
+
+ if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
+ {
+ *x_advance = ft_face->glyph->advance.x;
+ *y_advance = ft_face->glyph->advance.y;
+ }
+}
+
+static void
+hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
+ hb_face_t *face HB_UNUSED,
+ const void *user_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents)
+{
+ FT_Face ft_face = (FT_Face) user_data;
+ int load_flags = FT_LOAD_DEFAULT;
+
+ /* TODO: load_flags, embolden, etc */
+
+ if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
+ {
+ /* XXX: A few negations should be in order here, not sure. */
+ extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
+ extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
+ extents->width = ft_face->glyph->metrics.width;
+ extents->height = ft_face->glyph->metrics.height;
+ }
+}
+
+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 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_glyph_advance,
+ hb_ft_get_glyph_extents,
+ hb_ft_get_contour_point,
+ 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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ft.h b/third_party/harfbuzz-ng/src/hb-ft.h
new file mode 100644
index 0000000..be5c854
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ft.h
@@ -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
+ */
+
+#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 */
diff --git a/third_party/harfbuzz-ng/src/hb-glib.c b/third_party/harfbuzz-ng/src/hb-glib.c
new file mode 100644
index 0000000..c548fa6
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-glib.c
@@ -0,0 +1,64 @@
+/*
+ * 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>
+
+HB_BEGIN_DECLS
+
+
+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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-glib.h b/third_party/harfbuzz-ng/src/hb-glib.h
new file mode 100644
index 0000000..81ab15d
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-glib.h
@@ -0,0 +1,41 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-graphite.cc b/third_party/harfbuzz-ng/src/hb-graphite.cc
new file mode 100644
index 0000000..0a8d681
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-graphite.cc
@@ -0,0 +1,310 @@
+/*
+ * 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.h"
+#include "hb-graphite.h"
+#include <map>
+
+HB_BEGIN_DECLS
+
+
+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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-graphite.h b/third_party/harfbuzz-ng/src/hb-graphite.h
new file mode 100644
index 0000000..b5d1176
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-graphite.h
@@ -0,0 +1,47 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-icu.c b/third_party/harfbuzz-ng/src/hb-icu.c
new file mode 100644
index 0000000..601ef61
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-icu.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Keith Stribley
+ *
+ * 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>
+
+HB_BEGIN_DECLS
+
+
+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_SCRIPT2(MEITEI_MAYEK, MEETEI_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 */
+
+ /* Unicode-6.0 additions */
+ MATCH_SCRIPT (BATAK); /* Batk */
+ MATCH_SCRIPT (BRAHMI); /* Brah */
+ MATCH_SCRIPT2(MANDAEAN, MANDAIC); /* Mand */
+
+ }
+ 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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-icu.h b/third_party/harfbuzz-ng/src/hb-icu.h
new file mode 100644
index 0000000..cc17af8
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-icu.h
@@ -0,0 +1,41 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-language.c b/third_party/harfbuzz-ng/src/hb-language.c
new file mode 100644
index 0000000..2aabada
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-language.c
@@ -0,0 +1,120 @@
+/*
+ * 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"
+
+HB_BEGIN_DECLS
+
+
+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;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-language.h b/third_party/harfbuzz-ng/src/hb-language.h
new file mode 100644
index 0000000..d3c91fb
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-language.h
@@ -0,0 +1,46 @@
+/*
+ * 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 */
diff --git a/third_party/harfbuzz-ng/src/hb-object-private.h b/third_party/harfbuzz-ng/src/hb-object-private.h
new file mode 100644
index 0000000..a5b5355
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-object-private.h
@@ -0,0 +1,141 @@
+/*
+ * 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_OBJECT_PRIVATE_H
+#define HB_OBJECT_PRIVATE_H
+
+#include "hb-private.h"
+
+HB_BEGIN_DECLS
+
+
+/* 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)
+{
+ (void) (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
+
+
+HB_END_DECLS
+
+#endif /* HB_OBJECT_PRIVATE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-open-file-private.hh b/third_party/harfbuzz-ng/src/hb-open-file-private.hh
new file mode 100644
index 0000000..cfb60db
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-open-file-private.hh
@@ -0,0 +1,258 @@
+/*
+ * 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"
+
+HB_BEGIN_DECLS
+
+
+/*
+ *
+ * The OpenType Font File
+ *
+ */
+
+
+/*
+ * Organization of an OpenType Font
+ */
+
+struct OpenTypeFontFile;
+struct OffsetTable;
+struct TTCHeader;
+
+
+typedef struct TableRecord
+{
+ 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 TableRecord& get_table (unsigned int i) const
+ {
+ if (unlikely (i >= numTables)) return Null(TableRecord);
+ return tables[i];
+ }
+ inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
+ {
+ Tag t;
+ t.set (tag);
+ unsigned int count = numTables;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (t == tables[i].tag)
+ {
+ if (table_index) *table_index = i;
+ return true;
+ }
+ }
+ if (table_index) *table_index = Index::NOT_FOUND_INDEX;
+ return false;
+ }
+ inline const TableRecord& 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 (tables, TableRecord::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. */
+ TableRecord tables[VAR]; /* TableRecord entries. numTables items */
+ public:
+ DEFINE_SIZE_ARRAY (12, tables);
+} 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);
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OPEN_FILE_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-open-type-private.hh b/third_party/harfbuzz-ng/src/hb-open-type-private.hh
new file mode 100644
index 0000000..8f3001b
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-open-type-private.hh
@@ -0,0 +1,727 @@
+/*
+ * 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_TYPE_PRIVATE_HH
+#define HB_OPEN_TYPE_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-blob.h"
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+
+/*
+ * 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 (void) {
+ 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> (void) { \
+ 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) {
+ (void) (*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;
+
+ (void) (HB_DEBUG_SANITIZE &&
+ fprintf (stderr, "sanitize %p init [%p..%p] (%lu bytes)\n",
+ this->blob, this->start, this->end,
+ (unsigned long) (this->end - this->start)));
+ }
+
+ inline void finish (void)
+ {
+ (void) (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;
+
+ (void) (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 = record_size > 0 && len >= ((unsigned int) -1) / record_size;
+
+ (void) (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++;
+
+ (void) (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:
+ (void) (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) {
+ (void) (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) {
+ (void) (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 */
+ (void) (HB_DEBUG_SANITIZE &&
+ fprintf (stderr, "Sanitizer %p retry %s\n", blob, HB_FUNC));
+ goto retry;
+ }
+ }
+
+ (void) (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 void set (Type i) { hb_be_uint16_put (v,i); }
+ inline operator Type (void) const { return hb_be_uint16_get (v); }
+ inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_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 void set (Type i) { hb_be_uint32_put (v,i); }
+ inline operator Type (void) const { return hb_be_uint32_get (v); }
+ inline bool operator == (const BEInt<Type, 4>& o) const { return hb_be_uint32_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.set (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 int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
+ 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 (void) 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. We just include
+ * a small unreachable expression to make sure the structs
+ * pointed to do have a simple sanitize(), ie. they do not
+ * reference other structs via offsets.
+ */
+ (void) (false && array[0].sanitize (c));
+
+ 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 (void) 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. We just include
+ * a small unreachable expression to make sure the structs
+ * pointed to do have a simple sanitize(), ie. they do not
+ * reference other structs via offsets.
+ */
+ (void) (false && array[0].sanitize (c));
+
+ return true;
+ }
+
+ USHORT len;
+ Type array[VAR];
+ public:
+ DEFINE_SIZE_ARRAY (sizeof (USHORT), array);
+};
+
+
+/* An array with sorted elements. Supports binary searching. */
+template <typename Type>
+struct SortedArrayOf : ArrayOf<Type> {
+
+ template <typename SearchType>
+ inline int search (const SearchType &x) const {
+ class Cmp {
+ public: static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
+ };
+ const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
+ return p ? p - this->array : -1;
+ }
+};
+
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+#endif /* HB_OPEN_TYPE_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-head-private.hh b/third_party/harfbuzz-ng/src/hb-ot-head-private.hh
new file mode 100644
index 0000000..3a4bbbf
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-head-private.hh
@@ -0,0 +1,146 @@
+/*
+ * 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"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * 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 unsigned int get_upem (void) const {
+ unsigned int upem = unitsPerEm;
+ /* If no valid head table found, assume 1000, which matches typicaly Type1 usage. */
+ return 16 <= upem && upem <= 16384 ? upem : 1000;
+ }
+
+ 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);
+ }
+
+ private:
+ 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: Last Resort font. If set, indicates that
+ * the glyphs encoded in the cmap subtables are simply
+ * generic symbolic representations of code point
+ * ranges and don’t truly represent support for those
+ * code points. If unset, indicates that the glyphs
+ * encoded in the cmap subtables represent proper
+ * support for those code points.
+ * 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);
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_HEAD_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh
new file mode 100644
index 0000000..9ff5ca9
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2007,2008,2009 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google 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
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+
+/*
+ *
+ * OpenType Layout Common Table Formats
+ *
+ */
+
+
+/*
+ * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
+ */
+
+template <typename Type>
+struct Record
+{
+ inline int cmp (hb_tag_t a) const {
+ return tag.cmp (a);
+ }
+
+ 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 : SortedArrayOf<Record<Type> > {
+ inline const Tag& get_tag (unsigned int i) const
+ {
+ /* We cheat slightly and don't define separate Null objects
+ * for Record types. Instead, we return the correct Null(Tag)
+ * here. */
+ 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
+ {
+ int i = this->search (tag);
+ if (i != -1) {
+ if (index) *index = i;
+ return true;
+ } else {
+ 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 RangeRecord
+{
+ inline int cmp (hb_codepoint_t g) const {
+ hb_codepoint_t a = start, b = end;
+ return g < a ? -1 : g <= b ? 0 : +1 ;
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return c->check_struct (this);
+ }
+
+ GlyphID start; /* First GlyphID in the range */
+ GlyphID end; /* Last GlyphID in the range */
+ USHORT value; /* Value */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+DEFINE_NULL_DATA (RangeRecord, "\000\001");
+
+
+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);
+ }
+
+ 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; }
+
+ /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and
+ * higher 16-bit is mark-filtering-set if the lookup uses one.
+ * Not to be confused with glyph_props which is very similar. */
+ inline uint32_t get_props (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
+ {
+ int i = glyphArray.search (glyph_id);
+ if (i != -1)
+ 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 */
+ SortedArrayOf<GlyphID>
+ glyphArray; /* Array of GlyphIDs--in numerical order */
+ public:
+ DEFINE_SIZE_ARRAY (4, glyphArray);
+};
+
+struct CoverageFormat2
+{
+ friend struct Coverage;
+
+ private:
+ inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+ {
+ int i = rangeRecord.search (glyph_id);
+ if (i != -1) {
+ const RangeRecord &range = rangeRecord[i];
+ return (unsigned int) range.value + (glyph_id - range.start);
+ }
+ return NOT_COVERED;
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return rangeRecord.sanitize (c);
+ }
+
+ private:
+ USHORT coverageFormat; /* Format identifier--format = 2 */
+ SortedArrayOf<RangeRecord>
+ 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 unsigned int 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 ClassDefFormat2
+{
+ friend struct ClassDef;
+
+ private:
+ inline unsigned int get_class (hb_codepoint_t glyph_id) const
+ {
+ int i = rangeRecord.search (glyph_id);
+ if (i != -1)
+ return rangeRecord[i].value;
+ return 0;
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return rangeRecord.sanitize (c);
+ }
+
+ USHORT classFormat; /* Format identifier--format = 2 */
+ SortedArrayOf<RangeRecord>
+ rangeRecord; /* Array of glyph ranges--ordered by
+ * Start GlyphID */
+ public:
+ DEFINE_SIZE_ARRAY (4, rangeRecord);
+};
+
+struct ClassDef
+{
+ inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); }
+
+ inline unsigned int 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
+{
+
+ inline hb_position_t get_x_delta (hb_ot_layout_context_t *c) const
+ { return get_delta (c->font->x_ppem, c->font->x_scale); }
+
+ inline hb_position_t get_y_delta (hb_ot_layout_context_t *c) const
+ { return get_delta (c->font->y_ppem, c->font->y_scale); }
+
+ inline int get_delta (unsigned int ppem, unsigned int scale) const
+ {
+ if (!ppem) return 0;
+
+ int pixels = get_delta_pixels (ppem);
+
+ if (!pixels) return 0;
+
+ /* pixels is at most in the -8..7 range. So 64-bit arithmetic is
+ * not really necessary here. A simple cast to int may just work
+ * as well. But since this code is not reached that often and
+ * for the sake of correctness, we do a 64bit operation. */
+ return pixels * (int64_t) scale / ppem;
+ }
+
+
+ inline int get_delta_pixels (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 (void) 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);
+};
+
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-private.hh
new file mode 100644
index 0000000..4172a7c
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-private.hh
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2007,2008,2009 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google 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.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * 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_direction_t direction, hb_codepoint_t glyph_id HB_UNUSED) const
+ {
+ return HB_DIRECTION_IS_HORIZONTAL (direction) ? c->scale_x (coordinate) : c->scale_y (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_direction_t direction, hb_codepoint_t glyph_id) const
+ {
+ hb_position_t x, y;
+ if (hb_font_get_contour_point (c->font, c->face, caretValuePoint, glyph_id, &x, &y))
+ return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
+ 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_direction_t direction, hb_codepoint_t glyph_id) const
+ {
+ return HB_DIRECTION_IS_HORIZONTAL (direction) ?
+ c->scale_x (coordinate) + (this+deviceTable).get_x_delta (c) :
+ c->scale_y (coordinate) + (this+deviceTable).get_y_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_direction_t direction, hb_codepoint_t glyph_id) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.get_caret_value (c, direction, glyph_id);
+ case 2: return u.format2.get_caret_value (c, direction, glyph_id);
+ case 3: return u.format3.get_caret_value (c, direction, 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_direction_t direction,
+ 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, direction, 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_direction_t direction,
+ 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, direction, 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 (void) const { return glyphClassDef != 0; }
+ inline unsigned int get_glyph_class (hb_codepoint_t glyph) const
+ { return (this+glyphClassDef).get_class (glyph); }
+
+ inline bool has_mark_attachment_types (void) const { return markAttachClassDef != 0; }
+ inline unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
+ { return (this+markAttachClassDef).get_class (glyph); }
+
+ inline bool has_attach_points (void) 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 (void) const { return ligCaretList != 0; }
+ inline unsigned int get_lig_carets (hb_ot_layout_context_t *c,
+ hb_direction_t direction,
+ 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, direction, glyph_id, start_offset, caret_count, caret_array); }
+
+ inline bool has_mark_sets (void) 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));
+ }
+
+
+ /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
+ * glyph class and other bits, and high 8-bit gthe mark attachment type (if any).
+ * Not to be confused with lookup_props which is very similar. */
+ inline unsigned int get_glyph_props (hb_codepoint_t glyph) const
+ {
+ unsigned int klass = get_glyph_class (glyph);
+
+ switch (klass) {
+ default:
+ case UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
+ case BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
+ case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
+ case ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
+ case MarkGlyph:
+ klass = get_mark_attachment_type (glyph);
+ return HB_OT_LAYOUT_GLYPH_CLASS_MARK | (klass << 8);
+ }
+ }
+
+
+ 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);
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_GDEF_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-private.hh
new file mode 100644
index 0000000..11bb286
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-private.hh
@@ -0,0 +1,1588 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google 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"
+
+HB_BEGIN_DECLS
+
+
+/* buffer var allocations */
+#define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
+#define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */
+
+
+/* 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 (void) const
+ { return _hb_popcount32 ((unsigned int) *this); }
+ inline unsigned int get_size (void) const
+ { return get_len () * Value::static_size; }
+
+ void apply_value (hb_ot_layout_context_t *layout,
+ const void *base,
+ const Value *values,
+ hb_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 (void) 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 = false;
+
+ 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 (rows > 0 && 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_glyph_position_t &o = c->buffer->pos[c->buffer->i];
+ o.x_offset = base_x - mark_x;
+ o.y_offset = base_y - mark_y;
+ o.attach_lookback() = 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_props, 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_props, 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 ();
+
+ /* We don't handle mark glyphs here. */
+ if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
+ return false;
+
+ unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
+ if (unlikely (c->buffer->i + 2 > end))
+ return false;
+
+ const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->i].codepoint)];
+ if (!this_record.exitAnchor)
+ return false;
+
+ unsigned int j = c->buffer->i + 1;
+ while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_props, NULL))
+ {
+ if (unlikely (j == end))
+ return false;
+ j++;
+ }
+
+ const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)];
+ if (!next_record.entryAnchor)
+ return false;
+
+ unsigned int i = c->buffer->i;
+
+ hb_position_t entry_x, entry_y, exit_x, exit_y;
+ (this+this_record.exitAnchor).get_anchor (c->layout, c->buffer->info[i].codepoint, &exit_x, &exit_y);
+ (this+next_record.entryAnchor).get_anchor (c->layout, c->buffer->info[j].codepoint, &entry_x, &entry_y);
+
+ hb_direction_t direction = c->buffer->props.direction;
+
+ /* Align the exit anchor of the left/top glyph with the entry anchor of the right/bottom glyph
+ * by adjusting advance of the left/top glyph. */
+ if (HB_DIRECTION_IS_BACKWARD (direction))
+ {
+ if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+ c->buffer->pos[j].x_advance = c->buffer->pos[j].x_offset + entry_x - exit_x;
+ else
+ c->buffer->pos[j].y_advance = c->buffer->pos[j].y_offset + entry_y - exit_y;
+ }
+ else
+ {
+ if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+ c->buffer->pos[i].x_advance = c->buffer->pos[i].x_offset + exit_x - entry_x;
+ else
+ c->buffer->pos[i].y_advance = c->buffer->pos[i].y_offset + exit_y - entry_y;
+ }
+
+ if (c->lookup_props & LookupFlag::RightToLeft)
+ {
+ c->buffer->pos[i].cursive_chain() = j - i;
+ if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+ c->buffer->pos[i].y_offset = entry_y - exit_y;
+ else
+ c->buffer->pos[i].x_offset = entry_x - exit_x;
+ }
+ else
+ {
+ c->buffer->pos[j].cursive_chain() = i - j;
+ if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+ c->buffer->pos[j].y_offset = exit_y - entry_y;
+ else
+ c->buffer->pos[j].x_offset = exit_x - entry_x;
+ }
+
+ c->buffer->i = j;
+ 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 (!(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 (!(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].lig_comp())
+ {
+ comp_index = c->buffer->info[c->buffer->i].lig_comp() - 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_props, &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].lig_comp() != c->buffer->info[c->buffer->i].lig_comp()) ||
+ (c->buffer->info[j].lig_comp() && 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;
+};
+
+
+HB_BEGIN_DECLS
+static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+HB_END_DECLS
+
+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_props = get_props ();
+
+ if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_props, &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;
+
+ 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->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); }
+
+ static inline void position_finish (hb_buffer_t *buffer);
+
+ 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);
+};
+
+void
+GPOS::position_finish (hb_buffer_t *buffer)
+{
+ unsigned int i, j;
+ unsigned int len = hb_buffer_get_length (buffer);
+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer);
+ hb_direction_t direction = buffer->props.direction;
+
+ /* Handle cursive connections:
+ * First handle all chain-back connections, then handle all chain-forward connections. */
+ if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+ {
+ for (j = 0; j < len; j++) {
+ if (pos[j].cursive_chain() < 0)
+ pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
+ }
+ for (i = len; i > 0; i--) {
+ j = i - 1;
+ if (pos[j].cursive_chain() > 0)
+ pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
+ }
+ }
+ else
+ {
+ for (j = 0; j < len; j++) {
+ if (pos[j].cursive_chain() < 0)
+ pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
+ }
+ for (i = len; i > 0; i--) {
+ j = i - 1;
+ if (pos[j].cursive_chain() > 0)
+ pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
+ }
+ }
+
+
+ /* Handle attachments */
+ for (i = 0; i < len; i++)
+ if (pos[i].attach_lookback())
+ {
+ unsigned int back = i - pos[i].attach_lookback();
+ pos[i].x_offset += pos[back].x_offset;
+ pos[i].y_offset += pos[back].y_offset;
+
+ if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+ for (j = back + 1; j < i + 1; j++) {
+ pos[i].x_offset += pos[j].x_advance;
+ pos[i].y_offset += pos[j].y_advance;
+ }
+ else
+ for (j = back; j < i; j++) {
+ pos[i].x_offset -= pos[j].x_advance;
+ pos[i].y_offset -= pos[j].y_advance;
+ }
+ }
+}
+
+
+/* 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);
+}
+
+
+#undef attach_lookback
+#undef cursive_chain
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-private.hh
new file mode 100644
index 0000000..ae1c5f3
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-private.hh
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google 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"
+
+HB_BEGIN_DECLS
+
+
+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->replace_glyph (glyph_id);
+
+ 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->replace_glyph (glyph_id);
+
+ 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;
+
+ if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
+ c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
+ c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
+
+ 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->replace_glyph (glyph_id);
+
+ 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) 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 (count < 2 || c->buffer->i + count > end))
+ return false;
+
+ bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+ bool found_non_mark = 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_props, &property))
+ {
+ if (unlikely (j + count - i == end))
+ return false;
+ j++;
+ }
+
+ found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+ if (likely (c->buffer->info[j].codepoint != component[i]))
+ return false;
+ }
+
+ if (first_was_mark && found_non_mark)
+ c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
+
+ /* Allocate new ligature id */
+ unsigned int lig_id = allocate_lig_id (c->buffer);
+ c->buffer->info[c->buffer->i].lig_comp() = 0;
+ c->buffer->info[c->buffer->i].lig_id() = lig_id;
+
+ if (j == c->buffer->i + i) /* No input glyphs skipped */
+ {
+ c->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph);
+ }
+ else
+ {
+ c->replace_glyph (ligGlyph);
+
+ /* 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_props, NULL))
+ {
+ c->buffer->info[c->buffer->i].lig_comp() = i;
+ c->buffer->info[c->buffer->i].lig_id() = lig_id;
+ c->replace_glyph (c->buffer->info[c->buffer->i].codepoint);
+ }
+
+ /* Skip the base glyph */
+ c->buffer->i++;
+ }
+ }
+
+ return true;
+ }
+
+ inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const {
+ uint16_t lig_id = buffer->next_serial ();
+ if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflows */
+ return lig_id;
+ }
+
+ 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) 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))
+ 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;
+
+ 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);
+ }
+
+ 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;
+};
+
+
+HB_BEGIN_DECLS
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+HB_END_DECLS
+
+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_props = get_props ();
+
+ if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_props, &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);
+}
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh
new file mode 100644
index 0000000..c2b2eea
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh
@@ -0,0 +1,981 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google 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"
+
+HB_BEGIN_DECLS
+
+
+/* buffer var allocations */
+#define lig_id() var2.u16[0] /* unique ligature id */
+#define lig_comp() var2.u16[1] /* component number in the ligature (0 = base) */
+
+
+#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); \
+
+
+HB_BEGIN_DECLS
+
+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_props;
+ unsigned int property; /* propety of first glyph */
+
+
+ inline void replace_glyph (hb_codepoint_t glyph_index) const
+ {
+ clear_property ();
+ buffer->replace_glyph (glyph_index);
+ }
+ inline void replace_glyphs_be16 (unsigned int num_in,
+ unsigned int num_out,
+ const uint16_t *glyph_data_be) const
+ {
+ clear_property ();
+ buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
+ }
+
+ inline void guess_glyph_class (unsigned int klass)
+ {
+ /* XXX if ! has gdef */
+ buffer->info[buffer->i].props_cache() = klass;
+ }
+
+ private:
+ inline void clear_property (void) const
+ {
+ /* XXX if has gdef */
+ buffer->info[buffer->i].props_cache() = 0;
+ }
+};
+
+
+
+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_props, 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_props, 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_props, 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;
+}
+
+HB_END_DECLS
+
+
+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);
+};
+
+
+HB_BEGIN_DECLS
+
+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 (count == 0 || 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, it 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_props, 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;
+}
+
+HB_END_DECLS
+
+
+/* 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];
+ 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);
+ }
+
+ public:
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!backtrack.sanitize (c)) return false;
+ HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+ if (!input.sanitize (c)) return false;
+ ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+ if (!lookahead.sanitize (c)) return false;
+ ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+ return lookup.sanitize (c);
+ }
+
+ private:
+ ArrayOf<USHORT>
+ backtrack; /* Array of backtracking values
+ * (to be matched before the input
+ * sequence) */
+ HeadlessArrayOf<USHORT>
+ inputX; /* Array of input values (start with
+ * second glyph) */
+ ArrayOf<USHORT>
+ lookaheadX; /* Array of lookahead values's (to be
+ * matched after the input sequence) */
+ ArrayOf<LookupRecord>
+ lookupX; /* Array of LookupRecords--in
+ * design order) */
+ public:
+ DEFINE_SIZE_MIN (8);
+};
+
+struct ChainRuleSet
+{
+ inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+ {
+ TRACE_APPLY ();
+ unsigned int num_rules = rule.len;
+ for (unsigned int i = 0; i < num_rules; i++)
+ {
+ if ((this+rule[i]).apply (c, lookup_context))
+ return true;
+ }
+
+ return false;
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return rule.sanitize (c, this);
+ }
+
+ private:
+ OffsetArrayOf<ChainRule>
+ rule; /* Array of ChainRule tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, rule);
+};
+
+struct ChainContextFormat1
+{
+ friend struct ChainContext;
+
+ private:
+ inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+ {
+ TRACE_APPLY ();
+ unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+ if (likely (index == NOT_COVERED))
+ return false;
+
+ const ChainRuleSet &rule_set = this+ruleSet[index];
+ struct ChainContextLookupContext lookup_context = {
+ {match_glyph, apply_func},
+ {NULL, NULL, NULL}
+ };
+ return rule_set.apply (c, lookup_context);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return coverage.sanitize (c, this)
+ && ruleSet.sanitize (c, this);
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetArrayOf<ChainRuleSet>
+ ruleSet; /* Array of ChainRuleSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, ruleSet);
+};
+
+struct ChainContextFormat2
+{
+ friend struct ChainContext;
+
+ private:
+ inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+ {
+ TRACE_APPLY ();
+ unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
+ if (likely (index == NOT_COVERED))
+ return false;
+
+ const ClassDef &backtrack_class_def = this+backtrackClassDef;
+ const ClassDef &input_class_def = this+inputClassDef;
+ const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+ index = input_class_def (c->buffer->info[c->buffer->i].codepoint);
+ const ChainRuleSet &rule_set = this+ruleSet[index];
+ struct ChainContextLookupContext lookup_context = {
+ {match_class, apply_func},
+ {&backtrack_class_def,
+ &input_class_def,
+ &lookahead_class_def}
+ };
+ return rule_set.apply (c, lookup_context);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return coverage.sanitize (c, this)
+ && backtrackClassDef.sanitize (c, this)
+ && inputClassDef.sanitize (c, this)
+ && lookaheadClassDef.sanitize (c, this)
+ && ruleSet.sanitize (c, this);
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 2 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetTo<ClassDef>
+ backtrackClassDef; /* Offset to glyph ClassDef table
+ * containing backtrack sequence
+ * data--from beginning of table */
+ OffsetTo<ClassDef>
+ inputClassDef; /* Offset to glyph ClassDef
+ * table containing input sequence
+ * data--from beginning of table */
+ OffsetTo<ClassDef>
+ lookaheadClassDef; /* Offset to glyph ClassDef table
+ * containing lookahead sequence
+ * data--from beginning of table */
+ OffsetArrayOf<ChainRuleSet>
+ ruleSet; /* Array of ChainRuleSet tables
+ * ordered by class */
+ public:
+ DEFINE_SIZE_ARRAY (12, ruleSet);
+};
+
+struct ChainContextFormat3
+{
+ friend struct ChainContext;
+
+ private:
+
+ inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+ {
+ TRACE_APPLY ();
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+ unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->i].codepoint);
+ if (likely (index == NOT_COVERED))
+ return false;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+ struct ChainContextLookupContext lookup_context = {
+ {match_coverage, apply_func},
+ {this, this, this}
+ };
+ return chain_context_lookup (c,
+ backtrack.len, (const USHORT *) backtrack.array,
+ input.len, (const USHORT *) input.array + 1,
+ lookahead.len, (const USHORT *) lookahead.array,
+ lookup.len, lookup.array,
+ lookup_context);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!backtrack.sanitize (c, this)) return false;
+ OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+ if (!input.sanitize (c, this)) return false;
+ OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+ if (!lookahead.sanitize (c, this)) return false;
+ ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+ return lookup.sanitize (c);
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 3 */
+ OffsetArrayOf<Coverage>
+ backtrack; /* Array of coverage tables
+ * in backtracking sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ inputX ; /* Array of coverage
+ * tables in input sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ lookaheadX; /* Array of coverage tables
+ * in lookahead sequence, in glyph
+ * sequence order */
+ ArrayOf<LookupRecord>
+ lookupX; /* Array of LookupRecords--in
+ * design order) */
+ public:
+ DEFINE_SIZE_MIN (10);
+};
+
+struct ChainContext
+{
+ 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 */
+ ChainContextFormat1 format1;
+ ChainContextFormat2 format2;
+ ChainContextFormat3 format3;
+ } u;
+};
+
+
+struct ExtensionFormat1
+{
+ friend struct Extension;
+
+ protected:
+ inline unsigned int get_type (void) const { return extensionLookupType; }
+ inline unsigned int get_offset (void) const { return extensionOffset; }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return c->check_struct (this);
+ }
+
+ private:
+ USHORT format; /* Format identifier. Set to 1. */
+ USHORT extensionLookupType; /* Lookup type of subtable referenced
+ * by ExtensionOffset (i.e. the
+ * extension subtable). */
+ ULONG extensionOffset; /* Offset to the extension subtable,
+ * of lookup type subtable. */
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct Extension
+{
+ inline unsigned int get_type (void) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.get_type ();
+ default:return 0;
+ }
+ }
+ inline unsigned int get_offset (void) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.get_offset ();
+ 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);
+ default:return true;
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ ExtensionFormat1 format1;
+ } u;
+};
+
+
+/*
+ * GSUB/GPOS Common
+ */
+
+struct GSUBGPOS
+{
+ static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB;
+ static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS;
+
+ inline unsigned int get_script_count (void) const
+ { return (this+scriptList).len; }
+ inline const Tag& get_script_tag (unsigned int i) const
+ { return (this+scriptList).get_tag (i); }
+ inline unsigned int get_script_tags (unsigned int start_offset,
+ unsigned int *script_count /* IN/OUT */,
+ hb_tag_t *script_tags /* OUT */) const
+ { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
+ inline const Script& get_script (unsigned int i) const
+ { return (this+scriptList)[i]; }
+ inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
+ { return (this+scriptList).find_index (tag, index); }
+
+ inline unsigned int get_feature_count (void) const
+ { return (this+featureList).len; }
+ inline const Tag& get_feature_tag (unsigned int i) const
+ { return (this+featureList).get_tag (i); }
+ inline unsigned int get_feature_tags (unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ hb_tag_t *feature_tags /* OUT */) const
+ { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
+ inline const Feature& get_feature (unsigned int i) const
+ { return (this+featureList)[i]; }
+ inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
+ { return (this+featureList).find_index (tag, index); }
+
+ inline unsigned int get_lookup_count (void) const
+ { return (this+lookupList).len; }
+ inline const Lookup& get_lookup (unsigned int i) const
+ { return (this+lookupList)[i]; }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return version.sanitize (c) && likely (version.major == 1)
+ && scriptList.sanitize (c, this)
+ && featureList.sanitize (c, this)
+ && lookupList.sanitize (c, this);
+ }
+
+ protected:
+ FixedVersion version; /* Version of the GSUB/GPOS table--initially set
+ * to 0x00010000 */
+ OffsetTo<ScriptList>
+ scriptList; /* ScriptList table */
+ OffsetTo<FeatureList>
+ featureList; /* FeatureList table */
+ OffsetTo<LookupList>
+ lookupList; /* LookupList table */
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh
new file mode 100644
index 0000000..b0088fb
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh
@@ -0,0 +1,114 @@
+/*
+ * 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_PRIVATE_HH
+#define HB_OT_LAYOUT_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-ot-layout.h"
+#include "hb-ot-head-private.hh"
+
+#include "hb-font-private.h"
+#include "hb-buffer-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* buffer var allocations */
+#define props_cache() var1.u16[1] /* glyph_props cache */
+
+
+/* XXX cleanup */
+typedef enum {
+ HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0x0001,
+ HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 0x0002,
+ HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE = 0x0004,
+ HB_OT_LAYOUT_GLYPH_CLASS_MARK = 0x0008,
+ HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT = 0x0010
+} hb_ot_layout_glyph_class_t;
+
+
+/*
+ * hb_ot_layout_t
+ */
+
+struct hb_ot_layout_t
+{
+ hb_blob_t *gdef_blob;
+ hb_blob_t *gsub_blob;
+ hb_blob_t *gpos_blob;
+
+ const struct GDEF *gdef;
+ const struct GSUB *gsub;
+ const struct GPOS *gpos;
+};
+
+struct hb_ot_layout_context_t
+{
+ hb_face_t *face;
+ hb_font_t *font;
+
+ /* Convert from font-space to user-space */
+ inline hb_position_t scale_x (int16_t v) { return scale (v, this->font->x_scale); }
+ inline hb_position_t scale_y (int16_t v) { return scale (v, this->font->y_scale); }
+
+ private:
+ inline hb_position_t scale (int16_t v, unsigned int scale) { return v * (int64_t) scale / this->face->head_table->get_upem (); }
+};
+
+
+HB_INTERNAL hb_ot_layout_t *
+_hb_ot_layout_new (hb_face_t *face);
+
+HB_INTERNAL void
+_hb_ot_layout_free (hb_ot_layout_t *layout);
+
+
+/*
+ * GDEF
+ */
+
+HB_INTERNAL unsigned int
+_hb_ot_layout_get_glyph_property (hb_face_t *face,
+ hb_glyph_info_t *info);
+
+HB_INTERNAL hb_bool_t
+_hb_ot_layout_check_glyph_property (hb_face_t *face,
+ hb_glyph_info_t *ginfo,
+ unsigned int lookup_props,
+ unsigned int *property_out);
+
+HB_INTERNAL hb_bool_t
+_hb_ot_layout_skip_mark (hb_face_t *face,
+ hb_glyph_info_t *ginfo,
+ unsigned int lookup_props,
+ unsigned int *property_out);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout.cc b/third_party/harfbuzz-ng/src/hb-ot-layout.cc
new file mode 100644
index 0000000..fc4b1d3
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout.cc
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ * 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
+ */
+
+#define HB_OT_LAYOUT_CC
+
+#include "hb-ot-layout-private.hh"
+
+#include "hb-ot-layout-gdef-private.hh"
+#include "hb-ot-layout-gsub-private.hh"
+#include "hb-ot-layout-gpos-private.hh"
+
+
+#include <stdlib.h>
+#include <string.h>
+
+HB_BEGIN_DECLS
+
+
+hb_ot_layout_t *
+_hb_ot_layout_new (hb_face_t *face)
+{
+ /* Remove this object altogether */
+ hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
+
+ layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_get_table (face, HB_OT_TAG_GDEF));
+ layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
+
+ layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_get_table (face, HB_OT_TAG_GSUB));
+ layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
+
+ layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_get_table (face, HB_OT_TAG_GPOS));
+ layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
+
+ return layout;
+}
+
+void
+_hb_ot_layout_free (hb_ot_layout_t *layout)
+{
+ hb_blob_unlock (layout->gdef_blob);
+ hb_blob_unlock (layout->gsub_blob);
+ hb_blob_unlock (layout->gpos_blob);
+
+ hb_blob_destroy (layout->gdef_blob);
+ hb_blob_destroy (layout->gsub_blob);
+ hb_blob_destroy (layout->gpos_blob);
+
+ free (layout);
+}
+
+static inline const GDEF&
+_get_gdef (hb_face_t *face)
+{
+ return likely (face->ot_layout && face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF);
+}
+
+static inline const GSUB&
+_get_gsub (hb_face_t *face)
+{
+ return likely (face->ot_layout && face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB);
+}
+
+static inline const GPOS&
+_get_gpos (hb_face_t *face)
+{
+ return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS);
+}
+
+
+/*
+ * GDEF
+ */
+
+hb_bool_t
+hb_ot_layout_has_glyph_classes (hb_face_t *face)
+{
+ return _get_gdef (face).has_glyph_classes ();
+}
+
+unsigned int
+_hb_ot_layout_get_glyph_property (hb_face_t *face,
+ hb_glyph_info_t *info)
+{
+ if (!info->props_cache())
+ {
+ const GDEF &gdef = _get_gdef (face);
+ info->props_cache() = gdef.get_glyph_props (info->codepoint);
+ }
+
+ return info->props_cache();
+}
+
+static hb_bool_t
+_hb_ot_layout_match_properties (hb_face_t *face,
+ hb_codepoint_t codepoint,
+ unsigned int glyph_props,
+ unsigned int lookup_props)
+{
+ /* Not covered, if, for example, glyph class is ligature and
+ * lookup_props includes LookupFlags::IgnoreLigatures
+ */
+ if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
+ return false;
+
+ if (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
+ {
+ /* If using mark filtering sets, the high short of
+ * lookup_props has the set index.
+ */
+ if (lookup_props & LookupFlag::UseMarkFilteringSet)
+ return _get_gdef (face).mark_set_covers (lookup_props >> 16, codepoint);
+
+ /* The second byte of lookup_props has the meaning
+ * "ignore marks of attachment type different than
+ * the attachment type specified."
+ */
+ if (lookup_props & LookupFlag::MarkAttachmentType && glyph_props & LookupFlag::MarkAttachmentType)
+ return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
+ }
+
+ return true;
+}
+
+hb_bool_t
+_hb_ot_layout_check_glyph_property (hb_face_t *face,
+ hb_glyph_info_t *ginfo,
+ unsigned int lookup_props,
+ unsigned int *property_out)
+{
+ unsigned int property;
+
+ property = _hb_ot_layout_get_glyph_property (face, ginfo);
+ (void) (property_out && (*property_out = property));
+
+ return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
+}
+
+hb_bool_t
+_hb_ot_layout_skip_mark (hb_face_t *face,
+ hb_glyph_info_t *ginfo,
+ unsigned int lookup_props,
+ unsigned int *property_out)
+{
+ unsigned int property;
+
+ property = _hb_ot_layout_get_glyph_property (face, ginfo);
+ (void) (property_out && (*property_out = property));
+
+ /* If it's a mark, skip it we don't accept it. */
+ if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+ return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
+
+ /* If not a mark, don't skip. */
+ return false;
+}
+
+
+
+unsigned int
+hb_ot_layout_get_attach_points (hb_face_t *face,
+ hb_codepoint_t glyph,
+ unsigned int start_offset,
+ unsigned int *point_count /* IN/OUT */,
+ unsigned int *point_array /* OUT */)
+{
+ return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
+}
+
+unsigned int
+hb_ot_layout_get_ligature_carets (hb_font_t *font,
+ hb_face_t *face,
+ hb_direction_t direction,
+ hb_codepoint_t glyph,
+ unsigned int start_offset,
+ unsigned int *caret_count /* IN/OUT */,
+ int *caret_array /* OUT */)
+{
+ hb_ot_layout_context_t c;
+ c.font = font;
+ c.face = face;
+ return _get_gdef (face).get_lig_carets (&c, direction, glyph, start_offset, caret_count, caret_array);
+}
+
+/*
+ * GSUB/GPOS
+ */
+
+static const GSUBGPOS&
+get_gsubgpos_table (hb_face_t *face,
+ hb_tag_t table_tag)
+{
+ switch (table_tag) {
+ case HB_OT_TAG_GSUB: return _get_gsub (face);
+ case HB_OT_TAG_GPOS: return _get_gpos (face);
+ default: return Null(GSUBGPOS);
+ }
+}
+
+
+unsigned int
+hb_ot_layout_table_get_script_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int start_offset,
+ unsigned int *script_count /* IN/OUT */,
+ hb_tag_t *script_tags /* OUT */)
+{
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+ return g.get_script_tags (start_offset, script_count, script_tags);
+}
+
+hb_bool_t
+hb_ot_layout_table_find_script (hb_face_t *face,
+ hb_tag_t table_tag,
+ hb_tag_t script_tag,
+ unsigned int *script_index)
+{
+ ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+ if (g.find_script_index (script_tag, script_index))
+ return TRUE;
+
+ /* try finding 'DFLT' */
+ if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
+ return FALSE;
+
+ /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
+ * including many versions of DejaVu Sans Mono! */
+ if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
+ return FALSE;
+
+ if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
+ return FALSE;
+}
+
+hb_bool_t
+hb_ot_layout_table_choose_script (hb_face_t *face,
+ hb_tag_t table_tag,
+ const hb_tag_t *script_tags,
+ unsigned int *script_index)
+{
+ ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+ while (*script_tags)
+ {
+ if (g.find_script_index (*script_tags, script_index))
+ return TRUE;
+ script_tags++;
+ }
+
+ /* try finding 'DFLT' */
+ if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
+ return FALSE;
+
+ /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
+ if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
+ return FALSE;
+
+ if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
+ return FALSE;
+}
+
+unsigned int
+hb_ot_layout_table_get_feature_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ hb_tag_t *feature_tags /* OUT */)
+{
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+ return g.get_feature_tags (start_offset, feature_count, feature_tags);
+}
+
+
+unsigned int
+hb_ot_layout_script_get_language_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int start_offset,
+ unsigned int *language_count /* IN/OUT */,
+ hb_tag_t *language_tags /* OUT */)
+{
+ const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
+
+ return s.get_lang_sys_tags (start_offset, language_count, language_tags);
+}
+
+hb_bool_t
+hb_ot_layout_script_find_language (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ hb_tag_t language_tag,
+ unsigned int *language_index)
+{
+ ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
+ const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
+
+ if (s.find_lang_sys_index (language_tag, language_index))
+ return TRUE;
+
+ /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
+ if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
+ return FALSE;
+
+ if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
+ return FALSE;
+}
+
+hb_bool_t
+hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int *feature_index)
+{
+ const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
+
+ if (feature_index) *feature_index = l.get_required_feature_index ();
+
+ return l.has_required_feature ();
+}
+
+unsigned int
+hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ unsigned int *feature_indexes /* OUT */)
+{
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+ const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+
+ return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
+}
+
+unsigned int
+hb_ot_layout_language_get_feature_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ hb_tag_t *feature_tags /* OUT */)
+{
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+ const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+
+ ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
+ unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
+
+ if (feature_tags) {
+ unsigned int count = *feature_count;
+ for (unsigned int i = 0; i < count; i++)
+ feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
+ }
+
+ return ret;
+}
+
+
+hb_bool_t
+hb_ot_layout_language_find_feature (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ hb_tag_t feature_tag,
+ unsigned int *feature_index)
+{
+ ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+ const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+
+ unsigned int num_features = l.get_feature_count ();
+ for (unsigned int i = 0; i < num_features; i++) {
+ unsigned int f_index = l.get_feature_index (i);
+
+ if (feature_tag == g.get_feature_tag (f_index)) {
+ if (feature_index) *feature_index = f_index;
+ return TRUE;
+ }
+ }
+
+ if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
+ return FALSE;
+}
+
+unsigned int
+hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int feature_index,
+ unsigned int start_offset,
+ unsigned int *lookup_count /* IN/OUT */,
+ unsigned int *lookup_indexes /* OUT */)
+{
+ const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+ const Feature &f = g.get_feature (feature_index);
+
+ return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
+}
+
+
+/*
+ * GSUB
+ */
+
+hb_bool_t
+hb_ot_layout_has_substitution (hb_face_t *face)
+{
+ return &_get_gsub (face) != &Null(GSUB);
+}
+
+hb_bool_t
+hb_ot_layout_substitute_lookup (hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int lookup_index,
+ hb_mask_t mask)
+{
+ hb_ot_layout_context_t c;
+ c.font = NULL;
+ c.face = face;
+ return _get_gsub (face).substitute_lookup (&c, buffer, lookup_index, mask);
+}
+
+
+/*
+ * GPOS
+ */
+
+hb_bool_t
+hb_ot_layout_has_positioning (hb_face_t *face)
+{
+ return &_get_gpos (face) != &Null(GPOS);
+}
+
+hb_bool_t
+hb_ot_layout_position_lookup (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int lookup_index,
+ hb_mask_t mask)
+{
+ hb_ot_layout_context_t c;
+ c.font = font;
+ c.face = face;
+ return _get_gpos (face).position_lookup (&c, buffer, lookup_index, mask);
+}
+
+void
+hb_ot_layout_position_finish (hb_buffer_t *buffer)
+{
+ GPOS::position_finish (buffer);
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout.h b/third_party/harfbuzz-ng/src/hb-ot-layout.h
new file mode 100644
index 0000000..9619eb7
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout.h
@@ -0,0 +1,196 @@
+/*
+ * 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_H
+#define HB_OT_LAYOUT_H
+
+#include "hb-common.h"
+#include "hb-buffer.h"
+#include "hb-font.h"
+
+#include "hb-ot-tag.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
+#define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
+#define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
+
+/*
+ * GDEF
+ */
+
+hb_bool_t
+hb_ot_layout_has_glyph_classes (hb_face_t *face);
+
+/* Not that useful. Provides list of attach points for a glyph that a
+ * client may want to cache */
+unsigned int
+hb_ot_layout_get_attach_points (hb_face_t *face,
+ hb_codepoint_t glyph,
+ unsigned int start_offset,
+ unsigned int *point_count /* IN/OUT */,
+ unsigned int *point_array /* OUT */);
+
+/* Ligature caret positions */
+unsigned int
+hb_ot_layout_get_ligature_carets (hb_font_t *font,
+ hb_face_t *face,
+ hb_direction_t direction,
+ hb_codepoint_t glyph,
+ unsigned int start_offset,
+ unsigned int *caret_count /* IN/OUT */,
+ int *caret_array /* OUT */);
+
+
+/*
+ * GSUB/GPOS feature query and enumeration interface
+ */
+
+#define HB_OT_LAYOUT_NO_SCRIPT_INDEX ((unsigned int) 0xFFFF)
+#define HB_OT_LAYOUT_NO_FEATURE_INDEX ((unsigned int) 0xFFFF)
+#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX ((unsigned int) 0xFFFF)
+
+unsigned int
+hb_ot_layout_table_get_script_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int start_offset,
+ unsigned int *script_count /* IN/OUT */,
+ hb_tag_t *script_tags /* OUT */);
+
+hb_bool_t
+hb_ot_layout_table_find_script (hb_face_t *face,
+ hb_tag_t table_tag,
+ hb_tag_t script_tag,
+ unsigned int *script_index);
+
+/* Like find_script, but takes zero-terminated array of scripts to test */
+hb_bool_t
+hb_ot_layout_table_choose_script (hb_face_t *face,
+ hb_tag_t table_tag,
+ const hb_tag_t *script_tags,
+ unsigned int *script_index);
+
+unsigned int
+hb_ot_layout_table_get_feature_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ hb_tag_t *feature_tags /* OUT */);
+
+unsigned int
+hb_ot_layout_script_get_language_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int start_offset,
+ unsigned int *language_count /* IN/OUT */,
+ hb_tag_t *language_tags /* OUT */);
+
+hb_bool_t
+hb_ot_layout_script_find_language (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ hb_tag_t language_tag,
+ unsigned int *language_index);
+
+hb_bool_t
+hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int *feature_index);
+
+unsigned int
+hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ unsigned int *feature_indexes /* OUT */);
+
+unsigned int
+hb_ot_layout_language_get_feature_tags (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ unsigned int start_offset,
+ unsigned int *feature_count /* IN/OUT */,
+ hb_tag_t *feature_tags /* OUT */);
+
+hb_bool_t
+hb_ot_layout_language_find_feature (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int script_index,
+ unsigned int language_index,
+ hb_tag_t feature_tag,
+ unsigned int *feature_index);
+
+unsigned int
+hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int feature_index,
+ unsigned int start_offset,
+ unsigned int *lookup_count /* IN/OUT */,
+ unsigned int *lookup_indexes /* OUT */);
+
+
+/*
+ * GSUB
+ */
+
+hb_bool_t
+hb_ot_layout_has_substitution (hb_face_t *face);
+
+hb_bool_t
+hb_ot_layout_substitute_lookup (hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int lookup_index,
+ hb_mask_t mask);
+
+/*
+ * GPOS
+ */
+
+hb_bool_t
+hb_ot_layout_has_positioning (hb_face_t *face);
+
+hb_bool_t
+hb_ot_layout_position_lookup (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int lookup_index,
+ hb_mask_t mask);
+
+/* Should be called after all the position_lookup's are done */
+void
+hb_ot_layout_position_finish (hb_buffer_t *buffer);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_LAYOUT_H */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-map-private.hh b/third_party/harfbuzz-ng/src/hb-ot-map-private.hh
new file mode 100644
index 0000000..0a1d9f6
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-map-private.hh
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_MAP_PRIVATE_HH
+#define HB_OT_MAP_PRIVATE_HH
+
+#include "hb-buffer-private.hh"
+
+#include "hb-ot-layout.h"
+
+HB_BEGIN_DECLS
+
+
+#define MAX_FEATURES 100 /* FIXME */
+#define MAX_LOOKUPS 1000 /* FIXME */
+
+static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
+
+struct hb_ot_map_t {
+
+ private:
+
+ struct feature_info_t {
+ hb_tag_t tag;
+ unsigned int seq; /* sequence#, used for stable sorting only */
+ unsigned int max_value;
+ bool global; /* whether the feature applies value to every glyph in the buffer */
+ unsigned int default_value; /* for non-global features, what should the unset glyphs take */
+
+ static int cmp (const feature_info_t *a, const feature_info_t *b)
+ { return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); }
+ };
+
+ struct feature_map_t {
+ hb_tag_t tag; /* should be first for our bsearch to work */
+ unsigned int index[2]; /* GSUB, GPOS */
+ unsigned int shift;
+ hb_mask_t mask;
+ hb_mask_t _1_mask; /* mask for value=1, for quick access */
+
+ static int cmp (const feature_map_t *a, const feature_map_t *b)
+ { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; }
+ };
+
+ struct lookup_map_t {
+ unsigned int index;
+ hb_mask_t mask;
+
+ static int cmp (const lookup_map_t *a, const lookup_map_t *b)
+ { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
+ };
+
+ HB_INTERNAL void add_lookups (hb_face_t *face,
+ unsigned int table_index,
+ unsigned int feature_index,
+ hb_mask_t mask);
+
+
+ public:
+
+ hb_ot_map_t (void) : feature_count (0) {}
+
+ void add_feature (hb_tag_t tag, unsigned int value, bool global)
+ {
+ feature_info_t *info = &feature_infos[feature_count++];
+ info->tag = tag;
+ info->seq = feature_count;
+ info->max_value = value;
+ info->global = global;
+ info->default_value = global ? value : 0;
+ }
+
+ inline void add_bool_feature (hb_tag_t tag, bool global = true)
+ { add_feature (tag, 1, global); }
+
+ HB_INTERNAL void compile (hb_face_t *face,
+ const hb_segment_properties_t *props);
+
+ inline hb_mask_t get_global_mask (void) const { return global_mask; }
+
+ inline hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift = NULL) const {
+ const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), (hb_compare_func_t) feature_map_t::cmp);
+ if (shift) *shift = map ? map->shift : 0;
+ return map ? map->mask : 0;
+ }
+
+ inline hb_mask_t get_1_mask (hb_tag_t tag) const {
+ const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), (hb_compare_func_t) feature_map_t::cmp);
+ return map ? map->_1_mask : 0;
+ }
+
+ inline void substitute (hb_face_t *face, hb_buffer_t *buffer) const {
+ for (unsigned int i = 0; i < lookup_count[0]; i++)
+ hb_ot_layout_substitute_lookup (face, buffer, lookup_maps[0][i].index, lookup_maps[0][i].mask);
+ }
+
+ inline void position (hb_font_t *font, hb_face_t *face, hb_buffer_t *buffer) const {
+ for (unsigned int i = 0; i < lookup_count[1]; i++)
+ hb_ot_layout_position_lookup (font, face, buffer, lookup_maps[1][i].index, lookup_maps[1][i].mask);
+ }
+
+ private:
+
+ hb_mask_t global_mask;
+
+ unsigned int feature_count;
+ feature_info_t feature_infos[MAX_FEATURES]; /* used before compile() only */
+ feature_map_t feature_maps[MAX_FEATURES];
+
+ lookup_map_t lookup_maps[2][MAX_LOOKUPS]; /* GSUB/GPOS */
+ unsigned int lookup_count[2];
+};
+
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_MAP_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-map.cc b/third_party/harfbuzz-ng/src/hb-ot-map.cc
new file mode 100644
index 0000000..ca96ba9
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-map.cc
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-map-private.hh"
+
+#include "hb-ot-shape-private.hh"
+
+HB_BEGIN_DECLS
+
+
+void
+hb_ot_map_t::add_lookups (hb_face_t *face,
+ unsigned int table_index,
+ unsigned int feature_index,
+ hb_mask_t mask)
+{
+ unsigned int i = MAX_LOOKUPS - lookup_count[table_index];
+ lookup_map_t *lookups = lookup_maps[table_index] + lookup_count[table_index];
+
+ unsigned int *lookup_indices = (unsigned int *) lookups;
+
+ hb_ot_layout_feature_get_lookup_indexes (face,
+ table_tags[table_index],
+ feature_index,
+ 0, &i,
+ lookup_indices);
+
+ lookup_count[table_index] += i;
+
+ while (i--) {
+ lookups[i].mask = mask;
+ lookups[i].index = lookup_indices[i];
+ }
+}
+
+
+void
+hb_ot_map_t::compile (hb_face_t *face,
+ const hb_segment_properties_t *props)
+{
+ global_mask = 1;
+ lookup_count[0] = lookup_count[1] = 0;
+
+ if (!feature_count)
+ return;
+
+
+ /* Fetch script/language indices for GSUB/GPOS. We need these later to skip
+ * features not available in either table and not waste precious bits for them. */
+
+ const hb_tag_t *script_tags;
+ hb_tag_t language_tag;
+
+ script_tags = hb_ot_tags_from_script (props->script);
+ language_tag = hb_ot_tag_from_language (props->language);
+
+ unsigned int script_index[2], language_index[2];
+ for (unsigned int table_index = 0; table_index < 2; table_index++) {
+ hb_tag_t table_tag = table_tags[table_index];
+ hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index]);
+ hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+ }
+
+
+ /* Sort features and merge duplicates */
+ qsort (feature_infos, feature_count, sizeof (feature_infos[0]), (hb_compare_func_t) feature_info_t::cmp);
+ unsigned int j = 0;
+ for (unsigned int i = 1; i < feature_count; i++)
+ if (feature_infos[i].tag != feature_infos[j].tag)
+ feature_infos[++j] = feature_infos[i];
+ else {
+ if (feature_infos[i].global)
+ feature_infos[j] = feature_infos[i];
+ else {
+ feature_infos[j].global = false;
+ feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value);
+ /* Inherit default_value from j */
+ }
+ }
+ feature_count = j + 1;
+
+
+ /* Allocate bits now */
+ unsigned int next_bit = 1;
+ j = 0;
+ for (unsigned int i = 0; i < feature_count; i++) {
+ const feature_info_t *info = &feature_infos[i];
+
+ unsigned int bits_needed;
+
+ if (info->global && info->max_value == 1)
+ /* Uses the global bit */
+ bits_needed = 0;
+ else
+ bits_needed = _hb_bit_storage (info->max_value);
+
+ if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
+ continue; /* Feature disabled, or not enough bits. */
+
+
+ bool found = false;
+ unsigned int feature_index[2];
+ for (unsigned int table_index = 0; table_index < 2; table_index++)
+ found |= hb_ot_layout_language_find_feature (face,
+ table_tags[table_index],
+ script_index[table_index],
+ language_index[table_index],
+ info->tag,
+ &feature_index[table_index]);
+ if (!found)
+ continue;
+
+
+ feature_map_t *map = &feature_maps[j++];
+
+ map->tag = info->tag;
+ map->index[0] = feature_index[0];
+ map->index[1] = feature_index[1];
+ if (info->global && info->max_value == 1) {
+ /* Uses the global bit */
+ map->shift = 0;
+ map->mask = 1;
+ } else {
+ map->shift = next_bit;
+ map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
+ next_bit += bits_needed;
+ if (info->global)
+ global_mask |= (info->default_value << map->shift) & map->mask;
+ }
+ map->_1_mask = (1 << map->shift) & map->mask;
+
+ }
+ feature_count = j;
+
+
+ for (unsigned int table_index = 0; table_index < 2; table_index++) {
+ hb_tag_t table_tag = table_tags[table_index];
+
+ /* Collect lookup indices for features */
+
+ unsigned int required_feature_index;
+ if (hb_ot_layout_language_get_required_feature_index (face,
+ table_tag,
+ script_index[table_index],
+ language_index[table_index],
+ &required_feature_index))
+ add_lookups (face, table_index, required_feature_index, 1);
+
+ for (unsigned i = 0; i < feature_count; i++)
+ add_lookups (face, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
+
+ /* Sort lookups and merge duplicates */
+ qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), (hb_compare_func_t) lookup_map_t::cmp);
+ if (lookup_count[table_index])
+ {
+ unsigned int j = 0;
+ for (unsigned int i = 1; i < lookup_count[table_index]; i++)
+ if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
+ lookup_maps[table_index][++j] = lookup_maps[table_index][i];
+ else
+ lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
+ j++;
+ lookup_count[table_index] = j;
+ }
+ }
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-table.h b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-table.h
new file mode 100644
index 0000000..523fc84
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-table.h
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H
+#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H
+
+#include "hb-private.h"
+
+HB_BEGIN_DECLS
+
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ * ./gen-arabic-joining-table.py < ArabicShaping.txt
+ *
+ * on the ArabicShaping.txt file with the header:
+ *
+ * # ArabicShaping-6.1.0.txt
+ * # Date: 2010-11-09, 12:10:00 PST [KW]
+ */
+static const uint8_t joining_table[] =
+{
+
+ /* Arabic characters */
+
+ JOINING_TYPE_U, /* 0600; ARABIC NUMBER SIGN; U; No_Joining_Group */
+ JOINING_TYPE_U, /* 0601; ARABIC SIGN SANAH; U; No_Joining_Group */
+ JOINING_TYPE_U, /* 0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group */
+ JOINING_TYPE_U, /* 0603; ARABIC SIGN SAFHA; U; No_Joining_Group */
+ JOINING_TYPE_X, /* 0604 */
+ JOINING_TYPE_X, /* 0605 */
+ JOINING_TYPE_X, /* 0606 */
+ JOINING_TYPE_X, /* 0607 */
+ JOINING_TYPE_U, /* 0608; ARABIC RAY; U; No_Joining_Group */
+ JOINING_TYPE_X, /* 0609 */
+ JOINING_TYPE_X, /* 060A */
+ JOINING_TYPE_U, /* 060B; AFGHANI SIGN; U; No_Joining_Group */
+ JOINING_TYPE_X, /* 060C */
+ JOINING_TYPE_X, /* 060D */
+ JOINING_TYPE_X, /* 060E */
+ JOINING_TYPE_X, /* 060F */
+ JOINING_TYPE_X, /* 0610 */
+ JOINING_TYPE_X, /* 0611 */
+ JOINING_TYPE_X, /* 0612 */
+ JOINING_TYPE_X, /* 0613 */
+ JOINING_TYPE_X, /* 0614 */
+ JOINING_TYPE_X, /* 0615 */
+ JOINING_TYPE_X, /* 0616 */
+ JOINING_TYPE_X, /* 0617 */
+ JOINING_TYPE_X, /* 0618 */
+ JOINING_TYPE_X, /* 0619 */
+ JOINING_TYPE_X, /* 061A */
+ JOINING_TYPE_X, /* 061B */
+ JOINING_TYPE_X, /* 061C */
+ JOINING_TYPE_X, /* 061D */
+ JOINING_TYPE_X, /* 061E */
+ JOINING_TYPE_X, /* 061F */
+ JOINING_TYPE_D, /* 0620; YEH WITH RING; D; YEH */
+ JOINING_TYPE_U, /* 0621; HAMZA; U; No_Joining_Group */
+ JOINING_TYPE_R, /* 0622; MADDA ON ALEF; R; ALEF */
+ JOINING_TYPE_R, /* 0623; HAMZA ON ALEF; R; ALEF */
+ JOINING_TYPE_R, /* 0624; HAMZA ON WAW; R; WAW */
+ JOINING_TYPE_R, /* 0625; HAMZA UNDER ALEF; R; ALEF */
+ JOINING_TYPE_D, /* 0626; HAMZA ON YEH; D; YEH */
+ JOINING_TYPE_R, /* 0627; ALEF; R; ALEF */
+ JOINING_TYPE_D, /* 0628; BEH; D; BEH */
+ JOINING_TYPE_R, /* 0629; TEH MARBUTA; R; TEH MARBUTA */
+ JOINING_TYPE_D, /* 062A; TEH; D; BEH */
+ JOINING_TYPE_D, /* 062B; THEH; D; BEH */
+ JOINING_TYPE_D, /* 062C; JEEM; D; HAH */
+ JOINING_TYPE_D, /* 062D; HAH; D; HAH */
+ JOINING_TYPE_D, /* 062E; KHAH; D; HAH */
+ JOINING_TYPE_R, /* 062F; DAL; R; DAL */
+ JOINING_TYPE_R, /* 0630; THAL; R; DAL */
+ JOINING_TYPE_R, /* 0631; REH; R; REH */
+ JOINING_TYPE_R, /* 0632; ZAIN; R; REH */
+ JOINING_TYPE_D, /* 0633; SEEN; D; SEEN */
+ JOINING_TYPE_D, /* 0634; SHEEN; D; SEEN */
+ JOINING_TYPE_D, /* 0635; SAD; D; SAD */
+ JOINING_TYPE_D, /* 0636; DAD; D; SAD */
+ JOINING_TYPE_D, /* 0637; TAH; D; TAH */
+ JOINING_TYPE_D, /* 0638; ZAH; D; TAH */
+ JOINING_TYPE_D, /* 0639; AIN; D; AIN */
+ JOINING_TYPE_D, /* 063A; GHAIN; D; AIN */
+ JOINING_TYPE_D, /* 063B; KEHEH WITH 2 DOTS ABOVE; D; GAF */
+ JOINING_TYPE_D, /* 063C; KEHEH WITH 3 DOTS BELOW; D; GAF */
+ JOINING_TYPE_D, /* 063D; FARSI YEH WITH INVERTED V; D; FARSI YEH */
+ JOINING_TYPE_D, /* 063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH */
+ JOINING_TYPE_D, /* 063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH */
+ JOINING_TYPE_C, /* 0640; TATWEEL; C; No_Joining_Group */
+ JOINING_TYPE_D, /* 0641; FEH; D; FEH */
+ JOINING_TYPE_D, /* 0642; QAF; D; QAF */
+ JOINING_TYPE_D, /* 0643; KAF; D; KAF */
+ JOINING_TYPE_D, /* 0644; LAM; D; LAM */
+ JOINING_TYPE_D, /* 0645; MEEM; D; MEEM */
+ JOINING_TYPE_D, /* 0646; NOON; D; NOON */
+ JOINING_TYPE_D, /* 0647; HEH; D; HEH */
+ JOINING_TYPE_R, /* 0648; WAW; R; WAW */
+ JOINING_TYPE_D, /* 0649; ALEF MAKSURA; D; YEH */
+ JOINING_TYPE_D, /* 064A; YEH; D; YEH */
+ JOINING_TYPE_X, /* 064B */
+ JOINING_TYPE_X, /* 064C */
+ JOINING_TYPE_X, /* 064D */
+ JOINING_TYPE_X, /* 064E */
+ JOINING_TYPE_X, /* 064F */
+ JOINING_TYPE_X, /* 0650 */
+ JOINING_TYPE_X, /* 0651 */
+ JOINING_TYPE_X, /* 0652 */
+ JOINING_TYPE_X, /* 0653 */
+ JOINING_TYPE_X, /* 0654 */
+ JOINING_TYPE_X, /* 0655 */
+ JOINING_TYPE_X, /* 0656 */
+ JOINING_TYPE_X, /* 0657 */
+ JOINING_TYPE_X, /* 0658 */
+ JOINING_TYPE_X, /* 0659 */
+ JOINING_TYPE_X, /* 065A */
+ JOINING_TYPE_X, /* 065B */
+ JOINING_TYPE_X, /* 065C */
+ JOINING_TYPE_X, /* 065D */
+ JOINING_TYPE_X, /* 065E */
+ JOINING_TYPE_X, /* 065F */
+ JOINING_TYPE_X, /* 0660 */
+ JOINING_TYPE_X, /* 0661 */
+ JOINING_TYPE_X, /* 0662 */
+ JOINING_TYPE_X, /* 0663 */
+ JOINING_TYPE_X, /* 0664 */
+ JOINING_TYPE_X, /* 0665 */
+ JOINING_TYPE_X, /* 0666 */
+ JOINING_TYPE_X, /* 0667 */
+ JOINING_TYPE_X, /* 0668 */
+ JOINING_TYPE_X, /* 0669 */
+ JOINING_TYPE_X, /* 066A */
+ JOINING_TYPE_X, /* 066B */
+ JOINING_TYPE_X, /* 066C */
+ JOINING_TYPE_X, /* 066D */
+ JOINING_TYPE_D, /* 066E; DOTLESS BEH; D; BEH */
+ JOINING_TYPE_D, /* 066F; DOTLESS QAF; D; QAF */
+ JOINING_TYPE_X, /* 0670 */
+ JOINING_TYPE_R, /* 0671; HAMZAT WASL ON ALEF; R; ALEF */
+ JOINING_TYPE_R, /* 0672; WAVY HAMZA ON ALEF; R; ALEF */
+ JOINING_TYPE_R, /* 0673; WAVY HAMZA UNDER ALEF; R; ALEF */
+ JOINING_TYPE_U, /* 0674; HIGH HAMZA; U; No_Joining_Group */
+ JOINING_TYPE_R, /* 0675; HIGH HAMZA ALEF; R; ALEF */
+ JOINING_TYPE_R, /* 0676; HIGH HAMZA WAW; R; WAW */
+ JOINING_TYPE_R, /* 0677; HIGH HAMZA WAW WITH DAMMA; R; WAW */
+ JOINING_TYPE_D, /* 0678; HIGH HAMZA YEH; D; YEH */
+ JOINING_TYPE_D, /* 0679; TEH WITH SMALL TAH; D; BEH */
+ JOINING_TYPE_D, /* 067A; TEH WITH 2 DOTS VERTICAL ABOVE; D; BEH */
+ JOINING_TYPE_D, /* 067B; BEH WITH 2 DOTS VERTICAL BELOW; D; BEH */
+ JOINING_TYPE_D, /* 067C; TEH WITH RING; D; BEH */
+ JOINING_TYPE_D, /* 067D; TEH WITH 3 DOTS ABOVE DOWNWARD; D; BEH */
+ JOINING_TYPE_D, /* 067E; TEH WITH 3 DOTS BELOW; D; BEH */
+ JOINING_TYPE_D, /* 067F; TEH WITH 4 DOTS ABOVE; D; BEH */
+ JOINING_TYPE_D, /* 0680; BEH WITH 4 DOTS BELOW; D; BEH */
+ JOINING_TYPE_D, /* 0681; HAMZA ON HAH; D; HAH */
+ JOINING_TYPE_D, /* 0682; HAH WITH 2 DOTS VERTICAL ABOVE; D; HAH */
+ JOINING_TYPE_D, /* 0683; HAH WITH MIDDLE 2 DOTS; D; HAH */
+ JOINING_TYPE_D, /* 0684; HAH WITH MIDDLE 2 DOTS VERTICAL; D; HAH */
+ JOINING_TYPE_D, /* 0685; HAH WITH 3 DOTS ABOVE; D; HAH */
+ JOINING_TYPE_D, /* 0686; HAH WITH MIDDLE 3 DOTS DOWNWARD; D; HAH */
+ JOINING_TYPE_D, /* 0687; HAH WITH MIDDLE 4 DOTS; D; HAH */
+ JOINING_TYPE_R, /* 0688; DAL WITH SMALL TAH; R; DAL */
+ JOINING_TYPE_R, /* 0689; DAL WITH RING; R; DAL */
+ JOINING_TYPE_R, /* 068A; DAL WITH DOT BELOW; R; DAL */
+ JOINING_TYPE_R, /* 068B; DAL WITH DOT BELOW AND SMALL TAH; R; DAL */
+ JOINING_TYPE_R, /* 068C; DAL WITH 2 DOTS ABOVE; R; DAL */
+ JOINING_TYPE_R, /* 068D; DAL WITH 2 DOTS BELOW; R; DAL */
+ JOINING_TYPE_R, /* 068E; DAL WITH 3 DOTS ABOVE; R; DAL */
+ JOINING_TYPE_R, /* 068F; DAL WITH 3 DOTS ABOVE DOWNWARD; R; DAL */
+ JOINING_TYPE_R, /* 0690; DAL WITH 4 DOTS ABOVE; R; DAL */
+ JOINING_TYPE_R, /* 0691; REH WITH SMALL TAH; R; REH */
+ JOINING_TYPE_R, /* 0692; REH WITH SMALL V; R; REH */
+ JOINING_TYPE_R, /* 0693; REH WITH RING; R; REH */
+ JOINING_TYPE_R, /* 0694; REH WITH DOT BELOW; R; REH */
+ JOINING_TYPE_R, /* 0695; REH WITH SMALL V BELOW; R; REH */
+ JOINING_TYPE_R, /* 0696; REH WITH DOT BELOW AND DOT ABOVE; R; REH */
+ JOINING_TYPE_R, /* 0697; REH WITH 2 DOTS ABOVE; R; REH */
+ JOINING_TYPE_R, /* 0698; REH WITH 3 DOTS ABOVE; R; REH */
+ JOINING_TYPE_R, /* 0699; REH WITH 4 DOTS ABOVE; R; REH */
+ JOINING_TYPE_D, /* 069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 069B; SEEN WITH 3 DOTS BELOW; D; SEEN */
+ JOINING_TYPE_D, /* 069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 069D; SAD WITH 2 DOTS BELOW; D; SAD */
+ JOINING_TYPE_D, /* 069E; SAD WITH 3 DOTS ABOVE; D; SAD */
+ JOINING_TYPE_D, /* 069F; TAH WITH 3 DOTS ABOVE; D; TAH */
+ JOINING_TYPE_D, /* 06A0; AIN WITH 3 DOTS ABOVE; D; AIN */
+ JOINING_TYPE_D, /* 06A1; DOTLESS FEH; D; FEH */
+ JOINING_TYPE_D, /* 06A2; FEH WITH DOT MOVED BELOW; D; FEH */
+ JOINING_TYPE_D, /* 06A3; FEH WITH DOT BELOW; D; FEH */
+ JOINING_TYPE_D, /* 06A4; FEH WITH 3 DOTS ABOVE; D; FEH */
+ JOINING_TYPE_D, /* 06A5; FEH WITH 3 DOTS BELOW; D; FEH */
+ JOINING_TYPE_D, /* 06A6; FEH WITH 4 DOTS ABOVE; D; FEH */
+ JOINING_TYPE_D, /* 06A7; QAF WITH DOT ABOVE; D; QAF */
+ JOINING_TYPE_D, /* 06A8; QAF WITH 3 DOTS ABOVE; D; QAF */
+ JOINING_TYPE_D, /* 06A9; KEHEH; D; GAF */
+ JOINING_TYPE_D, /* 06AA; SWASH KAF; D; SWASH KAF */
+ JOINING_TYPE_D, /* 06AB; KAF WITH RING; D; GAF */
+ JOINING_TYPE_D, /* 06AC; KAF WITH DOT ABOVE; D; KAF */
+ JOINING_TYPE_D, /* 06AD; KAF WITH 3 DOTS ABOVE; D; KAF */
+ JOINING_TYPE_D, /* 06AE; KAF WITH 3 DOTS BELOW; D; KAF */
+ JOINING_TYPE_D, /* 06AF; GAF; D; GAF */
+ JOINING_TYPE_D, /* 06B0; GAF WITH RING; D; GAF */
+ JOINING_TYPE_D, /* 06B1; GAF WITH 2 DOTS ABOVE; D; GAF */
+ JOINING_TYPE_D, /* 06B2; GAF WITH 2 DOTS BELOW; D; GAF */
+ JOINING_TYPE_D, /* 06B3; GAF WITH 2 DOTS VERTICAL BELOW; D; GAF */
+ JOINING_TYPE_D, /* 06B4; GAF WITH 3 DOTS ABOVE; D; GAF */
+ JOINING_TYPE_D, /* 06B5; LAM WITH SMALL V; D; LAM */
+ JOINING_TYPE_D, /* 06B6; LAM WITH DOT ABOVE; D; LAM */
+ JOINING_TYPE_D, /* 06B7; LAM WITH 3 DOTS ABOVE; D; LAM */
+ JOINING_TYPE_D, /* 06B8; LAM WITH 3 DOTS BELOW; D; LAM */
+ JOINING_TYPE_D, /* 06B9; NOON WITH DOT BELOW; D; NOON */
+ JOINING_TYPE_D, /* 06BA; DOTLESS NOON; D; NOON */
+ JOINING_TYPE_D, /* 06BB; DOTLESS NOON WITH SMALL TAH; D; NOON */
+ JOINING_TYPE_D, /* 06BC; NOON WITH RING; D; NOON */
+ JOINING_TYPE_D, /* 06BD; NYA; D; NYA */
+ JOINING_TYPE_D, /* 06BE; KNOTTED HEH; D; KNOTTED HEH */
+ JOINING_TYPE_D, /* 06BF; HAH WITH MIDDLE 3 DOTS DOWNWARD AND DOT ABOVE; D; HAH */
+ JOINING_TYPE_R, /* 06C0; HAMZA ON HEH; R; TEH MARBUTA */
+ JOINING_TYPE_D, /* 06C1; HEH GOAL; D; HEH GOAL */
+ JOINING_TYPE_D, /* 06C2; HAMZA ON HEH GOAL; D; HEH GOAL */
+ JOINING_TYPE_R, /* 06C3; TEH MARBUTA GOAL; R; TEH MARBUTA GOAL */
+ JOINING_TYPE_R, /* 06C4; WAW WITH RING; R; WAW */
+ JOINING_TYPE_R, /* 06C5; WAW WITH BAR; R; WAW */
+ JOINING_TYPE_R, /* 06C6; WAW WITH SMALL V; R; WAW */
+ JOINING_TYPE_R, /* 06C7; WAW WITH DAMMA; R; WAW */
+ JOINING_TYPE_R, /* 06C8; WAW WITH ALEF ABOVE; R; WAW */
+ JOINING_TYPE_R, /* 06C9; WAW WITH INVERTED SMALL V; R; WAW */
+ JOINING_TYPE_R, /* 06CA; WAW WITH 2 DOTS ABOVE; R; WAW */
+ JOINING_TYPE_R, /* 06CB; WAW WITH 3 DOTS ABOVE; R; WAW */
+ JOINING_TYPE_D, /* 06CC; FARSI YEH; D; FARSI YEH */
+ JOINING_TYPE_R, /* 06CD; YEH WITH TAIL; R; YEH WITH TAIL */
+ JOINING_TYPE_D, /* 06CE; FARSI YEH WITH SMALL V; D; FARSI YEH */
+ JOINING_TYPE_R, /* 06CF; WAW WITH DOT ABOVE; R; WAW */
+ JOINING_TYPE_D, /* 06D0; YEH WITH 2 DOTS VERTICAL BELOW; D; YEH */
+ JOINING_TYPE_D, /* 06D1; YEH WITH 3 DOTS BELOW; D; YEH */
+ JOINING_TYPE_R, /* 06D2; YEH BARREE; R; YEH BARREE */
+ JOINING_TYPE_R, /* 06D3; HAMZA ON YEH BARREE; R; YEH BARREE */
+ JOINING_TYPE_X, /* 06D4 */
+ JOINING_TYPE_R, /* 06D5; AE; R; TEH MARBUTA */
+ JOINING_TYPE_X, /* 06D6 */
+ JOINING_TYPE_X, /* 06D7 */
+ JOINING_TYPE_X, /* 06D8 */
+ JOINING_TYPE_X, /* 06D9 */
+ JOINING_TYPE_X, /* 06DA */
+ JOINING_TYPE_X, /* 06DB */
+ JOINING_TYPE_X, /* 06DC */
+ JOINING_TYPE_U, /* 06DD; ARABIC END OF AYAH; U; No_Joining_Group */
+ JOINING_TYPE_X, /* 06DE */
+ JOINING_TYPE_X, /* 06DF */
+ JOINING_TYPE_X, /* 06E0 */
+ JOINING_TYPE_X, /* 06E1 */
+ JOINING_TYPE_X, /* 06E2 */
+ JOINING_TYPE_X, /* 06E3 */
+ JOINING_TYPE_X, /* 06E4 */
+ JOINING_TYPE_X, /* 06E5 */
+ JOINING_TYPE_X, /* 06E6 */
+ JOINING_TYPE_X, /* 06E7 */
+ JOINING_TYPE_X, /* 06E8 */
+ JOINING_TYPE_X, /* 06E9 */
+ JOINING_TYPE_X, /* 06EA */
+ JOINING_TYPE_X, /* 06EB */
+ JOINING_TYPE_X, /* 06EC */
+ JOINING_TYPE_X, /* 06ED */
+ JOINING_TYPE_R, /* 06EE; DAL WITH INVERTED V; R; DAL */
+ JOINING_TYPE_R, /* 06EF; REH WITH INVERTED V; R; REH */
+ JOINING_TYPE_X, /* 06F0 */
+ JOINING_TYPE_X, /* 06F1 */
+ JOINING_TYPE_X, /* 06F2 */
+ JOINING_TYPE_X, /* 06F3 */
+ JOINING_TYPE_X, /* 06F4 */
+ JOINING_TYPE_X, /* 06F5 */
+ JOINING_TYPE_X, /* 06F6 */
+ JOINING_TYPE_X, /* 06F7 */
+ JOINING_TYPE_X, /* 06F8 */
+ JOINING_TYPE_X, /* 06F9 */
+ JOINING_TYPE_D, /* 06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 06FB; DAD WITH DOT BELOW; D; SAD */
+ JOINING_TYPE_D, /* 06FC; GHAIN WITH DOT BELOW; D; AIN */
+ JOINING_TYPE_X, /* 06FD */
+ JOINING_TYPE_X, /* 06FE */
+ JOINING_TYPE_D, /* 06FF; HEH WITH INVERTED V; D; KNOTTED HEH */
+
+ /* Syriac characters */
+
+ JOINING_TYPE_X, /* 0700 */
+ JOINING_TYPE_X, /* 0701 */
+ JOINING_TYPE_X, /* 0702 */
+ JOINING_TYPE_X, /* 0703 */
+ JOINING_TYPE_X, /* 0704 */
+ JOINING_TYPE_X, /* 0705 */
+ JOINING_TYPE_X, /* 0706 */
+ JOINING_TYPE_X, /* 0707 */
+ JOINING_TYPE_X, /* 0708 */
+ JOINING_TYPE_X, /* 0709 */
+ JOINING_TYPE_X, /* 070A */
+ JOINING_TYPE_X, /* 070B */
+ JOINING_TYPE_X, /* 070C */
+ JOINING_TYPE_X, /* 070D */
+ JOINING_TYPE_X, /* 070E */
+ JOINING_TYPE_X, /* 070F */
+ JOINING_GROUP_ALAPH, /* 0710; ALAPH; R; ALAPH */
+ JOINING_TYPE_X, /* 0711 */
+ JOINING_TYPE_D, /* 0712; BETH; D; BETH */
+ JOINING_TYPE_D, /* 0713; GAMAL; D; GAMAL */
+ JOINING_TYPE_D, /* 0714; GAMAL GARSHUNI; D; GAMAL */
+ JOINING_GROUP_DALATH_RISH, /* 0715; DALATH; R; DALATH RISH */
+ JOINING_GROUP_DALATH_RISH, /* 0716; DOTLESS DALATH RISH; R; DALATH RISH */
+ JOINING_TYPE_R, /* 0717; HE; R; HE */
+ JOINING_TYPE_R, /* 0718; WAW; R; SYRIAC WAW */
+ JOINING_TYPE_R, /* 0719; ZAIN; R; ZAIN */
+ JOINING_TYPE_D, /* 071A; HETH; D; HETH */
+ JOINING_TYPE_D, /* 071B; TETH; D; TETH */
+ JOINING_TYPE_D, /* 071C; TETH GARSHUNI; D; TETH */
+ JOINING_TYPE_D, /* 071D; YUDH; D; YUDH */
+ JOINING_TYPE_R, /* 071E; YUDH HE; R; YUDH HE */
+ JOINING_TYPE_D, /* 071F; KAPH; D; KAPH */
+ JOINING_TYPE_D, /* 0720; LAMADH; D; LAMADH */
+ JOINING_TYPE_D, /* 0721; MIM; D; MIM */
+ JOINING_TYPE_D, /* 0722; NUN; D; NUN */
+ JOINING_TYPE_D, /* 0723; SEMKATH; D; SEMKATH */
+ JOINING_TYPE_D, /* 0724; FINAL SEMKATH; D; FINAL SEMKATH */
+ JOINING_TYPE_D, /* 0725; E; D; E */
+ JOINING_TYPE_D, /* 0726; PE; D; PE */
+ JOINING_TYPE_D, /* 0727; REVERSED PE; D; REVERSED PE */
+ JOINING_TYPE_R, /* 0728; SADHE; R; SADHE */
+ JOINING_TYPE_D, /* 0729; QAPH; D; QAPH */
+ JOINING_GROUP_DALATH_RISH, /* 072A; RISH; R; DALATH RISH */
+ JOINING_TYPE_D, /* 072B; SHIN; D; SHIN */
+ JOINING_TYPE_R, /* 072C; TAW; R; TAW */
+ JOINING_TYPE_D, /* 072D; PERSIAN BHETH; D; BETH */
+ JOINING_TYPE_D, /* 072E; PERSIAN GHAMAL; D; GAMAL */
+ JOINING_GROUP_DALATH_RISH, /* 072F; PERSIAN DHALATH; R; DALATH RISH */
+ JOINING_TYPE_X, /* 0730 */
+ JOINING_TYPE_X, /* 0731 */
+ JOINING_TYPE_X, /* 0732 */
+ JOINING_TYPE_X, /* 0733 */
+ JOINING_TYPE_X, /* 0734 */
+ JOINING_TYPE_X, /* 0735 */
+ JOINING_TYPE_X, /* 0736 */
+ JOINING_TYPE_X, /* 0737 */
+ JOINING_TYPE_X, /* 0738 */
+ JOINING_TYPE_X, /* 0739 */
+ JOINING_TYPE_X, /* 073A */
+ JOINING_TYPE_X, /* 073B */
+ JOINING_TYPE_X, /* 073C */
+ JOINING_TYPE_X, /* 073D */
+ JOINING_TYPE_X, /* 073E */
+ JOINING_TYPE_X, /* 073F */
+ JOINING_TYPE_X, /* 0740 */
+ JOINING_TYPE_X, /* 0741 */
+ JOINING_TYPE_X, /* 0742 */
+ JOINING_TYPE_X, /* 0743 */
+ JOINING_TYPE_X, /* 0744 */
+ JOINING_TYPE_X, /* 0745 */
+ JOINING_TYPE_X, /* 0746 */
+ JOINING_TYPE_X, /* 0747 */
+ JOINING_TYPE_X, /* 0748 */
+ JOINING_TYPE_X, /* 0749 */
+ JOINING_TYPE_X, /* 074A */
+ JOINING_TYPE_X, /* 074B */
+ JOINING_TYPE_X, /* 074C */
+ JOINING_TYPE_R, /* 074D; SOGDIAN ZHAIN; R; ZHAIN */
+ JOINING_TYPE_D, /* 074E; SOGDIAN KHAPH; D; KHAPH */
+ JOINING_TYPE_D, /* 074F; SOGDIAN FE; D; FE */
+
+ /* Arabic supplement characters */
+
+ JOINING_TYPE_D, /* 0750; BEH WITH 3 DOTS HORIZONTALLY BELOW; D; BEH */
+ JOINING_TYPE_D, /* 0751; BEH WITH DOT BELOW AND 3 DOTS ABOVE; D; BEH */
+ JOINING_TYPE_D, /* 0752; BEH WITH 3 DOTS POINTING UPWARDS BELOW; D; BEH */
+ JOINING_TYPE_D, /* 0753; BEH WITH 3 DOTS POINTING UPWARDS BELOW AND 2 DOTS ABOVE; D; BEH */
+ JOINING_TYPE_D, /* 0754; BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH */
+ JOINING_TYPE_D, /* 0755; BEH WITH INVERTED SMALL V BELOW; D; BEH */
+ JOINING_TYPE_D, /* 0756; BEH WITH SMALL V; D; BEH */
+ JOINING_TYPE_D, /* 0757; HAH WITH 2 DOTS ABOVE; D; HAH */
+ JOINING_TYPE_D, /* 0758; HAH WITH 3 DOTS POINTING UPWARDS BELOW; D; HAH */
+ JOINING_TYPE_R, /* 0759; DAL WITH 2 DOTS VERTICALLY BELOW AND SMALL TAH; R; DAL */
+ JOINING_TYPE_R, /* 075A; DAL WITH INVERTED SMALL V BELOW; R; DAL */
+ JOINING_TYPE_R, /* 075B; REH WITH STROKE; R; REH */
+ JOINING_TYPE_D, /* 075C; SEEN WITH 4 DOTS ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 075D; AIN WITH 2 DOTS ABOVE; D; AIN */
+ JOINING_TYPE_D, /* 075E; AIN WITH 3 DOTS POINTING DOWNWARDS ABOVE; D; AIN */
+ JOINING_TYPE_D, /* 075F; AIN WITH 2 DOTS VERTICALLY ABOVE; D; AIN */
+ JOINING_TYPE_D, /* 0760; FEH WITH 2 DOTS BELOW; D; FEH */
+ JOINING_TYPE_D, /* 0761; FEH WITH 3 DOTS POINTING UPWARDS BELOW; D; FEH */
+ JOINING_TYPE_D, /* 0762; KEHEH WITH DOT ABOVE; D; GAF */
+ JOINING_TYPE_D, /* 0763; KEHEH WITH 3 DOTS ABOVE; D; GAF */
+ JOINING_TYPE_D, /* 0764; KEHEH WITH 3 DOTS POINTING UPWARDS BELOW; D; GAF */
+ JOINING_TYPE_D, /* 0765; MEEM WITH DOT ABOVE; D; MEEM */
+ JOINING_TYPE_D, /* 0766; MEEM WITH DOT BELOW; D; MEEM */
+ JOINING_TYPE_D, /* 0767; NOON WITH 2 DOTS BELOW; D; NOON */
+ JOINING_TYPE_D, /* 0768; NOON WITH SMALL TAH; D; NOON */
+ JOINING_TYPE_D, /* 0769; NOON WITH SMALL V; D; NOON */
+ JOINING_TYPE_D, /* 076A; LAM WITH BAR; D; LAM */
+ JOINING_TYPE_R, /* 076B; REH WITH 2 DOTS VERTICALLY ABOVE; R; REH */
+ JOINING_TYPE_R, /* 076C; REH WITH HAMZA ABOVE; R; REH */
+ JOINING_TYPE_D, /* 076D; SEEN WITH 2 DOTS VERTICALLY ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 076E; HAH WITH SMALL TAH BELOW; D; HAH */
+ JOINING_TYPE_D, /* 076F; HAH WITH SMALL TAH AND 2 DOTS; D; HAH */
+ JOINING_TYPE_D, /* 0770; SEEN WITH SMALL TAH AND 2 DOTS; D; SEEN */
+ JOINING_TYPE_R, /* 0771; REH WITH SMALL TAH AND 2 DOTS; R; REH */
+ JOINING_TYPE_D, /* 0772; HAH WITH SMALL TAH ABOVE; D; HAH */
+ JOINING_TYPE_R, /* 0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF */
+ JOINING_TYPE_R, /* 0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF */
+ JOINING_TYPE_D, /* 0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH */
+ JOINING_TYPE_D, /* 0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH */
+ JOINING_TYPE_D, /* 0777; YEH WITH DIGIT FOUR BELOW; D; YEH */
+ JOINING_TYPE_R, /* 0778; WAW WITH DIGIT TWO ABOVE; R; WAW */
+ JOINING_TYPE_R, /* 0779; WAW WITH DIGIT THREE ABOVE; R; WAW */
+ JOINING_TYPE_D, /* 077A; YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE */
+ JOINING_TYPE_D, /* 077B; YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE */
+ JOINING_TYPE_D, /* 077C; HAH WITH DIGIT FOUR BELOW; D; HAH */
+ JOINING_TYPE_D, /* 077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN */
+ JOINING_TYPE_D, /* 077E; SEEN WITH INVERTED V; D; SEEN */
+ JOINING_TYPE_D, /* 077F; KAF WITH 2 DOTS ABOVE; D; KAF */
+
+ /* N'Ko Characters */
+
+ JOINING_TYPE_X, /* 0780 */
+ JOINING_TYPE_X, /* 0781 */
+ JOINING_TYPE_X, /* 0782 */
+ JOINING_TYPE_X, /* 0783 */
+ JOINING_TYPE_X, /* 0784 */
+ JOINING_TYPE_X, /* 0785 */
+ JOINING_TYPE_X, /* 0786 */
+ JOINING_TYPE_X, /* 0787 */
+ JOINING_TYPE_X, /* 0788 */
+ JOINING_TYPE_X, /* 0789 */
+ JOINING_TYPE_X, /* 078A */
+ JOINING_TYPE_X, /* 078B */
+ JOINING_TYPE_X, /* 078C */
+ JOINING_TYPE_X, /* 078D */
+ JOINING_TYPE_X, /* 078E */
+ JOINING_TYPE_X, /* 078F */
+ JOINING_TYPE_X, /* 0790 */
+ JOINING_TYPE_X, /* 0791 */
+ JOINING_TYPE_X, /* 0792 */
+ JOINING_TYPE_X, /* 0793 */
+ JOINING_TYPE_X, /* 0794 */
+ JOINING_TYPE_X, /* 0795 */
+ JOINING_TYPE_X, /* 0796 */
+ JOINING_TYPE_X, /* 0797 */
+ JOINING_TYPE_X, /* 0798 */
+ JOINING_TYPE_X, /* 0799 */
+ JOINING_TYPE_X, /* 079A */
+ JOINING_TYPE_X, /* 079B */
+ JOINING_TYPE_X, /* 079C */
+ JOINING_TYPE_X, /* 079D */
+ JOINING_TYPE_X, /* 079E */
+ JOINING_TYPE_X, /* 079F */
+ JOINING_TYPE_X, /* 07A0 */
+ JOINING_TYPE_X, /* 07A1 */
+ JOINING_TYPE_X, /* 07A2 */
+ JOINING_TYPE_X, /* 07A3 */
+ JOINING_TYPE_X, /* 07A4 */
+ JOINING_TYPE_X, /* 07A5 */
+ JOINING_TYPE_X, /* 07A6 */
+ JOINING_TYPE_X, /* 07A7 */
+ JOINING_TYPE_X, /* 07A8 */
+ JOINING_TYPE_X, /* 07A9 */
+ JOINING_TYPE_X, /* 07AA */
+ JOINING_TYPE_X, /* 07AB */
+ JOINING_TYPE_X, /* 07AC */
+ JOINING_TYPE_X, /* 07AD */
+ JOINING_TYPE_X, /* 07AE */
+ JOINING_TYPE_X, /* 07AF */
+ JOINING_TYPE_X, /* 07B0 */
+ JOINING_TYPE_X, /* 07B1 */
+ JOINING_TYPE_X, /* 07B2 */
+ JOINING_TYPE_X, /* 07B3 */
+ JOINING_TYPE_X, /* 07B4 */
+ JOINING_TYPE_X, /* 07B5 */
+ JOINING_TYPE_X, /* 07B6 */
+ JOINING_TYPE_X, /* 07B7 */
+ JOINING_TYPE_X, /* 07B8 */
+ JOINING_TYPE_X, /* 07B9 */
+ JOINING_TYPE_X, /* 07BA */
+ JOINING_TYPE_X, /* 07BB */
+ JOINING_TYPE_X, /* 07BC */
+ JOINING_TYPE_X, /* 07BD */
+ JOINING_TYPE_X, /* 07BE */
+ JOINING_TYPE_X, /* 07BF */
+ JOINING_TYPE_X, /* 07C0 */
+ JOINING_TYPE_X, /* 07C1 */
+ JOINING_TYPE_X, /* 07C2 */
+ JOINING_TYPE_X, /* 07C3 */
+ JOINING_TYPE_X, /* 07C4 */
+ JOINING_TYPE_X, /* 07C5 */
+ JOINING_TYPE_X, /* 07C6 */
+ JOINING_TYPE_X, /* 07C7 */
+ JOINING_TYPE_X, /* 07C8 */
+ JOINING_TYPE_X, /* 07C9 */
+ JOINING_TYPE_D, /* 07CA; NKO A; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07CB; NKO EE; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07CC; NKO I; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07CD; NKO E; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07CE; NKO U; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07CF; NKO OO; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D0; NKO O; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D1; NKO DAGBASINNA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D2; NKO N; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D3; NKO BA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D4; NKO PA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D5; NKO TA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D6; NKO JA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D7; NKO CHA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D8; NKO DA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07D9; NKO RA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DA; NKO RRA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DB; NKO SA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DC; NKO GBA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DD; NKO FA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DE; NKO KA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07DF; NKO LA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E0; NKO NA WOLOSO; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E1; NKO MA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E2; NKO NYA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E3; NKO NA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E4; NKO HA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E5; NKO WA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E6; NKO YA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E7; NKO NYA WOLOSO; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E8; NKO JONA JA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07E9; NKO JONA CHA; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 07EA; NKO JONA RA; D; No_Joining_Group */
+ JOINING_TYPE_X, /* 07EB */
+ JOINING_TYPE_X, /* 07EC */
+ JOINING_TYPE_X, /* 07ED */
+ JOINING_TYPE_X, /* 07EE */
+ JOINING_TYPE_X, /* 07EF */
+ JOINING_TYPE_X, /* 07F0 */
+ JOINING_TYPE_X, /* 07F1 */
+ JOINING_TYPE_X, /* 07F2 */
+ JOINING_TYPE_X, /* 07F3 */
+ JOINING_TYPE_X, /* 07F4 */
+ JOINING_TYPE_X, /* 07F5 */
+ JOINING_TYPE_X, /* 07F6 */
+ JOINING_TYPE_X, /* 07F7 */
+ JOINING_TYPE_X, /* 07F8 */
+ JOINING_TYPE_X, /* 07F9 */
+ JOINING_TYPE_C, /* 07FA; NKO LAJANYALAN; C; No_Joining_Group */
+
+ /* Mandaic Characters */
+
+ JOINING_TYPE_X, /* 07FB */
+ JOINING_TYPE_X, /* 07FC */
+ JOINING_TYPE_X, /* 07FD */
+ JOINING_TYPE_X, /* 07FE */
+ JOINING_TYPE_X, /* 07FF */
+ JOINING_TYPE_X, /* 0800 */
+ JOINING_TYPE_X, /* 0801 */
+ JOINING_TYPE_X, /* 0802 */
+ JOINING_TYPE_X, /* 0803 */
+ JOINING_TYPE_X, /* 0804 */
+ JOINING_TYPE_X, /* 0805 */
+ JOINING_TYPE_X, /* 0806 */
+ JOINING_TYPE_X, /* 0807 */
+ JOINING_TYPE_X, /* 0808 */
+ JOINING_TYPE_X, /* 0809 */
+ JOINING_TYPE_X, /* 080A */
+ JOINING_TYPE_X, /* 080B */
+ JOINING_TYPE_X, /* 080C */
+ JOINING_TYPE_X, /* 080D */
+ JOINING_TYPE_X, /* 080E */
+ JOINING_TYPE_X, /* 080F */
+ JOINING_TYPE_X, /* 0810 */
+ JOINING_TYPE_X, /* 0811 */
+ JOINING_TYPE_X, /* 0812 */
+ JOINING_TYPE_X, /* 0813 */
+ JOINING_TYPE_X, /* 0814 */
+ JOINING_TYPE_X, /* 0815 */
+ JOINING_TYPE_X, /* 0816 */
+ JOINING_TYPE_X, /* 0817 */
+ JOINING_TYPE_X, /* 0818 */
+ JOINING_TYPE_X, /* 0819 */
+ JOINING_TYPE_X, /* 081A */
+ JOINING_TYPE_X, /* 081B */
+ JOINING_TYPE_X, /* 081C */
+ JOINING_TYPE_X, /* 081D */
+ JOINING_TYPE_X, /* 081E */
+ JOINING_TYPE_X, /* 081F */
+ JOINING_TYPE_X, /* 0820 */
+ JOINING_TYPE_X, /* 0821 */
+ JOINING_TYPE_X, /* 0822 */
+ JOINING_TYPE_X, /* 0823 */
+ JOINING_TYPE_X, /* 0824 */
+ JOINING_TYPE_X, /* 0825 */
+ JOINING_TYPE_X, /* 0826 */
+ JOINING_TYPE_X, /* 0827 */
+ JOINING_TYPE_X, /* 0828 */
+ JOINING_TYPE_X, /* 0829 */
+ JOINING_TYPE_X, /* 082A */
+ JOINING_TYPE_X, /* 082B */
+ JOINING_TYPE_X, /* 082C */
+ JOINING_TYPE_X, /* 082D */
+ JOINING_TYPE_X, /* 082E */
+ JOINING_TYPE_X, /* 082F */
+ JOINING_TYPE_X, /* 0830 */
+ JOINING_TYPE_X, /* 0831 */
+ JOINING_TYPE_X, /* 0832 */
+ JOINING_TYPE_X, /* 0833 */
+ JOINING_TYPE_X, /* 0834 */
+ JOINING_TYPE_X, /* 0835 */
+ JOINING_TYPE_X, /* 0836 */
+ JOINING_TYPE_X, /* 0837 */
+ JOINING_TYPE_X, /* 0838 */
+ JOINING_TYPE_X, /* 0839 */
+ JOINING_TYPE_X, /* 083A */
+ JOINING_TYPE_X, /* 083B */
+ JOINING_TYPE_X, /* 083C */
+ JOINING_TYPE_X, /* 083D */
+ JOINING_TYPE_X, /* 083E */
+ JOINING_TYPE_X, /* 083F */
+ JOINING_TYPE_R, /* 0840; MANDAIC HALQA; R; No_Joining_Group */
+ JOINING_TYPE_D, /* 0841; MANDAIC AB; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0842; MANDAIC AG; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0843; MANDAIC AD; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0844; MANDAIC AH; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0845; MANDAIC USHENNA; D; No_Joining_Group */
+ JOINING_TYPE_R, /* 0846; MANDAIC AZ; R; No_Joining_Group */
+ JOINING_TYPE_D, /* 0847; MANDAIC IT; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0848; MANDAIC ATT; D; No_Joining_Group */
+ JOINING_TYPE_R, /* 0849; MANDAIC AKSA; R; No_Joining_Group */
+ JOINING_TYPE_D, /* 084A; MANDAIC AK; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 084B; MANDAIC AL; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 084C; MANDAIC AM; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 084D; MANDAIC AN; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 084E; MANDAIC AS; D; No_Joining_Group */
+ JOINING_TYPE_R, /* 084F; MANDAIC IN; R; No_Joining_Group */
+ JOINING_TYPE_D, /* 0850; MANDAIC AP; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0851; MANDAIC ASZ; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0852; MANDAIC AQ; D; No_Joining_Group */
+ JOINING_TYPE_D, /* 0853; MANDAIC AR; D; No_Joining_Group */
+ JOINING_TYPE_R, /* 0854; MANDAIC ASH; R; No_Joining_Group */
+ JOINING_TYPE_D, /* 0855; MANDAIC AT; D; No_Joining_Group */
+ JOINING_TYPE_U, /* 0856; MANDAIC DUSHENNA; U; No_Joining_Group */
+ JOINING_TYPE_U, /* 0857; MANDAIC KAD; U; No_Joining_Group */
+ JOINING_TYPE_U, /* 0858; MANDAIC AIN; U; No_Joining_Group */
+
+ JOINING_TYPE_X /* dummy */
+};
+
+#define JOINING_TABLE_FIRST 0x0600
+#define JOINING_TABLE_LAST 0x0858
+
+/* == End of generated table == */
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc
new file mode 100644
index 0000000..77a9c82
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-complex-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* buffer var allocations */
+#define arabic_shaping_action() var2.u32 /* arabic shaping action */
+
+
+/*
+ * Bits used in the joining tables
+ */
+enum {
+ JOINING_TYPE_U = 0,
+ JOINING_TYPE_R = 1,
+ JOINING_TYPE_D = 2,
+ JOINING_TYPE_C = JOINING_TYPE_D,
+ JOINING_GROUP_ALAPH = 3,
+ JOINING_GROUP_DALATH_RISH = 4,
+ NUM_STATE_MACHINE_COLS = 5,
+
+ /* We deliberately don't have a JOINING_TYPE_L since that's unused in Unicode. */
+
+ JOINING_TYPE_T = 6,
+ JOINING_TYPE_X = 7 /* means: use general-category to choose between U or T. */
+};
+
+/*
+ * Joining types:
+ */
+
+#include "hb-ot-shape-complex-arabic-table.h"
+
+static unsigned int get_joining_type (hb_codepoint_t u, hb_category_t gen_cat)
+{
+ /* TODO Macroize the magic bit operations */
+
+ if (likely (JOINING_TABLE_FIRST <= u && u <= JOINING_TABLE_LAST)) {
+ unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST];
+ if (likely (j_type != JOINING_TYPE_X))
+ return j_type;
+ }
+
+ /* Mongolian joining data is not in ArabicJoining.txt yet */
+ if (unlikely (0x1800 <= u && u <= 0x18AF))
+ {
+ /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */
+ if (gen_cat == HB_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A)
+ return JOINING_TYPE_D;
+ }
+
+ if (unlikely ((u & ~(0x200C^0x200D)) == 0x200C)) {
+ return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
+ }
+
+ return ((1<<gen_cat) & ((1<<HB_CATEGORY_NON_SPACING_MARK)|(1<<HB_CATEGORY_ENCLOSING_MARK)|(1<<HB_CATEGORY_FORMAT))) ?
+ JOINING_TYPE_T : JOINING_TYPE_U;
+}
+
+
+
+static const hb_tag_t arabic_syriac_features[] =
+{
+ HB_TAG('i','n','i','t'),
+ HB_TAG('m','e','d','i'),
+ HB_TAG('f','i','n','a'),
+ HB_TAG('i','s','o','l'),
+ /* Syriac */
+ HB_TAG('m','e','d','2'),
+ HB_TAG('f','i','n','2'),
+ HB_TAG('f','i','n','3'),
+ HB_TAG_NONE
+};
+
+
+/* Same order as the feature array */
+enum {
+ INIT,
+ MEDI,
+ FINA,
+ ISOL,
+
+ /* Syriac */
+ MED2,
+ FIN2,
+ FIN3,
+
+ NONE,
+
+ COMMON_NUM_FEATURES = 4,
+ SYRIAC_NUM_FEATURES = 7,
+ TOTAL_NUM_FEATURES = NONE
+};
+
+static const struct arabic_state_table_entry {
+ uint8_t prev_action;
+ uint8_t curr_action;
+ uint8_t next_state;
+ uint8_t padding;
+} arabic_state_table[][NUM_STATE_MACHINE_COLS] =
+{
+ /* jt_U, jt_R, jt_D, jg_ALAPH, jg_DALATH_RISH */
+
+ /* State 0: prev was U, not willing to join. */
+ { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
+
+ /* State 1: prev was R or ISOL/ALAPH, not willing to join. */
+ { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
+
+ /* State 2: prev was D/ISOL, willing to join. */
+ { {NONE,NONE,0}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
+
+ /* State 3: prev was D/FINA, willing to join. */
+ { {NONE,NONE,0}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
+
+ /* State 4: prev was FINA ALAPH, not willing to join. */
+ { {NONE,NONE,0}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
+
+ /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */
+ { {NONE,NONE,0}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
+
+ /* State 6: prev was DALATH/RISH, not willing to join. */
+ { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
+};
+
+
+
+void
+_hb_ot_shape_complex_collect_features_arabic (hb_ot_shape_plan_t *plan, const hb_segment_properties_t *props)
+{
+ unsigned int num_features = props->script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
+ for (unsigned int i = 0; i < num_features; i++)
+ plan->map.add_bool_feature (arabic_syriac_features[i], false);
+}
+
+void
+_hb_ot_shape_complex_setup_masks_arabic (hb_ot_shape_context_t *c)
+{
+ unsigned int count = c->buffer->len;
+ unsigned int prev = 0, state = 0;
+
+ for (unsigned int i = 0; i < count; i++)
+ {
+ unsigned int this_type = get_joining_type (c->buffer->info[i].codepoint, (hb_category_t) c->buffer->info[i].general_category());
+
+ if (unlikely (this_type == JOINING_TYPE_T)) {
+ c->buffer->info[i].arabic_shaping_action() = NONE;
+ continue;
+ }
+
+ const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+
+ if (entry->prev_action != NONE)
+ c->buffer->info[prev].arabic_shaping_action() = entry->prev_action;
+
+ c->buffer->info[i].arabic_shaping_action() = entry->curr_action;
+
+ prev = i;
+ state = entry->next_state;
+ }
+
+ hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0};
+ unsigned int num_masks = c->buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
+ for (unsigned int i = 0; i < num_masks; i++)
+ mask_array[i] = c->plan->map.get_1_mask (arabic_syriac_features[i]);
+
+ for (unsigned int i = 0; i < count; i++)
+ c->buffer->info[i].mask |= mask_array[c->buffer->info[i].arabic_shaping_action()];
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-private.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-private.hh
new file mode 100644
index 0000000..fed167d
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-private.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-ot-shape-private.hh"
+
+HB_BEGIN_DECLS
+
+
+static inline hb_ot_complex_shaper_t
+hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
+{
+ switch ((int) props->script) {
+ case HB_SCRIPT_ARABIC:
+ case HB_SCRIPT_NKO:
+ case HB_SCRIPT_SYRIAC:
+ case HB_SCRIPT_MANDAIC:
+ case HB_SCRIPT_MONGOLIAN:
+ return hb_ot_complex_shaper_arabic;
+
+ default:
+ return hb_ot_complex_shaper_none;
+ }
+}
+
+
+
+/*
+ * collect_features()
+ *
+ * Called during shape_plan().
+ *
+ * Shapers should use plan->map to add their features.
+ */
+
+HB_INTERNAL void _hb_ot_shape_complex_collect_features_arabic (hb_ot_shape_plan_t *plan, const hb_segment_properties_t *props);
+
+static inline void
+hb_ot_shape_complex_collect_features (hb_ot_shape_plan_t *plan,
+ const hb_segment_properties_t *props)
+{
+ switch (plan->shaper) {
+ case hb_ot_complex_shaper_arabic: _hb_ot_shape_complex_collect_features_arabic (plan, props); return;
+ case hb_ot_complex_shaper_none: default: return;
+ }
+}
+
+
+/* setup_masks()
+ *
+ * Called during shape_execute().
+ *
+ * Shapers should use c->plan.map to get feature masks and set on buffer.
+ */
+
+HB_INTERNAL void _hb_ot_shape_complex_setup_masks_arabic (hb_ot_shape_context_t *c);
+
+static inline void
+hb_ot_shape_complex_setup_masks (hb_ot_shape_context_t *c)
+{
+ switch (c->plan->shaper) {
+ case hb_ot_complex_shaper_arabic: _hb_ot_shape_complex_setup_masks_arabic (c); return;
+ case hb_ot_complex_shaper_none: default: return;
+ }
+}
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-private.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-private.hh
new file mode 100644
index 0000000..deaec97
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-private.hh
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_PRIVATE_HH
+#define HB_OT_SHAPE_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-ot-shape.h"
+
+#include "hb-ot-map-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* buffer var allocations */
+#define general_category() var1.u8[0] /* unicode general_category (hb_category_t) */
+#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */
+
+
+enum hb_ot_complex_shaper_t {
+ hb_ot_complex_shaper_none,
+ hb_ot_complex_shaper_arabic
+};
+
+
+struct hb_ot_shape_plan_t
+{
+ hb_ot_map_t map;
+ hb_ot_complex_shaper_t shaper;
+};
+
+
+struct hb_ot_shape_context_t
+{
+ /* Input to hb_ot_shape_execute() */
+ hb_ot_shape_plan_t *plan;
+ hb_font_t *font;
+ hb_face_t *face;
+ hb_buffer_t *buffer;
+ const hb_feature_t *user_features;
+ unsigned int num_user_features;
+
+ /* Transient stuff */
+ hb_direction_t target_direction;
+ hb_bool_t applied_substitute_complex;
+ hb_bool_t applied_position_complex;
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape.cc b/third_party/harfbuzz-ng/src/hb-ot-shape.cc
new file mode 100644
index 0000000..92c3925
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape.cc
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2009,2010 Red Hat, Inc.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-private.hh"
+#include "hb-ot-shape-complex-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* XXX vertical */
+hb_tag_t default_features[] = {
+ HB_TAG('c','a','l','t'),
+ HB_TAG('c','c','m','p'),
+ HB_TAG('c','l','i','g'),
+ HB_TAG('c','s','w','h'),
+ HB_TAG('c','u','r','s'),
+ HB_TAG('k','e','r','n'),
+ HB_TAG('l','i','g','a'),
+ HB_TAG('l','o','c','l'),
+ HB_TAG('m','a','r','k'),
+ HB_TAG('m','k','m','k'),
+ HB_TAG('r','l','i','g')
+};
+
+static void
+hb_ot_shape_collect_features (hb_ot_shape_plan_t *plan,
+ const hb_segment_properties_t *props,
+ const hb_feature_t *user_features,
+ unsigned int num_user_features)
+{
+ switch (props->direction) {
+ case HB_DIRECTION_LTR:
+ plan->map.add_bool_feature (HB_TAG ('l','t','r','a'));
+ plan->map.add_bool_feature (HB_TAG ('l','t','r','m'));
+ break;
+ case HB_DIRECTION_RTL:
+ plan->map.add_bool_feature (HB_TAG ('r','t','l','a'));
+ plan->map.add_bool_feature (HB_TAG ('r','t','l','m'), false);
+ break;
+ case HB_DIRECTION_TTB:
+ case HB_DIRECTION_BTT:
+ default:
+ break;
+ }
+
+ for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
+ plan->map.add_bool_feature (default_features[i]);
+
+ hb_ot_shape_complex_collect_features (plan, props);
+
+ for (unsigned int i = 0; i < num_user_features; i++) {
+ const hb_feature_t *feature = &user_features[i];
+ plan->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
+ }
+}
+
+
+static void
+hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
+{
+ hb_mask_t global_mask = c->plan->map.get_global_mask ();
+ c->buffer->reset_masks (global_mask);
+
+ hb_ot_shape_complex_setup_masks (c); /* BUFFER: Clobbers var2 */
+
+ for (unsigned int i = 0; i < c->num_user_features; i++)
+ {
+ const hb_feature_t *feature = &c->user_features[i];
+ if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
+ unsigned int shift;
+ hb_mask_t mask = c->plan->map.get_mask (feature->tag, &shift);
+ c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
+ }
+ }
+}
+
+
+static void
+hb_ot_substitute_complex (hb_ot_shape_context_t *c)
+{
+ if (!hb_ot_layout_has_substitution (c->face))
+ return;
+
+ c->plan->map.substitute (c->face, c->buffer);
+
+ c->applied_substitute_complex = TRUE;
+ return;
+}
+
+static void
+hb_ot_position_complex (hb_ot_shape_context_t *c)
+{
+
+ if (!hb_ot_layout_has_positioning (c->face))
+ return;
+
+ c->plan->map.position (c->font, c->face, c->buffer);
+
+ hb_ot_layout_position_finish (c->buffer);
+
+ c->applied_position_complex = TRUE;
+ return;
+}
+
+
+/* Main shaper */
+
+/* Prepare */
+
+static inline hb_bool_t
+is_variation_selector (hb_codepoint_t unicode)
+{
+ return unlikely ((unicode >= 0x180B && unicode <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */
+ (unicode >= 0xFE00 && unicode <= 0xFE0F) || /* VARIATION SELECTOR-1..16 */
+ (unicode >= 0xE0100 && unicode <= 0xE01EF)); /* VARIATION SELECTOR-17..256 */
+}
+
+static void
+hb_set_unicode_props (hb_ot_shape_context_t *c)
+{
+ hb_unicode_get_general_category_func_t get_general_category = c->buffer->unicode->v.get_general_category;
+ hb_unicode_get_combining_class_func_t get_combining_class = c->buffer->unicode->v.get_combining_class;
+ hb_glyph_info_t *info = c->buffer->info;
+
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 1; i < count; i++) {
+ info[i].general_category() = get_general_category (info[i].codepoint);
+ info[i].combining_class() = get_combining_class (info[i].codepoint);
+ }
+}
+
+static void
+hb_form_clusters (hb_ot_shape_context_t *c)
+{
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 1; i < count; i++)
+ if (c->buffer->info[i].general_category() == HB_CATEGORY_NON_SPACING_MARK)
+ c->buffer->info[i].cluster = c->buffer->info[i - 1].cluster;
+}
+
+static void
+hb_ensure_native_direction (hb_ot_shape_context_t *c)
+{
+ hb_direction_t direction = c->buffer->props.direction;
+
+ /* TODO vertical */
+ if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
+ direction != _hb_script_get_horizontal_direction (c->buffer->props.script))
+ {
+ hb_buffer_reverse_clusters (c->buffer);
+ c->buffer->props.direction = HB_DIRECTION_REVERSE (c->buffer->props.direction);
+ }
+}
+
+static void
+hb_reset_glyph_infos (hb_ot_shape_context_t *c)
+{
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ c->buffer->info[i].var1.u32 = c->buffer->info[i].var2.u32 = 0;
+}
+
+
+/* Substitute */
+
+static void
+hb_mirror_chars (hb_ot_shape_context_t *c)
+{
+ hb_unicode_get_mirroring_func_t get_mirroring = c->buffer->unicode->v.get_mirroring;
+
+ if (HB_DIRECTION_IS_FORWARD (c->target_direction))
+ return;
+
+ hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
+
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 0; i < count; i++) {
+ hb_codepoint_t codepoint = get_mirroring (c->buffer->info[i].codepoint);
+ if (likely (codepoint == c->buffer->info[i].codepoint))
+ c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
+ else
+ c->buffer->info[i].codepoint = codepoint;
+ }
+}
+
+static void
+hb_map_glyphs (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer)
+{
+ if (unlikely (!buffer->len))
+ return;
+
+ buffer->clear_output ();
+ unsigned int count = buffer->len - 1;
+ for (buffer->i = 0; buffer->i < count;) {
+ if (unlikely (is_variation_selector (buffer->info[buffer->i + 1].codepoint))) {
+ buffer->replace_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, buffer->info[buffer->i + 1].codepoint));
+ buffer->i++;
+ } else {
+ buffer->replace_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0));
+ }
+ }
+ if (likely (buffer->i < buffer->len))
+ buffer->replace_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0));
+ buffer->swap ();
+}
+
+static void
+hb_substitute_default (hb_ot_shape_context_t *c)
+{
+ hb_map_glyphs (c->font, c->face, c->buffer);
+}
+
+static void
+hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
+{
+ /* TODO Arabic */
+}
+
+
+/* Position */
+
+static void
+hb_position_default (hb_ot_shape_context_t *c)
+{
+ hb_buffer_clear_positions (c->buffer);
+
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 0; i < count; i++) {
+ hb_font_get_glyph_advance (c->font, c->face, c->buffer->info[i].codepoint,
+ &c->buffer->pos[i].x_advance,
+ &c->buffer->pos[i].y_advance);
+ }
+}
+
+static void
+hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
+{
+ /* TODO Mark pos */
+}
+
+static void
+hb_truetype_kern (hb_ot_shape_context_t *c)
+{
+ /* TODO Check for kern=0 */
+ unsigned int count = c->buffer->len;
+ for (unsigned int i = 1; i < count; i++) {
+ hb_position_t kern, kern1, kern2;
+ kern = hb_font_get_kerning (c->font, c->face, c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint);
+ kern1 = kern >> 1;
+ kern2 = kern - kern1;
+ c->buffer->pos[i - 1].x_advance += kern1;
+ c->buffer->pos[i].x_advance += kern2;
+ c->buffer->pos[i].x_offset += kern2;
+ }
+}
+
+static void
+hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
+{
+ hb_truetype_kern (c);
+}
+
+
+/* Do it! */
+
+static void
+hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
+{
+ /* Save the original direction, we use it later. */
+ c->target_direction = c->buffer->props.direction;
+
+ hb_reset_glyph_infos (c); /* BUFFER: Clear buffer var1 and var2 */
+
+ hb_set_unicode_props (c); /* BUFFER: Set general_category and combining_class in var1 */
+
+ hb_ensure_native_direction (c);
+
+ hb_form_clusters (c);
+
+ hb_ot_shape_setup_masks (c); /* BUFFER: Clobbers var2 */
+
+ /* SUBSTITUTE */
+ {
+ /* Mirroring needs to see the original direction */
+ hb_mirror_chars (c);
+
+ hb_substitute_default (c);
+
+ hb_ot_substitute_complex (c);
+
+ if (!c->applied_substitute_complex)
+ hb_substitute_complex_fallback (c);
+ }
+
+ /* POSITION */
+ {
+ hb_position_default (c);
+
+ hb_ot_position_complex (c);
+
+ hb_bool_t position_fallback = !c->applied_position_complex;
+ if (position_fallback)
+ hb_position_complex_fallback (c);
+
+ if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
+ hb_buffer_reverse (c->buffer);
+
+ if (position_fallback)
+ hb_position_complex_fallback_visual (c);
+ }
+
+ c->buffer->props.direction = c->target_direction;
+}
+
+void
+hb_ot_shape_plan_internal (hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
+ const hb_segment_properties_t *props,
+ const hb_feature_t *user_features,
+ unsigned int num_user_features)
+{
+ plan->shaper = hb_ot_shape_complex_categorize (props);
+
+ hb_ot_shape_collect_features (plan, props, user_features, num_user_features);
+
+ plan->map.compile (face, props);
+}
+
+void
+hb_ot_shape_execute (hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ const hb_feature_t *user_features,
+ unsigned int num_user_features)
+{
+ hb_ot_shape_context_t c = {plan, font, face, buffer, user_features, num_user_features};
+ hb_ot_shape_execute_internal (&c);
+}
+
+void
+hb_ot_shape (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ const hb_feature_t *user_features,
+ unsigned int num_user_features)
+{
+ hb_ot_shape_plan_t plan;
+
+ hb_ot_shape_plan_internal (&plan, face, &buffer->props, user_features, num_user_features);
+ hb_ot_shape_execute (&plan, font, face, buffer, user_features, num_user_features);
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape.h b/third_party/harfbuzz-ng/src/hb-ot-shape.h
new file mode 100644
index 0000000..c90e0fe
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape.h
@@ -0,0 +1,46 @@
+/*
+ * 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_SHAPE_H
+#define HB_OT_SHAPE_H
+
+#include "hb-shape.h"
+
+
+HB_BEGIN_DECLS
+
+
+void
+hb_ot_shape (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ const hb_feature_t *user_features,
+ unsigned int num_user_features);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-tag.c b/third_party/harfbuzz-ng/src/hb-ot-tag.c
new file mode 100644
index 0000000..f3e0f1f
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-tag.c
@@ -0,0 +1,703 @@
+/*
+ * 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-ot.h"
+
+#include <string.h>
+
+HB_BEGIN_DECLS
+
+
+/*
+ * Complete list at:
+ * http://www.microsoft.com/typography/otspec/scripttags.htm
+ */
+static const hb_tag_t ot_scripts[][3] = {
+ {HB_TAG('D','F','L','T')}, /* HB_SCRIPT_COMMON */
+ {HB_TAG('D','F','L','T')}, /* HB_SCRIPT_INHERITED */
+ {HB_TAG('a','r','a','b')}, /* HB_SCRIPT_ARABIC */
+ {HB_TAG('a','r','m','n')}, /* HB_SCRIPT_ARMENIAN */
+ {HB_TAG('b','n','g','2'), HB_TAG('b','e','n','g')}, /* HB_SCRIPT_BENGALI */
+ {HB_TAG('b','o','p','o')}, /* HB_SCRIPT_BOPOMOFO */
+ {HB_TAG('c','h','e','r')}, /* HB_SCRIPT_CHEROKEE */
+ {HB_TAG('c','o','p','t')}, /* HB_SCRIPT_COPTIC */
+ {HB_TAG('c','y','r','l')}, /* HB_SCRIPT_CYRILLIC */
+ {HB_TAG('d','s','r','t')}, /* HB_SCRIPT_DESERET */
+ {HB_TAG('d','e','v','2'), HB_TAG('d','e','v','a')}, /* HB_SCRIPT_DEVANAGARI */
+ {HB_TAG('e','t','h','i')}, /* HB_SCRIPT_ETHIOPIC */
+ {HB_TAG('g','e','o','r')}, /* HB_SCRIPT_GEORGIAN */
+ {HB_TAG('g','o','t','h')}, /* HB_SCRIPT_GOTHIC */
+ {HB_TAG('g','r','e','k')}, /* HB_SCRIPT_GREEK */
+ {HB_TAG('g','j','r','2'), HB_TAG('g','u','j','r')}, /* HB_SCRIPT_GUJARATI */
+ {HB_TAG('g','u','r','2'), HB_TAG('g','u','r','u')}, /* HB_SCRIPT_GURMUKHI */
+ {HB_TAG('h','a','n','i')}, /* HB_SCRIPT_HAN */
+ {HB_TAG('h','a','n','g')}, /* HB_SCRIPT_HANGUL */
+ {HB_TAG('h','e','b','r')}, /* HB_SCRIPT_HEBREW */
+ {HB_TAG('k','a','n','a')}, /* HB_SCRIPT_HIRAGANA */
+ {HB_TAG('k','n','d','2'), HB_TAG('k','n','d','a')}, /* HB_SCRIPT_KANNADA */
+ {HB_TAG('k','a','n','a')}, /* HB_SCRIPT_KATAKANA */
+ {HB_TAG('k','h','m','r')}, /* HB_SCRIPT_KHMER */
+ {HB_TAG('l','a','o',' ')}, /* HB_SCRIPT_LAO */
+ {HB_TAG('l','a','t','n')}, /* HB_SCRIPT_LATIN */
+ {HB_TAG('m','l','m','2'), HB_TAG('m','l','y','m')}, /* HB_SCRIPT_MALAYALAM */
+ {HB_TAG('m','o','n','g')}, /* HB_SCRIPT_MONGOLIAN */
+ {HB_TAG('m','y','m','r')}, /* HB_SCRIPT_MYANMAR */
+ {HB_TAG('o','g','a','m')}, /* HB_SCRIPT_OGHAM */
+ {HB_TAG('i','t','a','l')}, /* HB_SCRIPT_OLD_ITALIC */
+ {HB_TAG('o','r','y','2'), HB_TAG('o','r','y','a')}, /* HB_SCRIPT_ORIYA */
+ {HB_TAG('r','u','n','r')}, /* HB_SCRIPT_RUNIC */
+ {HB_TAG('s','i','n','h')}, /* HB_SCRIPT_SINHALA */
+ {HB_TAG('s','y','r','c')}, /* HB_SCRIPT_SYRIAC */
+ {HB_TAG('t','m','l','2'), HB_TAG('t','a','m','l')}, /* HB_SCRIPT_TAMIL */
+ {HB_TAG('t','e','l','2'), HB_TAG('t','e','l','u')}, /* HB_SCRIPT_TELUGU */
+ {HB_TAG('t','h','a','a')}, /* HB_SCRIPT_THAANA */
+ {HB_TAG('t','h','a','i')}, /* HB_SCRIPT_THAI */
+ {HB_TAG('t','i','b','t')}, /* HB_SCRIPT_TIBETAN */
+ {HB_TAG('c','a','n','s')}, /* HB_SCRIPT_CANADIAN_ABORIGINAL */
+ {HB_TAG('y','i',' ',' ')}, /* HB_SCRIPT_YI */
+ {HB_TAG('t','g','l','g')}, /* HB_SCRIPT_TAGALOG */
+ {HB_TAG('h','a','n','o')}, /* HB_SCRIPT_HANUNOO */
+ {HB_TAG('b','u','h','d')}, /* HB_SCRIPT_BUHID */
+ {HB_TAG('t','a','g','b')}, /* HB_SCRIPT_TAGBANWA */
+
+ /* Unicode-4.0 additions */
+ {HB_TAG('b','r','a','i')}, /* HB_SCRIPT_BRAILLE */
+ {HB_TAG('c','p','r','t')}, /* HB_SCRIPT_CYPRIOT */
+ {HB_TAG('l','i','m','b')}, /* HB_SCRIPT_LIMBU */
+ {HB_TAG('o','s','m','a')}, /* HB_SCRIPT_OSMANYA */
+ {HB_TAG('s','h','a','w')}, /* HB_SCRIPT_SHAVIAN */
+ {HB_TAG('l','i','n','b')}, /* HB_SCRIPT_LINEAR_B */
+ {HB_TAG('t','a','l','e')}, /* HB_SCRIPT_TAI_LE */
+ {HB_TAG('u','g','a','r')}, /* HB_SCRIPT_UGARITIC */
+
+ /* Unicode-4.1 additions */
+ {HB_TAG('t','a','l','u')}, /* HB_SCRIPT_NEW_TAI_LUE */
+ {HB_TAG('b','u','g','i')}, /* HB_SCRIPT_BUGINESE */
+ {HB_TAG('g','l','a','g')}, /* HB_SCRIPT_GLAGOLITIC */
+ {HB_TAG('t','f','n','g')}, /* HB_SCRIPT_TIFINAGH */
+ {HB_TAG('s','y','l','o')}, /* HB_SCRIPT_SYLOTI_NAGRI */
+ {HB_TAG('x','p','e','o')}, /* HB_SCRIPT_OLD_PERSIAN */
+ {HB_TAG('k','h','a','r')}, /* HB_SCRIPT_KHAROSHTHI */
+
+ /* Unicode-5.0 additions */
+ {HB_TAG('D','F','L','T')}, /* HB_SCRIPT_UNKNOWN */
+ {HB_TAG('b','a','l','i')}, /* HB_SCRIPT_BALINESE */
+ {HB_TAG('x','s','u','x')}, /* HB_SCRIPT_CUNEIFORM */
+ {HB_TAG('p','h','n','x')}, /* HB_SCRIPT_PHOENICIAN */
+ {HB_TAG('p','h','a','g')}, /* HB_SCRIPT_PHAGS_PA */
+ {HB_TAG('n','k','o',' ')}, /* HB_SCRIPT_NKO */
+
+ /* Unicode-5.1 additions */
+ {HB_TAG('k','a','l','i')}, /* HB_SCRIPT_KAYAH_LI */
+ {HB_TAG('l','e','p','c')}, /* HB_SCRIPT_LEPCHA */
+ {HB_TAG('r','j','n','g')}, /* HB_SCRIPT_REJANG */
+ {HB_TAG('s','u','n','d')}, /* HB_SCRIPT_SUNDANESE */
+ {HB_TAG('s','a','u','r')}, /* HB_SCRIPT_SAURASHTRA */
+ {HB_TAG('c','h','a','m')}, /* HB_SCRIPT_CHAM */
+ {HB_TAG('o','l','c','k')}, /* HB_SCRIPT_OL_CHIKI */
+ {HB_TAG('v','a','i',' ')}, /* HB_SCRIPT_VAI */
+ {HB_TAG('c','a','r','i')}, /* HB_SCRIPT_CARIAN */
+ {HB_TAG('l','y','c','i')}, /* HB_SCRIPT_LYCIAN */
+ {HB_TAG('l','y','d','i')}, /* HB_SCRIPT_LYDIAN */
+
+ /* Unicode-5.2 additions */
+ {HB_TAG('a','v','s','t')}, /* HB_SCRIPT_AVESTAN */
+ {HB_TAG('b','a','m','u')}, /* HB_SCRIPT_BAMUM */
+ {HB_TAG('e','g','y','p')}, /* HB_SCRIPT_EGYPTIAN_HIEROGLYPHS */
+ {HB_TAG('a','r','m','i')}, /* HB_SCRIPT_IMPERIAL_ARAMAIC */
+ {HB_TAG('p','h','l','i')}, /* HB_SCRIPT_INSCRIPTIONAL_PAHLAVI */
+ {HB_TAG('p','r','t','i')}, /* HB_SCRIPT_INSCRIPTIONAL_PARTHIAN */
+ {HB_TAG('j','a','v','a')}, /* HB_SCRIPT_JAVANESE */
+ {HB_TAG('k','t','h','i')}, /* HB_SCRIPT_KAITHI */
+ {HB_TAG('l','i','s','u')}, /* HB_SCRIPT_LISU */
+ {HB_TAG('m','y','e','i')}, /* HB_SCRIPT_MEETEI_MAYEK */
+ {HB_TAG('s','a','r','b')}, /* HB_SCRIPT_OLD_SOUTH_ARABIAN */
+ {HB_TAG('o','r','k','h')}, /* HB_SCRIPT_OLD_TURKIC */
+ {HB_TAG('s','a','m','r')}, /* HB_SCRIPT_SAMARITAN */
+ {HB_TAG('l','a','n','a')}, /* HB_SCRIPT_TAI_THAM */
+ {HB_TAG('t','a','v','t')}, /* HB_SCRIPT_TAI_VIET */
+
+ /* Unicode-6.0 additions */
+ {HB_TAG('b','a','t','k')}, /* HB_SCRIPT_BATAK */
+ {HB_TAG('b','r','a','h')}, /* HB_SCRIPT_BRAHMI */
+ {HB_TAG('m','a','n','d')} /* HB_SCRIPT_MANDAIC */
+};
+
+const hb_tag_t *
+hb_ot_tags_from_script (hb_script_t script)
+{
+ static const hb_tag_t def_tag[] = {HB_OT_TAG_DEFAULT_SCRIPT, HB_TAG_NONE};
+
+ if (unlikely ((unsigned int) script >= ARRAY_LENGTH (ot_scripts)))
+ return def_tag;
+
+ return ot_scripts[script];
+}
+
+hb_script_t
+hb_ot_tag_to_script (hb_tag_t tag)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH (ot_scripts); i++) {
+ const hb_tag_t *p;
+ for (p = ot_scripts[i]; *p; p++)
+ if (tag == *p)
+ return i;
+ }
+
+ return HB_SCRIPT_UNKNOWN;
+}
+
+typedef struct {
+ char language[6];
+ hb_tag_t tag;
+} LangTag;
+
+/*
+ * Complete list at:
+ * http://www.microsoft.com/typography/otspec/languagetags.htm
+ *
+ * Generated by intersecting the OpenType language tag list from
+ * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
+ * 2008/08/04, matching on name, and finally adjusted manually.
+ *
+ * Many items still missing. Those are commented out at the end.
+ * Keep sorted for bsearch.
+ */
+static const LangTag ot_languages[] = {
+ {"aa", HB_TAG('A','F','R',' ')}, /* Afar */
+ {"ab", HB_TAG('A','B','K',' ')}, /* Abkhazian */
+ {"abq", HB_TAG('A','B','A',' ')}, /* Abaza */
+ {"ady", HB_TAG('A','D','Y',' ')}, /* Adyghe */
+ {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */
+ {"aiw", HB_TAG('A','R','I',' ')}, /* Aari */
+ {"am", HB_TAG('A','M','H',' ')}, /* Amharic */
+ {"ar", HB_TAG('A','R','A',' ')}, /* Arabic */
+ {"arn", HB_TAG('M','A','P',' ')}, /* Mapudungun */
+ {"as", HB_TAG('A','S','M',' ')}, /* Assamese */
+ {"av", HB_TAG('A','V','R',' ')}, /* Avaric */
+ {"awa", HB_TAG('A','W','A',' ')}, /* Awadhi */
+ {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara */
+ {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani */
+ {"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */
+ {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi */
+ {"bcq", HB_TAG('B','C','H',' ')}, /* Bench */
+ {"bem", HB_TAG('B','E','M',' ')}, /* Bemba (Zambia) */
+ {"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */
+ {"bft", HB_TAG('B','L','T',' ')}, /* Balti */
+ {"bg", HB_TAG('B','G','R',' ')}, /* Bulgarian */
+ {"bhb", HB_TAG('B','H','I',' ')}, /* Bhili */
+ {"bho", HB_TAG('B','H','O',' ')}, /* Bhojpuri */
+ {"bik", HB_TAG('B','I','K',' ')}, /* Bikol */
+ {"bin", HB_TAG('E','D','O',' ')}, /* Bini */
+ {"bm", HB_TAG('B','M','B',' ')}, /* Bambara */
+ {"bn", HB_TAG('B','E','N',' ')}, /* Bengali */
+ {"bo", HB_TAG('T','I','B',' ')}, /* Tibetan */
+ {"br", HB_TAG('B','R','E',' ')}, /* Breton */
+ {"brh", HB_TAG('B','R','H',' ')}, /* Brahui */
+ {"bs", HB_TAG('B','O','S',' ')}, /* Bosnian */
+ {"btb", HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) */
+ {"ca", HB_TAG('C','A','T',' ')}, /* Catalan */
+ {"ce", HB_TAG('C','H','E',' ')}, /* Chechen */
+ {"ceb", HB_TAG('C','E','B',' ')}, /* Cebuano */
+ {"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */
+ {"chr", HB_TAG('C','H','R',' ')}, /* Cherokee */
+ {"cop", HB_TAG('C','O','P',' ')}, /* Coptic */
+ {"cr", HB_TAG('C','R','E',' ')}, /* Cree */
+ {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */
+ {"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */
+ {"crx", HB_TAG('C','R','R',' ')}, /* Carrier */
+ {"cs", HB_TAG('C','S','Y',' ')}, /* Czech */
+ {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavic */
+ {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */
+ {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */
+ {"cy", HB_TAG('W','E','L',' ')}, /* Welsh */
+ {"da", HB_TAG('D','A','N',' ')}, /* Danish */
+ {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) */
+ {"dar", HB_TAG('D','A','R',' ')}, /* Dargwa */
+ {"de", HB_TAG('D','E','U',' ')}, /* German */
+ {"din", HB_TAG('D','N','K',' ')}, /* Dinka */
+ {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */
+ {"doi", HB_TAG('D','G','R',' ')}, /* Dogri */
+ {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */
+ {"dv", HB_TAG('D','I','V',' ')}, /* Dhivehi */
+ {"dz", HB_TAG('D','Z','N',' ')}, /* Dzongkha */
+ {"ee", HB_TAG('E','W','E',' ')}, /* Ewe */
+ {"efi", HB_TAG('E','F','I',' ')}, /* Efik */
+ {"el", HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) */
+ {"en", HB_TAG('E','N','G',' ')}, /* English */
+ {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */
+ {"eot", HB_TAG('B','T','I',' ')}, /* Beti (Côte d'Ivoire) */
+ {"es", HB_TAG('E','S','P',' ')}, /* Spanish */
+ {"et", HB_TAG('E','T','I',' ')}, /* Estonian */
+ {"eu", HB_TAG('E','U','Q',' ')}, /* Basque */
+ {"eve", HB_TAG('E','V','N',' ')}, /* Even */
+ {"evn", HB_TAG('E','V','K',' ')}, /* Evenki */
+ {"fa", HB_TAG('F','A','R',' ')}, /* Persian */
+ {"ff", HB_TAG('F','U','L',' ')}, /* Fulah */
+ {"fi", HB_TAG('F','I','N',' ')}, /* Finnish */
+ {"fil", HB_TAG('P','I','L',' ')}, /* Filipino */
+ {"fj", HB_TAG('F','J','I',' ')}, /* Fijian */
+ {"fo", HB_TAG('F','O','S',' ')}, /* Faroese */
+ {"fon", HB_TAG('F','O','N',' ')}, /* Fon */
+ {"fr", HB_TAG('F','R','A',' ')}, /* French */
+ {"fur", HB_TAG('F','R','L',' ')}, /* Friulian */
+ {"fy", HB_TAG('F','R','I',' ')}, /* Western Frisian */
+ {"ga", HB_TAG('I','R','I',' ')}, /* Irish */
+ {"gaa", HB_TAG('G','A','D',' ')}, /* Ga */
+ {"gag", HB_TAG('G','A','G',' ')}, /* Gagauz */
+ {"gbm", HB_TAG('G','A','W',' ')}, /* Garhwali */
+ {"gd", HB_TAG('G','A','E',' ')}, /* Scottish Gaelic */
+ {"gl", HB_TAG('G','A','L',' ')}, /* Galician */
+ {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */
+ {"gn", HB_TAG('G','U','A',' ')}, /* Guarani */
+ {"gon", HB_TAG('G','O','N',' ')}, /* Gondi */
+ {"grt", HB_TAG('G','R','O',' ')}, /* Garo */
+ {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */
+ {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */
+ {"gv", HB_TAG('M','N','X',' ')}, /* Manx Gaelic */
+ {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */
+ {"har", HB_TAG('H','R','I',' ')}, /* Harari */
+ {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */
+ {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */
+ {"hil", HB_TAG('H','I','L',' ')}, /* Hiligaynon */
+ {"hoc", HB_TAG('H','O',' ',' ')}, /* Ho */
+ {"hr", HB_TAG('H','R','V',' ')}, /* Croatian */
+ {"hsb", HB_TAG('U','S','B',' ')}, /* Upper Sorbian */
+ {"ht", HB_TAG('H','A','I',' ')}, /* Haitian */
+ {"hu", HB_TAG('H','U','N',' ')}, /* Hungarian */
+ {"hy", HB_TAG('H','Y','E',' ')}, /* Armenian */
+ {"id", HB_TAG('I','N','D',' ')}, /* Indonesian */
+ {"ig", HB_TAG('I','B','O',' ')}, /* Igbo */
+ {"igb", HB_TAG('E','B','I',' ')}, /* Ebira */
+ {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */
+ {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */
+ {"it", HB_TAG('I','T','A',' ')}, /* Italian */
+ {"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut */
+ {"ja", HB_TAG('J','A','N',' ')}, /* Japanese */
+ {"jv", HB_TAG('J','A','V',' ')}, /* Javanese */
+ {"ka", HB_TAG('K','A','T',' ')}, /* Georgian */
+ {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */
+ {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */
+ {"kdr", HB_TAG('K','R','M',' ')}, /* Karaim */
+ {"kdt", HB_TAG('K','U','Y',' ')}, /* Kuy */
+ {"kfr", HB_TAG('K','A','C',' ')}, /* Kachchi */
+ {"kfy", HB_TAG('K','M','N',' ')}, /* Kumaoni */
+ {"kha", HB_TAG('K','S','I',' ')}, /* Khasi */
+ {"khw", HB_TAG('K','H','W',' ')}, /* Khowar */
+ {"ki", HB_TAG('K','I','K',' ')}, /* Kikuyu */
+ {"kk", HB_TAG('K','A','Z',' ')}, /* Kazakh */
+ {"kl", HB_TAG('G','R','N',' ')}, /* Kalaallisut */
+ {"kln", HB_TAG('K','A','L',' ')}, /* Kalenjin */
+ {"km", HB_TAG('K','H','M',' ')}, /* Central Khmer */
+ {"kmw", HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */
+ {"kn", HB_TAG('K','A','N',' ')}, /* Kannada */
+ {"ko", HB_TAG('K','O','R',' ')}, /* Korean */
+ {"koi", HB_TAG('K','O','P',' ')}, /* Komi-Permyak */
+ {"kok", HB_TAG('K','O','K',' ')}, /* Konkani */
+ {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle */
+ {"kpv", HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */
+ {"kpy", HB_TAG('K','Y','K',' ')}, /* Koryak */
+ {"kqy", HB_TAG('K','R','T',' ')}, /* Koorete */
+ {"kr", HB_TAG('K','N','R',' ')}, /* Kanuri */
+ {"kri", HB_TAG('K','R','I',' ')}, /* Krio */
+ {"krl", HB_TAG('K','R','L',' ')}, /* Karelian */
+ {"kru", HB_TAG('K','U','U',' ')}, /* Kurukh */
+ {"ks", HB_TAG('K','S','H',' ')}, /* Kashmiri */
+ {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish */
+ {"kum", HB_TAG('K','U','M',' ')}, /* Kumyk */
+ {"kvd", HB_TAG('K','U','I',' ')}, /* Kui (Indonesia) */
+ {"kxu", HB_TAG('K','U','I',' ')}, /* Kui (India) */
+ {"ky", HB_TAG('K','I','R',' ')}, /* Kirghiz */
+ {"la", HB_TAG('L','A','T',' ')}, /* Latin */
+ {"lad", HB_TAG('J','U','D',' ')}, /* Ladino */
+ {"lb", HB_TAG('L','T','Z',' ')}, /* Luxembourgish */
+ {"lbe", HB_TAG('L','A','K',' ')}, /* Lak */
+ {"lbj", HB_TAG('L','D','K',' ')}, /* Ladakhi */
+ {"lif", HB_TAG('L','M','B',' ')}, /* Limbu */
+ {"lld", HB_TAG('L','A','D',' ')}, /* Ladin */
+ {"ln", HB_TAG('L','I','N',' ')}, /* Lingala */
+ {"lo", HB_TAG('L','A','O',' ')}, /* Lao */
+ {"lt", HB_TAG('L','T','H',' ')}, /* Lithuanian */
+ {"luo", HB_TAG('L','U','O',' ')}, /* Luo (Kenya and Tanzania) */
+ {"luw", HB_TAG('L','U','O',' ')}, /* Luo (Cameroon) */
+ {"lv", HB_TAG('L','V','I',' ')}, /* Latvian */
+ {"lzz", HB_TAG('L','A','Z',' ')}, /* Laz */
+ {"mai", HB_TAG('M','T','H',' ')}, /* Maithili */
+ {"mdc", HB_TAG('M','L','E',' ')}, /* Male (Papua New Guinea) */
+ {"mdf", HB_TAG('M','O','K',' ')}, /* Moksha */
+ {"mdy", HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */
+ {"men", HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */
+ {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy */
+ {"mi", HB_TAG('M','R','I',' ')}, /* Maori */
+ {"mk", HB_TAG('M','K','D',' ')}, /* Macedonian */
+ {"ml", HB_TAG('M','L','R',' ')}, /* Malayalam */
+ {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian */
+ {"mnc", HB_TAG('M','C','H',' ')}, /* Manchu */
+ {"mni", HB_TAG('M','N','I',' ')}, /* Manipuri */
+ {"mnk", HB_TAG('M','N','D',' ')}, /* Mandinka */
+ {"mns", HB_TAG('M','A','N',' ')}, /* Mansi */
+ {"mnw", HB_TAG('M','O','N',' ')}, /* Mon */
+ {"mo", HB_TAG('M','O','L',' ')}, /* Moldavian */
+ {"moh", HB_TAG('M','O','H',' ')}, /* Mohawk */
+ {"mpe", HB_TAG('M','A','J',' ')}, /* Majang */
+ {"mr", HB_TAG('M','A','R',' ')}, /* Marathi */
+ {"ms", HB_TAG('M','L','Y',' ')}, /* Malay */
+ {"mt", HB_TAG('M','T','S',' ')}, /* Maltese */
+ {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari */
+ {"my", HB_TAG('B','R','M',' ')}, /* Burmese */
+ {"mym", HB_TAG('M','E','N',' ')}, /* Me'en */
+ {"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */
+ {"nb", HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål */
+ {"nco", HB_TAG('S','I','B',' ')}, /* Sibe */
+ {"ne", HB_TAG('N','E','P',' ')}, /* Nepali */
+ {"new", HB_TAG('N','E','W',' ')}, /* Newari */
+ {"ng", HB_TAG('N','D','G',' ')}, /* Ndonga */
+ {"ngl", HB_TAG('L','M','W',' ')}, /* Lomwe */
+ {"niu", HB_TAG('N','I','U',' ')}, /* Niuean */
+ {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */
+ {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */
+ {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk */
+ {"no", HB_TAG('N','O','R',' ')}, /* Norwegian (deprecated) */
+ {"nog", HB_TAG('N','O','G',' ')}, /* Nogai */
+ {"nqo", HB_TAG('N','K','O',' ')}, /* N'Ko */
+ {"nsk", HB_TAG('N','A','S',' ')}, /* Naskapi */
+ {"ny", HB_TAG('C','H','I',' ')}, /* Nyanja */
+ {"oc", HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */
+ {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa */
+ {"om", HB_TAG('O','R','O',' ')}, /* Oromo */
+ {"or", HB_TAG('O','R','I',' ')}, /* Oriya */
+ {"os", HB_TAG('O','S','S',' ')}, /* Ossetian */
+ {"pa", HB_TAG('P','A','N',' ')}, /* Panjabi */
+ {"pi", HB_TAG('P','A','L',' ')}, /* Pali */
+ {"pl", HB_TAG('P','L','K',' ')}, /* Polish */
+ {"plp", HB_TAG('P','A','P',' ')}, /* Palpa */
+ {"prs", HB_TAG('D','R','I',' ')}, /* Dari */
+ {"ps", HB_TAG('P','A','S',' ')}, /* Pushto */
+ {"pt", HB_TAG('P','T','G',' ')}, /* Portuguese */
+ {"raj", HB_TAG('R','A','J',' ')}, /* Rajasthani */
+ {"ria", HB_TAG('R','I','A',' ')}, /* Riang (India) */
+ {"ril", HB_TAG('R','I','A',' ')}, /* Riang (Myanmar) */
+ {"ro", HB_TAG('R','O','M',' ')}, /* Romanian */
+ {"rom", HB_TAG('R','O','Y',' ')}, /* Romany */
+ {"ru", HB_TAG('R','U','S',' ')}, /* Russian */
+ {"rue", HB_TAG('R','S','Y',' ')}, /* Rusyn */
+ {"sa", HB_TAG('S','A','N',' ')}, /* Sanskrit */
+ {"sah", HB_TAG('Y','A','K',' ')}, /* Yakut */
+ {"sat", HB_TAG('S','A','T',' ')}, /* Santali */
+ {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */
+ {"sd", HB_TAG('S','N','D',' ')}, /* Sindhi */
+ {"se", HB_TAG('N','S','M',' ')}, /* Northern Sami */
+ {"seh", HB_TAG('S','N','A',' ')}, /* Sena */
+ {"sel", HB_TAG('S','E','L',' ')}, /* Selkup */
+ {"sg", HB_TAG('S','G','O',' ')}, /* Sango */
+ {"shn", HB_TAG('S','H','N',' ')}, /* Shan */
+ {"si", HB_TAG('S','N','H',' ')}, /* Sinhala */
+ {"sid", HB_TAG('S','I','D',' ')}, /* Sidamo */
+ {"sjd", HB_TAG('K','S','M',' ')}, /* Kildin Sami */
+ {"sk", HB_TAG('S','K','Y',' ')}, /* Slovak */
+ {"skr", HB_TAG('S','R','K',' ')}, /* Seraiki */
+ {"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */
+ {"sm", HB_TAG('S','M','O',' ')}, /* Samoan */
+ {"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */
+ {"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */
+ {"smn", HB_TAG('I','S','M',' ')}, /* Inari Sami */
+ {"sms", HB_TAG('S','K','S',' ')}, /* Skolt Sami */
+ {"snk", HB_TAG('S','N','K',' ')}, /* Soninke */
+ {"so", HB_TAG('S','M','L',' ')}, /* Somali */
+ {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian */
+ {"sr", HB_TAG('S','R','B',' ')}, /* Serbian */
+ {"srr", HB_TAG('S','R','R',' ')}, /* Serer */
+ {"suq", HB_TAG('S','U','R',' ')}, /* Suri */
+ {"sv", HB_TAG('S','V','E',' ')}, /* Swedish */
+ {"sva", HB_TAG('S','V','A',' ')}, /* Svan */
+ {"sw", HB_TAG('S','W','K',' ')}, /* Swahili */
+ {"swb", HB_TAG('C','M','R',' ')}, /* Comorian */
+ {"syr", HB_TAG('S','Y','R',' ')}, /* Syriac */
+ {"ta", HB_TAG('T','A','M',' ')}, /* Tamil */
+ {"tcy", HB_TAG('T','U','L',' ')}, /* Tulu */
+ {"te", HB_TAG('T','E','L',' ')}, /* Telugu */
+ {"tg", HB_TAG('T','A','J',' ')}, /* Tajik */
+ {"th", HB_TAG('T','H','A',' ')}, /* Thai */
+ {"ti", HB_TAG('T','G','Y',' ')}, /* Tigrinya */
+ {"tig", HB_TAG('T','G','R',' ')}, /* Tigre */
+ {"tk", HB_TAG('T','K','M',' ')}, /* Turkmen */
+ {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */
+ {"tnz", HB_TAG('T','N','G',' ')}, /* Tonga (Thailand) */
+ {"to", HB_TAG('T','N','G',' ')}, /* Tonga (Tonga Islands) */
+ {"tog", HB_TAG('T','N','G',' ')}, /* Tonga (Nyasa) */
+ {"toi", HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */
+ {"tr", HB_TAG('T','R','K',' ')}, /* Turkish */
+ {"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */
+ {"tt", HB_TAG('T','A','T',' ')}, /* Tatar */
+ {"tw", HB_TAG('T','W','I',' ')}, /* Twi */
+ {"ty", HB_TAG('T','H','T',' ')}, /* Tahitian */
+ {"udm", HB_TAG('U','D','M',' ')}, /* Udmurt */
+ {"ug", HB_TAG('U','Y','G',' ')}, /* Uighur */
+ {"uk", HB_TAG('U','K','R',' ')}, /* Ukrainian */
+ {"unr", HB_TAG('M','U','N',' ')}, /* Mundari */
+ {"ur", HB_TAG('U','R','D',' ')}, /* Urdu */
+ {"uz", HB_TAG('U','Z','B',' ')}, /* Uzbek */
+ {"ve", HB_TAG('V','E','N',' ')}, /* Venda */
+ {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */
+ {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */
+ {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */
+ {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */
+ {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */
+ {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */
+ {"xom", HB_TAG('K','M','O',' ')}, /* Komo (Sudan) */
+ {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */
+ {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish */
+ {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */
+ {"yso", HB_TAG('N','I','S',' ')}, /* Nisi (China) */
+ {"zh-cn", HB_TAG('Z','H','S',' ')}, /* Chinese (China) */
+ {"zh-hk", HB_TAG('Z','H','H',' ')}, /* Chinese (Hong Kong) */
+ {"zh-mo", HB_TAG('Z','H','T',' ')}, /* Chinese (Macao) */
+ {"zh-sg", HB_TAG('Z','H','S',' ')}, /* Chinese (Singapore) */
+ {"zh-tw", HB_TAG('Z','H','T',' ')}, /* Chinese (Taiwan) */
+ {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */
+ {"zu", HB_TAG('Z','U','L',' ')} /* Zulu */
+
+ /* I couldn't find the language id for these */
+
+/*{"??", HB_TAG('A','G','W',' ')},*/ /* Agaw */
+/*{"??", HB_TAG('A','L','S',' ')},*/ /* Alsatian */
+/*{"??", HB_TAG('A','L','T',' ')},*/ /* Altai */
+/*{"??", HB_TAG('A','R','K',' ')},*/ /* Arakanese */
+/*{"??", HB_TAG('A','T','H',' ')},*/ /* Athapaskan */
+/*{"??", HB_TAG('B','A','G',' ')},*/ /* Baghelkhandi */
+/*{"??", HB_TAG('B','A','L',' ')},*/ /* Balkar */
+/*{"??", HB_TAG('B','A','U',' ')},*/ /* Baule */
+/*{"??", HB_TAG('B','B','R',' ')},*/ /* Berber */
+/*{"??", HB_TAG('B','C','R',' ')},*/ /* Bible Cree */
+/*{"??", HB_TAG('B','E','L',' ')},*/ /* Belarussian */
+/*{"??", HB_TAG('B','I','L',' ')},*/ /* Bilen */
+/*{"??", HB_TAG('B','K','F',' ')},*/ /* Blackfoot */
+/*{"??", HB_TAG('B','L','N',' ')},*/ /* Balante */
+/*{"??", HB_TAG('B','M','L',' ')},*/ /* Bamileke */
+/*{"??", HB_TAG('B','R','I',' ')},*/ /* Braj Bhasha */
+/*{"??", HB_TAG('C','H','G',' ')},*/ /* Chaha Gurage */
+/*{"??", HB_TAG('C','H','H',' ')},*/ /* Chattisgarhi */
+/*{"??", HB_TAG('C','H','K',' ')},*/ /* Chukchi */
+/*{"??", HB_TAG('D','J','R',' ')},*/ /* Djerma */
+/*{"??", HB_TAG('D','N','G',' ')},*/ /* Dangme */
+/*{"??", HB_TAG('E','C','R',' ')},*/ /* Eastern Cree */
+/*{"??", HB_TAG('F','A','N',' ')},*/ /* French Antillean */
+/*{"??", HB_TAG('F','L','E',' ')},*/ /* Flemish */
+/*{"??", HB_TAG('F','N','E',' ')},*/ /* Forest Nenets */
+/*{"??", HB_TAG('F','T','A',' ')},*/ /* Futa */
+/*{"??", HB_TAG('G','A','R',' ')},*/ /* Garshuni */
+/*{"??", HB_TAG('G','E','Z',' ')},*/ /* Ge'ez */
+/*{"??", HB_TAG('H','A','L',' ')},*/ /* Halam */
+/*{"??", HB_TAG('H','A','R',' ')},*/ /* Harauti */
+/*{"??", HB_TAG('H','A','W',' ')},*/ /* Hawaiin */
+/*{"??", HB_TAG('H','B','N',' ')},*/ /* Hammer-Banna */
+/*{"??", HB_TAG('H','M','A',' ')},*/ /* High Mari */
+/*{"??", HB_TAG('H','N','D',' ')},*/ /* Hindko */
+/*{"??", HB_TAG('I','J','O',' ')},*/ /* Ijo */
+/*{"??", HB_TAG('I','L','O',' ')},*/ /* Ilokano */
+/*{"??", HB_TAG('I','R','T',' ')},*/ /* Irish Traditional */
+/*{"??", HB_TAG('J','U','L',' ')},*/ /* Jula */
+/*{"??", HB_TAG('K','A','R',' ')},*/ /* Karachay */
+/*{"??", HB_TAG('K','E','B',' ')},*/ /* Kebena */
+/*{"??", HB_TAG('K','G','E',' ')},*/ /* Khutsuri Georgian */
+/*{"??", HB_TAG('K','H','A',' ')},*/ /* Khakass */
+/*{"??", HB_TAG('K','H','K',' ')},*/ /* Khanty-Kazim */
+/*{"??", HB_TAG('K','H','S',' ')},*/ /* Khanty-Shurishkar */
+/*{"??", HB_TAG('K','H','V',' ')},*/ /* Khanty-Vakhi */
+/*{"??", HB_TAG('K','I','S',' ')},*/ /* Kisii */
+/*{"??", HB_TAG('K','K','N',' ')},*/ /* Kokni */
+/*{"??", HB_TAG('K','M','S',' ')},*/ /* Komso */
+/*{"??", HB_TAG('K','O','D',' ')},*/ /* Kodagu */
+/*{"??", HB_TAG('K','O','H',' ')},*/ /* Korean Old Hangul */
+/*{"??", HB_TAG('K','O','N',' ')},*/ /* Kikongo */
+/*{"??", HB_TAG('K','R','K',' ')},*/ /* Karakalpak */
+/*{"??", HB_TAG('K','R','N',' ')},*/ /* Karen */
+/*{"??", HB_TAG('K','U','L',' ')},*/ /* Kulvi */
+/*{"??", HB_TAG('L','A','H',' ')},*/ /* Lahuli */
+/*{"??", HB_TAG('L','A','M',' ')},*/ /* Lambani */
+/*{"??", HB_TAG('L','C','R',' ')},*/ /* L-Cree */
+/*{"??", HB_TAG('L','E','Z',' ')},*/ /* Lezgi */
+/*{"??", HB_TAG('L','M','A',' ')},*/ /* Low Mari */
+/*{"??", HB_TAG('L','U','B',' ')},*/ /* Luba */
+/*{"??", HB_TAG('L','U','G',' ')},*/ /* Luganda */
+/*{"??", HB_TAG('L','U','H',' ')},*/ /* Luhya */
+/*{"??", HB_TAG('M','A','K',' ')},*/ /* Makua */
+/*{"??", HB_TAG('M','A','L',' ')},*/ /* Malayalam Traditional */
+/*{"??", HB_TAG('M','B','N',' ')},*/ /* Mbundu */
+/*{"??", HB_TAG('M','I','Z',' ')},*/ /* Mizo */
+/*{"??", HB_TAG('M','L','N',' ')},*/ /* Malinke */
+/*{"??", HB_TAG('M','N','K',' ')},*/ /* Maninka */
+/*{"??", HB_TAG('M','O','R',' ')},*/ /* Moroccan */
+/*{"??", HB_TAG('N','A','G',' ')},*/ /* Naga-Assamese */
+/*{"??", HB_TAG('N','C','R',' ')},*/ /* N-Cree */
+/*{"??", HB_TAG('N','D','B',' ')},*/ /* Ndebele */
+/*{"??", HB_TAG('N','G','R',' ')},*/ /* Nagari */
+/*{"??", HB_TAG('N','H','C',' ')},*/ /* Norway House Cree */
+/*{"??", HB_TAG('N','K','L',' ')},*/ /* Nkole */
+/*{"??", HB_TAG('N','T','A',' ')},*/ /* Northern Tai */
+/*{"??", HB_TAG('O','C','R',' ')},*/ /* Oji-Cree */
+/*{"??", HB_TAG('P','A','A',' ')},*/ /* Palestinian Aramaic */
+/*{"??", HB_TAG('P','G','R',' ')},*/ /* Polytonic Greek */
+/*{"??", HB_TAG('P','L','G',' ')},*/ /* Palaung */
+/*{"??", HB_TAG('Q','I','N',' ')},*/ /* Chin */
+/*{"??", HB_TAG('R','B','U',' ')},*/ /* Russian Buriat */
+/*{"??", HB_TAG('R','C','R',' ')},*/ /* R-Cree */
+/*{"??", HB_TAG('R','M','S',' ')},*/ /* Rhaeto-Romanic */
+/*{"??", HB_TAG('R','U','A',' ')},*/ /* Ruanda */
+/*{"??", HB_TAG('S','A','Y',' ')},*/ /* Sayisi */
+/*{"??", HB_TAG('S','E','K',' ')},*/ /* Sekota */
+/*{"??", HB_TAG('S','I','G',' ')},*/ /* Silte Gurage */
+/*{"??", HB_TAG('S','L','A',' ')},*/ /* Slavey */
+/*{"??", HB_TAG('S','O','G',' ')},*/ /* Sodo Gurage */
+/*{"??", HB_TAG('S','O','T',' ')},*/ /* Sotho */
+/*{"??", HB_TAG('S','W','A',' ')},*/ /* Swadaya Aramaic */
+/*{"??", HB_TAG('S','W','Z',' ')},*/ /* Swazi */
+/*{"??", HB_TAG('S','X','T',' ')},*/ /* Sutu */
+/*{"??", HB_TAG('T','A','B',' ')},*/ /* Tabasaran */
+/*{"??", HB_TAG('T','C','R',' ')},*/ /* TH-Cree */
+/*{"??", HB_TAG('T','G','N',' ')},*/ /* Tongan */
+/*{"??", HB_TAG('T','M','N',' ')},*/ /* Temne */
+/*{"??", HB_TAG('T','N','E',' ')},*/ /* Tundra Nenets */
+/*{"??", HB_TAG('T','O','D',' ')},*/ /* Todo */
+/*{"??", HB_TAG('T','U','A',' ')},*/ /* Turoyo Aramaic */
+/*{"??", HB_TAG('T','U','V',' ')},*/ /* Tuvin */
+/*{"??", HB_TAG('W','C','R',' ')},*/ /* West-Cree */
+/*{"??", HB_TAG('X','B','D',' ')},*/ /* Tai Lue */
+/*{"??", HB_TAG('Y','C','R',' ')},*/ /* Y-Cree */
+/*{"??", HB_TAG('Y','I','C',' ')},*/ /* Yi Classic */
+/*{"??", HB_TAG('Y','I','M',' ')},*/ /* Yi Modern */
+/*{"??", HB_TAG('Z','H','P',' ')},*/ /* Chinese Phonetic */
+};
+
+static int
+lang_compare_first_component (const char *a,
+ const char *b)
+{
+ unsigned int da, db;
+ const char *p;
+
+ p = strstr (a, "-");
+ da = p ? (unsigned int) (p - a) : strlen (a);
+
+ p = strstr (b, "-");
+ db = p ? (unsigned int) (p - b) : strlen (b);
+
+ return strncmp (a, b, MAX (da, db));
+}
+
+static hb_bool_t
+lang_matches (const char *lang_str, const char *spec)
+{
+ unsigned int len = strlen (spec);
+
+ return lang_str && strncmp (lang_str, spec, len) == 0 &&
+ (lang_str[len] == '\0' || lang_str[len] == '-');
+}
+
+hb_tag_t
+hb_ot_tag_from_language (hb_language_t language)
+{
+ const char *lang_str;
+ LangTag *lang_tag;
+
+ if (language == NULL)
+ return HB_OT_TAG_DEFAULT_LANGUAGE;
+
+ lang_str = hb_language_to_string (language);
+
+ if (0 == strcmp (lang_str, "x-hbot")) {
+ char tag[4];
+ int i;
+ lang_str += 6;
+#define IS_LETTER(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+#define TO_UPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) + 'A' - 'a' : (c))
+ for (i = 0; i < 4 && IS_LETTER (lang_str[i]); i++)
+ tag[i] = TO_UPPER (lang_str[i]);
+ for (; i < 4; i++)
+ tag[i] = ' ';
+ return HB_TAG_STR (tag);
+ }
+
+ /* find a language matching in the first component */
+ lang_tag = bsearch (lang_str, ot_languages,
+ ARRAY_LENGTH (ot_languages), sizeof (LangTag),
+ (hb_compare_func_t) lang_compare_first_component);
+
+ /* we now need to find the best language matching */
+ if (lang_tag)
+ {
+ hb_bool_t found = FALSE;
+
+ /* go to the final one matching in the first component */
+ while (lang_tag + 1 < ot_languages + ARRAY_LENGTH (ot_languages) &&
+ lang_compare_first_component (lang_str, (lang_tag + 1)->language) == 0)
+ lang_tag++;
+
+ /* go back, find which one matches completely */
+ while (lang_tag >= ot_languages &&
+ lang_compare_first_component (lang_str, lang_tag->language) == 0)
+ {
+ if (lang_matches (lang_str, lang_tag->language)) {
+ found = TRUE;
+ break;
+ }
+
+ lang_tag--;
+ }
+
+ if (!found)
+ lang_tag = NULL;
+ }
+
+ if (lang_tag)
+ return lang_tag->tag;
+
+ return HB_OT_TAG_DEFAULT_LANGUAGE;
+}
+
+hb_language_t
+hb_ot_tag_to_language (hb_tag_t tag)
+{
+ unsigned int i;
+ unsigned char buf[11] = "x-hbot";
+
+ for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
+ if (ot_languages[i].tag == tag)
+ return hb_language_from_string (ot_languages[i].language);
+
+ buf[6] = tag >> 24;
+ buf[7] = (tag >> 16) & 0xFF;
+ buf[8] = (tag >> 8) & 0xFF;
+ buf[9] = tag & 0xFF;
+ buf[10] = '\0';
+ return hb_language_from_string ((char *) buf);
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-ot-tag.h b/third_party/harfbuzz-ng/src/hb-ot-tag.h
new file mode 100644
index 0000000..1eec69f
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot-tag.h
@@ -0,0 +1,54 @@
+/*
+ * 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_OT_TAG_H
+#define HB_OT_TAG_H
+
+#include "hb-common.h"
+#include "hb-language.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T')
+#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't')
+
+const hb_tag_t *
+hb_ot_tags_from_script (hb_script_t script);
+
+hb_script_t
+hb_ot_tag_to_script (hb_tag_t tag);
+
+hb_tag_t
+hb_ot_tag_from_language (hb_language_t language);
+
+hb_language_t
+hb_ot_tag_to_language (hb_tag_t tag);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_TAG_H */
diff --git a/third_party/harfbuzz-ng/src/hb-ot.h b/third_party/harfbuzz-ng/src/hb-ot.h
new file mode 100644
index 0000000..268711d
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-ot.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_OT_H
+#define HB_OT_H
+
+#include "hb.h"
+
+#include "hb-ot-layout.h"
+#include "hb-ot-shape.h"
+#include "hb-ot-tag.h"
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+#endif /* HB_OT_H */
diff --git a/third_party/harfbuzz-ng/src/hb-private.h b/third_party/harfbuzz-ng/src/hb-private.h
new file mode 100644
index 0000000..96b9464
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-private.h
@@ -0,0 +1,270 @@
+/*
+ * 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_PRIVATE_H
+#define HB_PRIVATE_H
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hb-common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* We only use these two for debug output. However, the debug code is
+ * always seen by the compiler (and optimized out in non-debug builds.
+ * If including these becomes a problem, we can start thinking about
+ * someway around that. */
+#include <stdio.h>
+#include <errno.h>
+
+HB_BEGIN_DECLS
+
+
+/* Essentials */
+
+#ifndef NULL
+# define NULL ((void *) 0)
+#endif
+
+#undef FALSE
+#define FALSE 0
+
+#undef TRUE
+#define TRUE 1
+
+
+/* Basics */
+
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#undef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+#undef ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
+
+#define HB_STMT_START do
+#define HB_STMT_END while (0)
+
+#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
+#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond))
+#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond))
+
+
+/* Misc */
+
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0)
+#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1))
+#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0))
+#else
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#endif
+
+#ifndef __GNUC__
+#undef __attribute__
+#define __attribute__(x)
+#endif
+
+#if __GNUC__ >= 3
+#define HB_PURE_FUNC __attribute__((pure))
+#define HB_CONST_FUNC __attribute__((const))
+#else
+#define HB_PURE_FUNC
+#define HB_CONST_FUNC
+#endif
+#if __GNUC__ >= 4
+#define HB_UNUSED __attribute__((unused))
+#else
+#define HB_UNUSED
+#endif
+
+#ifndef HB_INTERNAL
+# define HB_INTERNAL __attribute__((__visibility__("hidden")))
+#endif
+
+
+#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+#ifdef _MSC_VER
+#undef inline
+#define inline __inline
+#endif
+
+#ifdef __STRICT_ANSI__
+#undef inline
+#define inline __inline__
+#endif
+
+
+#if __GNUC__ >= 3
+#define HB_FUNC __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+#define HB_FUNC __FUNCSIG__
+#else
+#define HB_FUNC __func__
+#endif
+
+
+/* Return the number of 1 bits in mask. */
+static inline HB_CONST_FUNC unsigned int
+_hb_popcount32 (uint32_t mask)
+{
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ return __builtin_popcount (mask);
+#else
+ /* "HACKMEM 169" */
+ register uint32_t y;
+ y = (mask >> 1) &033333333333;
+ y = mask - y - ((y >>1) & 033333333333);
+ return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+/* Returns the number of bits needed to store number */
+static inline HB_CONST_FUNC unsigned int
+_hb_bit_storage (unsigned int number)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+ return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0;
+#else
+ register unsigned int n_bits = 0;
+ while (number) {
+ n_bits++;
+ number >>= 1;
+ }
+ return n_bits;
+#endif
+}
+
+/* Returns the number of zero bits in the least significant side of number */
+static inline HB_CONST_FUNC unsigned int
+_hb_ctz (unsigned int number)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+ return likely (number) ? __builtin_ctz (number) : 0;
+#else
+ register unsigned int n_bits = 0;
+ if (unlikely (!number)) return 0;
+ while (!(number & 1)) {
+ n_bits++;
+ number >>= 1;
+ }
+ return n_bits;
+#endif
+}
+
+/* Type of bsearch() / qsort() compare function */
+typedef int (*hb_compare_func_t) (const void *, const void *);
+
+
+/* We need external help for these */
+
+#ifdef HAVE_GLIB
+
+#include <glib.h>
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_fetch_and_add(AI, V) g_atomic_int_exchange_and_add (&(AI), V)
+#define hb_atomic_int_get(AI) g_atomic_int_get (&(AI))
+#define hb_atomic_int_set(AI, V) g_atomic_int_set (&(AI), V)
+
+typedef GStaticMutex hb_mutex_t;
+#define HB_MUTEX_INIT G_STATIC_MUTEX_INIT
+#define hb_mutex_init(M) g_static_mutex_init (&M)
+#define hb_mutex_lock(M) g_static_mutex_lock (&M)
+#define hb_mutex_trylock(M) g_static_mutex_trylock (&M)
+#define hb_mutex_unlock(M) g_static_mutex_unlock (&M)
+
+#else
+
+#ifdef _MSC_VER
+#pragma message(__LOC__"Could not find any system to define platform macros, library will NOT be thread-safe")
+#else
+#warning "Could not find any system to define platform macros, library will NOT be thread-safe"
+#endif
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_fetch_and_add(AI, V) ((AI) += (V), (AI) - (V))
+#define hb_atomic_int_get(AI) (AI)
+#define hb_atomic_int_set(AI, V) HB_STMT_START { (AI) = (V); } HB_STMT_END
+
+typedef int hb_mutex_t;
+#define HB_MUTEX_INIT 0
+#define hb_mutex_init(M) HB_STMT_START { (M) = 0; } HB_STMT_END
+#define hb_mutex_lock(M) HB_STMT_START { (M) = 1; } HB_STMT_END
+#define hb_mutex_trylock(M) ((M) = 1, 1)
+#define hb_mutex_unlock(M) HB_STMT_START { (M) = 0; } HB_STMT_END
+
+#endif
+
+
+/* Big-endian handling */
+
+#define hb_be_uint16(v) ((uint16_t) ((((const uint8_t *)&(v))[0] << 8) + (((const uint8_t *)&(v))[1])))
+
+#define hb_be_uint16_put(v,V) HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
+#define hb_be_uint16_get(v) (uint16_t) ((v[0] << 8) + v[1])
+#define hb_be_uint16_cmp(a,b) (a[0] == b[0] && a[1] == b[1])
+
+#define hb_be_uint32_put(v,V) HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END
+#define hb_be_uint32_get(v) (uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
+#define hb_be_uint32_cmp(a,b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
+
+
+/* Debug */
+
+#ifndef HB_DEBUG
+#define HB_DEBUG 0
+#endif
+
+static inline hb_bool_t /* always returns TRUE */
+_hb_trace (const char *what,
+ const char *function,
+ const void *obj,
+ unsigned int depth,
+ unsigned int max_depth)
+{
+ (void) ((depth < max_depth) && fprintf (stderr, "%s(%p) %-*d-> %s\n", what, obj, depth, depth, function));
+ return TRUE;
+}
+
+
+#include "hb-object-private.h"
+
+
+HB_END_DECLS
+
+#endif /* HB_PRIVATE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-shape.cc b/third_party/harfbuzz-ng/src/hb-shape.cc
new file mode 100644
index 0000000..a73977b
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-shape.cc
@@ -0,0 +1,65 @@
+/*
+ * 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-shape.h"
+
+#include "hb-buffer-private.hh"
+
+#include "hb-ot-shape.h"
+
+#ifdef HAVE_GRAPHITE
+#include "hb-graphite.h"
+#endif
+
+HB_BEGIN_DECLS
+
+
+void
+hb_shape (hb_font_t *font,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ hb_feature_t *features,
+ unsigned int num_features)
+{
+#if 0 && defined(HAVE_GRAPHITE)
+ hb_blob_t *silf_blob;
+ silf_blob = hb_face_get_table (face, HB_GRAPHITE_TAG_Silf);
+ if (hb_blob_get_length(silf_blob))
+ {
+ hb_graphite_shape(font, face, buffer, features, num_features);
+ hb_blob_destroy(silf_blob);
+ return;
+ }
+ hb_blob_destroy(silf_blob);
+#endif
+
+ hb_ot_shape (font, face, buffer, features, num_features);
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-shape.h b/third_party/harfbuzz-ng/src/hb-shape.h
new file mode 100644
index 0000000..48f1a55
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-shape.h
@@ -0,0 +1,54 @@
+/*
+ * 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_SHAPE_H
+#define HB_SHAPE_H
+
+#include "hb-common.h"
+#include "hb-buffer.h"
+#include "hb-font.h"
+
+HB_BEGIN_DECLS
+
+
+typedef struct _hb_feature_t {
+ hb_tag_t tag;
+ uint32_t value;
+ unsigned int start;
+ unsigned int end;
+} hb_feature_t;
+
+void
+hb_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_SHAPE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-unicode-private.h b/third_party/harfbuzz-ng/src/hb-unicode-private.h
new file mode 100644
index 0000000..419404b
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-unicode-private.h
@@ -0,0 +1,64 @@
+/*
+ * 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_UNICODE_PRIVATE_H
+#define HB_UNICODE_PRIVATE_H
+
+#include "hb-private.h"
+
+#include "hb-unicode.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * hb_unicode_funcs_t
+ */
+
+struct _hb_unicode_funcs_t {
+ hb_reference_count_t ref_count;
+
+ hb_bool_t immutable;
+
+ struct {
+ hb_unicode_get_general_category_func_t get_general_category;
+ hb_unicode_get_combining_class_func_t get_combining_class;
+ hb_unicode_get_mirroring_func_t get_mirroring;
+ hb_unicode_get_script_func_t get_script;
+ hb_unicode_get_eastasian_width_func_t get_eastasian_width;
+ } v;
+};
+
+extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_nil;
+
+
+HB_INTERNAL hb_direction_t
+_hb_script_get_horizontal_direction (hb_script_t script);
+
+
+HB_END_DECLS
+
+#endif /* HB_UNICODE_PRIVATE_H */
diff --git a/third_party/harfbuzz-ng/src/hb-unicode.c b/third_party/harfbuzz-ng/src/hb-unicode.c
new file mode 100644
index 0000000..2ab308b
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-unicode.c
@@ -0,0 +1,364 @@
+/*
+ * 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-unicode-private.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * hb_unicode_funcs_t
+ */
+
+static hb_codepoint_t hb_unicode_get_mirroring_nil (hb_codepoint_t unicode) { return unicode; }
+static hb_category_t hb_unicode_get_general_category_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_CATEGORY_OTHER_LETTER; }
+static hb_script_t hb_unicode_get_script_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_SCRIPT_UNKNOWN; }
+static unsigned int hb_unicode_get_combining_class_nil (hb_codepoint_t unicode HB_UNUSED) { return 0; }
+static unsigned int hb_unicode_get_eastasian_width_nil (hb_codepoint_t unicode HB_UNUSED) { return 1; }
+
+hb_unicode_funcs_t _hb_unicode_funcs_nil = {
+ HB_REFERENCE_COUNT_INVALID, /* ref_count */
+ TRUE, /* immutable */
+ {
+ hb_unicode_get_general_category_nil,
+ hb_unicode_get_combining_class_nil,
+ hb_unicode_get_mirroring_nil,
+ hb_unicode_get_script_nil,
+ hb_unicode_get_eastasian_width_nil
+ }
+};
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_create (void)
+{
+ hb_unicode_funcs_t *ufuncs;
+
+ if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
+ return &_hb_unicode_funcs_nil;
+
+ ufuncs->v = _hb_unicode_funcs_nil.v;
+
+ return ufuncs;
+}
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs)
+{
+ HB_OBJECT_DO_REFERENCE (ufuncs);
+}
+
+unsigned int
+hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs)
+{
+ HB_OBJECT_DO_GET_REFERENCE_COUNT (ufuncs);
+}
+
+void
+hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
+{
+ HB_OBJECT_DO_DESTROY (ufuncs);
+
+ free (ufuncs);
+}
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_copy (hb_unicode_funcs_t *other_ufuncs)
+{
+ hb_unicode_funcs_t *ufuncs;
+
+ if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
+ return &_hb_unicode_funcs_nil;
+
+ ufuncs->v = other_ufuncs->v;
+
+ return ufuncs;
+}
+
+void
+hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
+{
+ if (HB_OBJECT_IS_INERT (ufuncs))
+ return;
+
+ ufuncs->immutable = TRUE;
+}
+
+hb_bool_t
+hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->immutable;
+}
+
+
+void
+hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_mirroring_func_t mirroring_func)
+{
+ if (ufuncs->immutable)
+ return;
+
+ ufuncs->v.get_mirroring = mirroring_func ? mirroring_func : hb_unicode_get_mirroring_nil;
+}
+
+void
+hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_general_category_func_t general_category_func)
+{
+ if (ufuncs->immutable)
+ return;
+
+ ufuncs->v.get_general_category = general_category_func ? general_category_func : hb_unicode_get_general_category_nil;
+}
+
+void
+hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_script_func_t script_func)
+{
+ if (ufuncs->immutable)
+ return;
+
+ ufuncs->v.get_script = script_func ? script_func : hb_unicode_get_script_nil;
+}
+
+void
+hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_combining_class_func_t combining_class_func)
+{
+ if (ufuncs->immutable)
+ return;
+
+ ufuncs->v.get_combining_class = combining_class_func ? combining_class_func : hb_unicode_get_combining_class_nil;
+}
+
+void
+hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_eastasian_width_func_t eastasian_width_func)
+{
+ if (ufuncs->immutable)
+ return;
+
+ ufuncs->v.get_eastasian_width = eastasian_width_func ? eastasian_width_func : hb_unicode_get_eastasian_width_nil;
+}
+
+
+hb_unicode_get_mirroring_func_t
+hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->v.get_mirroring;
+}
+
+hb_unicode_get_general_category_func_t
+hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->v.get_general_category;
+}
+
+hb_unicode_get_script_func_t
+hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->v.get_script;
+}
+
+hb_unicode_get_combining_class_func_t
+hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->v.get_combining_class;
+}
+
+hb_unicode_get_eastasian_width_func_t
+hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs)
+{
+ return ufuncs->v.get_eastasian_width;
+}
+
+
+
+hb_codepoint_t
+hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ return ufuncs->v.get_mirroring (unicode);
+}
+
+hb_category_t
+hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ return ufuncs->v.get_general_category (unicode);
+}
+
+hb_script_t
+hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ return ufuncs->v.get_script (unicode);
+}
+
+unsigned int
+hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ return ufuncs->v.get_combining_class (unicode);
+}
+
+unsigned int
+hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ return ufuncs->v.get_eastasian_width (unicode);
+}
+
+
+
+#define LTR HB_DIRECTION_LTR
+#define RTL HB_DIRECTION_RTL
+const hb_direction_t horiz_dir[] =
+{
+ LTR, /* Zyyy */
+ LTR, /* Qaai */
+ RTL, /* Arab */
+ LTR, /* Armn */
+ LTR, /* Beng */
+ LTR, /* Bopo */
+ LTR, /* Cher */
+ LTR, /* Qaac */
+ LTR, /* Cyrl (Cyrs) */
+ LTR, /* Dsrt */
+ LTR, /* Deva */
+ LTR, /* Ethi */
+ LTR, /* Geor (Geon, Geoa) */
+ LTR, /* Goth */
+ LTR, /* Grek */
+ LTR, /* Gujr */
+ LTR, /* Guru */
+ LTR, /* Hani */
+ LTR, /* Hang */
+ RTL, /* Hebr */
+ LTR, /* Hira */
+ LTR, /* Knda */
+ LTR, /* Kana */
+ LTR, /* Khmr */
+ LTR, /* Laoo */
+ LTR, /* Latn (Latf, Latg) */
+ LTR, /* Mlym */
+ LTR, /* Mong */
+ LTR, /* Mymr */
+ LTR, /* Ogam */
+ LTR, /* Ital */
+ LTR, /* Orya */
+ LTR, /* Runr */
+ LTR, /* Sinh */
+ RTL, /* Syrc (Syrj, Syrn, Syre) */
+ LTR, /* Taml */
+ LTR, /* Telu */
+ RTL, /* Thaa */
+ LTR, /* Thai */
+ LTR, /* Tibt */
+ LTR, /* Cans */
+ LTR, /* Yiii */
+ LTR, /* Tglg */
+ LTR, /* Hano */
+ LTR, /* Buhd */
+ LTR, /* Tagb */
+
+ /* Unicode-4.0 additions */
+ LTR, /* Brai */
+ RTL, /* Cprt */
+ LTR, /* Limb */
+ LTR, /* Osma */
+ LTR, /* Shaw */
+ LTR, /* Linb */
+ LTR, /* Tale */
+ LTR, /* Ugar */
+
+ /* Unicode-4.1 additions */
+ LTR, /* Talu */
+ LTR, /* Bugi */
+ LTR, /* Glag */
+ LTR, /* Tfng */
+ LTR, /* Sylo */
+ LTR, /* Xpeo */
+ LTR, /* Khar */
+
+ /* Unicode-5.0 additions */
+ LTR, /* Zzzz */
+ LTR, /* Bali */
+ LTR, /* Xsux */
+ RTL, /* Phnx */
+ LTR, /* Phag */
+ RTL, /* Nkoo */
+
+ /* Unicode-5.1 additions */
+ LTR, /* Kali */
+ LTR, /* Lepc */
+ LTR, /* Rjng */
+ LTR, /* Sund */
+ LTR, /* Saur */
+ LTR, /* Cham */
+ LTR, /* Olck */
+ LTR, /* Vaii */
+ LTR, /* Cari */
+ LTR, /* Lyci */
+ LTR, /* Lydi */
+
+ /* Unicode-5.2 additions */
+ RTL, /* Avst */
+ LTR, /* Bamu */
+ LTR, /* Egyp */
+ RTL, /* Armi */
+ RTL, /* Phli */
+ RTL, /* Prti */
+ LTR, /* Java */
+ LTR, /* Kthi */
+ LTR, /* Lisu */
+ LTR, /* Mtei */
+ RTL, /* Sarb */
+ RTL, /* Orkh */
+ RTL, /* Samr */
+ LTR, /* Lana */
+ LTR, /* Tavt */
+
+ /* Unicode-6.0 additions */
+ LTR, /* Batk */
+ LTR, /* Brah */
+ RTL /* Mand */
+};
+#undef LTR
+#undef RTL
+
+hb_direction_t
+_hb_script_get_horizontal_direction (hb_script_t script)
+{
+ if (unlikely ((unsigned int) script >= ARRAY_LENGTH (horiz_dir)))
+ return HB_DIRECTION_LTR;
+
+ return horiz_dir[script];
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/hb-unicode.h b/third_party/harfbuzz-ng/src/hb-unicode.h
new file mode 100644
index 0000000..43b04ca
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb-unicode.h
@@ -0,0 +1,294 @@
+/*
+ * 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_UNICODE_H
+#define HB_UNICODE_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+/* Unicode General Category property */
+typedef enum
+{
+ HB_CATEGORY_CONTROL, /* Cc */
+ HB_CATEGORY_FORMAT, /* Cf */
+ HB_CATEGORY_UNASSIGNED, /* Cn */
+ HB_CATEGORY_PRIVATE_USE, /* Co */
+ HB_CATEGORY_SURROGATE, /* Cs */
+ HB_CATEGORY_LOWERCASE_LETTER, /* Ll */
+ HB_CATEGORY_MODIFIER_LETTER, /* Lm */
+ HB_CATEGORY_OTHER_LETTER, /* Lo */
+ HB_CATEGORY_TITLECASE_LETTER, /* Lt */
+ HB_CATEGORY_UPPERCASE_LETTER, /* Lu */
+ HB_CATEGORY_COMBINING_MARK, /* Mc */
+ HB_CATEGORY_ENCLOSING_MARK, /* Me */
+ HB_CATEGORY_NON_SPACING_MARK, /* Mn */
+ HB_CATEGORY_DECIMAL_NUMBER, /* Nd */
+ HB_CATEGORY_LETTER_NUMBER, /* Nl */
+ HB_CATEGORY_OTHER_NUMBER, /* No */
+ HB_CATEGORY_CONNECT_PUNCTUATION, /* Pc */
+ HB_CATEGORY_DASH_PUNCTUATION, /* Pd */
+ HB_CATEGORY_CLOSE_PUNCTUATION, /* Pe */
+ HB_CATEGORY_FINAL_PUNCTUATION, /* Pf */
+ HB_CATEGORY_INITIAL_PUNCTUATION, /* Pi */
+ HB_CATEGORY_OTHER_PUNCTUATION, /* Po */
+ HB_CATEGORY_OPEN_PUNCTUATION, /* Ps */
+ HB_CATEGORY_CURRENCY_SYMBOL, /* Sc */
+ HB_CATEGORY_MODIFIER_SYMBOL, /* Sk */
+ HB_CATEGORY_MATH_SYMBOL, /* Sm */
+ HB_CATEGORY_OTHER_SYMBOL, /* So */
+ HB_CATEGORY_LINE_SEPARATOR, /* Zl */
+ HB_CATEGORY_PARAGRAPH_SEPARATOR, /* Zp */
+ HB_CATEGORY_SPACE_SEPARATOR /* Zs */
+} hb_category_t;
+
+/* Unicode Script property */
+typedef enum
+{ /* ISO 15924 code */
+ HB_SCRIPT_INVALID_CODE = -1,
+ HB_SCRIPT_COMMON = 0, /* Zyyy */
+ HB_SCRIPT_INHERITED, /* Qaai */
+ HB_SCRIPT_ARABIC, /* Arab */
+ HB_SCRIPT_ARMENIAN, /* Armn */
+ HB_SCRIPT_BENGALI, /* Beng */
+ HB_SCRIPT_BOPOMOFO, /* Bopo */
+ HB_SCRIPT_CHEROKEE, /* Cher */
+ HB_SCRIPT_COPTIC, /* Qaac */
+ HB_SCRIPT_CYRILLIC, /* Cyrl (Cyrs) */
+ HB_SCRIPT_DESERET, /* Dsrt */
+ HB_SCRIPT_DEVANAGARI, /* Deva */
+ HB_SCRIPT_ETHIOPIC, /* Ethi */
+ HB_SCRIPT_GEORGIAN, /* Geor (Geon, Geoa) */
+ HB_SCRIPT_GOTHIC, /* Goth */
+ HB_SCRIPT_GREEK, /* Grek */
+ HB_SCRIPT_GUJARATI, /* Gujr */
+ HB_SCRIPT_GURMUKHI, /* Guru */
+ HB_SCRIPT_HAN, /* Hani */
+ HB_SCRIPT_HANGUL, /* Hang */
+ HB_SCRIPT_HEBREW, /* Hebr */
+ HB_SCRIPT_HIRAGANA, /* Hira */
+ HB_SCRIPT_KANNADA, /* Knda */
+ HB_SCRIPT_KATAKANA, /* Kana */
+ HB_SCRIPT_KHMER, /* Khmr */
+ HB_SCRIPT_LAO, /* Laoo */
+ HB_SCRIPT_LATIN, /* Latn (Latf, Latg) */
+ HB_SCRIPT_MALAYALAM, /* Mlym */
+ HB_SCRIPT_MONGOLIAN, /* Mong */
+ HB_SCRIPT_MYANMAR, /* Mymr */
+ HB_SCRIPT_OGHAM, /* Ogam */
+ HB_SCRIPT_OLD_ITALIC, /* Ital */
+ HB_SCRIPT_ORIYA, /* Orya */
+ HB_SCRIPT_RUNIC, /* Runr */
+ HB_SCRIPT_SINHALA, /* Sinh */
+ HB_SCRIPT_SYRIAC, /* Syrc (Syrj, Syrn, Syre) */
+ HB_SCRIPT_TAMIL, /* Taml */
+ HB_SCRIPT_TELUGU, /* Telu */
+ HB_SCRIPT_THAANA, /* Thaa */
+ HB_SCRIPT_THAI, /* Thai */
+ HB_SCRIPT_TIBETAN, /* Tibt */
+ HB_SCRIPT_CANADIAN_ABORIGINAL, /* Cans */
+ HB_SCRIPT_YI, /* Yiii */
+ HB_SCRIPT_TAGALOG, /* Tglg */
+ HB_SCRIPT_HANUNOO, /* Hano */
+ HB_SCRIPT_BUHID, /* Buhd */
+ HB_SCRIPT_TAGBANWA, /* Tagb */
+
+ /* Unicode-4.0 additions */
+ HB_SCRIPT_BRAILLE, /* Brai */
+ HB_SCRIPT_CYPRIOT, /* Cprt */
+ HB_SCRIPT_LIMBU, /* Limb */
+ HB_SCRIPT_OSMANYA, /* Osma */
+ HB_SCRIPT_SHAVIAN, /* Shaw */
+ HB_SCRIPT_LINEAR_B, /* Linb */
+ HB_SCRIPT_TAI_LE, /* Tale */
+ HB_SCRIPT_UGARITIC, /* Ugar */
+
+ /* Unicode-4.1 additions */
+ HB_SCRIPT_NEW_TAI_LUE, /* Talu */
+ HB_SCRIPT_BUGINESE, /* Bugi */
+ HB_SCRIPT_GLAGOLITIC, /* Glag */
+ HB_SCRIPT_TIFINAGH, /* Tfng */
+ HB_SCRIPT_SYLOTI_NAGRI, /* Sylo */
+ HB_SCRIPT_OLD_PERSIAN, /* Xpeo */
+ HB_SCRIPT_KHAROSHTHI, /* Khar */
+
+ /* Unicode-5.0 additions */
+ HB_SCRIPT_UNKNOWN, /* Zzzz */
+ HB_SCRIPT_BALINESE, /* Bali */
+ HB_SCRIPT_CUNEIFORM, /* Xsux */
+ HB_SCRIPT_PHOENICIAN, /* Phnx */
+ HB_SCRIPT_PHAGS_PA, /* Phag */
+ HB_SCRIPT_NKO, /* Nkoo */
+
+ /* Unicode-5.1 additions */
+ HB_SCRIPT_KAYAH_LI, /* Kali */
+ HB_SCRIPT_LEPCHA, /* Lepc */
+ HB_SCRIPT_REJANG, /* Rjng */
+ HB_SCRIPT_SUNDANESE, /* Sund */
+ HB_SCRIPT_SAURASHTRA, /* Saur */
+ HB_SCRIPT_CHAM, /* Cham */
+ HB_SCRIPT_OL_CHIKI, /* Olck */
+ HB_SCRIPT_VAI, /* Vaii */
+ HB_SCRIPT_CARIAN, /* Cari */
+ HB_SCRIPT_LYCIAN, /* Lyci */
+ HB_SCRIPT_LYDIAN, /* Lydi */
+
+ /* Unicode-5.2 additions */
+ HB_SCRIPT_AVESTAN, /* Avst */
+ HB_SCRIPT_BAMUM, /* Bamu */
+ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, /* Egyp */
+ HB_SCRIPT_IMPERIAL_ARAMAIC, /* Armi */
+ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, /* Phli */
+ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, /* Prti */
+ HB_SCRIPT_JAVANESE, /* Java */
+ HB_SCRIPT_KAITHI, /* Kthi */
+ HB_SCRIPT_LISU, /* Lisu */
+ HB_SCRIPT_MEETEI_MAYEK, /* Mtei */
+ HB_SCRIPT_OLD_SOUTH_ARABIAN, /* Sarb */
+ HB_SCRIPT_OLD_TURKIC, /* Orkh */
+ HB_SCRIPT_SAMARITAN, /* Samr */
+ HB_SCRIPT_TAI_THAM, /* Lana */
+ HB_SCRIPT_TAI_VIET, /* Tavt */
+
+ /* Unicode-6.0 additions */
+ HB_SCRIPT_BATAK, /* Batk */
+ HB_SCRIPT_BRAHMI, /* Brah */
+ HB_SCRIPT_MANDAIC /* Mand */
+} hb_script_t;
+
+
+/*
+ * hb_unicode_funcs_t
+ */
+
+typedef struct _hb_unicode_funcs_t hb_unicode_funcs_t;
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_create (void);
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs);
+
+unsigned int
+hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs);
+
+void
+hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_copy (hb_unicode_funcs_t *ufuncs);
+
+void
+hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs);
+
+hb_bool_t
+hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs);
+
+/*
+ * funcs
+ */
+
+
+/* typedefs */
+
+typedef hb_codepoint_t (*hb_unicode_get_mirroring_func_t) (hb_codepoint_t unicode);
+typedef hb_category_t (*hb_unicode_get_general_category_func_t) (hb_codepoint_t unicode);
+typedef hb_script_t (*hb_unicode_get_script_func_t) (hb_codepoint_t unicode);
+typedef unsigned int (*hb_unicode_get_combining_class_func_t) (hb_codepoint_t unicode);
+typedef unsigned int (*hb_unicode_get_eastasian_width_func_t) (hb_codepoint_t unicode);
+
+
+/* setters */
+
+void
+hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_mirroring_func_t mirroring_func);
+
+void
+hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_general_category_func_t general_category_func);
+
+void
+hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_script_func_t script_func);
+
+void
+hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_combining_class_func_t combining_class_func);
+
+void
+hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
+ hb_unicode_get_eastasian_width_func_t eastasian_width_func);
+
+
+/* getters */
+
+/* These never return NULL. Return fallback defaults instead. */
+
+hb_unicode_get_mirroring_func_t
+hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_general_category_func_t
+hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_script_func_t
+hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_combining_class_func_t
+hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_eastasian_width_func_t
+hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs);
+
+
+/* accessors */
+
+hb_codepoint_t
+hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+hb_category_t
+hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+hb_script_t
+hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+unsigned int
+hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+unsigned int
+hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+
+HB_END_DECLS
+
+#endif /* HB_UNICODE_H */
diff --git a/third_party/harfbuzz-ng/src/hb.h b/third_party/harfbuzz-ng/src/hb.h
new file mode 100644
index 0000000..691adee
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/hb.h
@@ -0,0 +1,41 @@
+/*
+ * 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_H
+#define HB_H
+
+#include "hb-blob.h"
+#include "hb-buffer.h"
+#include "hb-common.h"
+#include "hb-font.h"
+#include "hb-language.h"
+#include "hb-shape.h"
+#include "hb-unicode.h"
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+#endif /* HB_H */
diff --git a/third_party/harfbuzz-ng/src/main.cc b/third_party/harfbuzz-ng/src/main.cc
new file mode 100644
index 0000000..8126dae
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/main.cc
@@ -0,0 +1,196 @@
+/*
+ * 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
+ */
+
+#define HB_OT_LAYOUT_CC
+#include "hb-open-file-private.hh"
+#include "hb-ot-layout-gdef-private.hh"
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+HB_BEGIN_DECLS
+
+
+int
+main (int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
+ exit (1);
+ }
+
+ const char *font_data = NULL;
+ int len = 0;
+
+#ifdef HAVE_GLIB
+ GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL);
+ font_data = g_mapped_file_get_contents (mf);
+ len = g_mapped_file_get_length (mf);
+#else
+ FILE *f = fopen (argv[1], "rb");
+ fseek (f, 0, SEEK_END);
+ len = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ font_data = (const char *) malloc (len);
+ len = fread ((char *) font_data, 1, len, f);
+#endif
+
+ printf ("Opened font file %s: %d bytes long\n", argv[1], len);
+
+ const OpenTypeFontFile &ot = *CastP<OpenTypeFontFile> (font_data);
+
+ switch (ot.get_tag ()) {
+ case OpenTypeFontFile::TrueTypeTag:
+ printf ("OpenType font with TrueType outlines\n");
+ break;
+ case OpenTypeFontFile::CFFTag:
+ printf ("OpenType font with CFF (Type1) outlines\n");
+ break;
+ case OpenTypeFontFile::TTCTag:
+ printf ("TrueType Collection of OpenType fonts\n");
+ break;
+ case OpenTypeFontFile::TrueTag:
+ printf ("Obsolete Apple TrueType font\n");
+ break;
+ case OpenTypeFontFile::Typ1Tag:
+ printf ("Obsolete Apple Type1 font in SFNT container\n");
+ break;
+ default:
+ printf ("Unknown font format\n");
+ break;
+ }
+
+ int num_fonts = ot.get_face_count ();
+ printf ("%d font(s) found in file\n", num_fonts);
+ for (int n_font = 0; n_font < num_fonts; n_font++) {
+ const OpenTypeFontFace &font = ot.get_face (n_font);
+ printf ("Font %d of %d:\n", n_font, num_fonts);
+
+ int num_tables = font.get_table_count ();
+ printf (" %d table(s) found in font\n", num_tables);
+ for (int n_table = 0; n_table < num_tables; n_table++) {
+ const OpenTypeTable &table = font.get_table (n_table);
+ printf (" Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables,
+ (const char *)table.tag,
+ (unsigned int) table.offset,
+ (unsigned int) table.length);
+
+ switch (table.tag) {
+
+ case GSUBGPOS::GSUBTag:
+ case GSUBGPOS::GPOSTag:
+ {
+
+ const GSUBGPOS &g = *CastP<GSUBGPOS> (font_data + table.offset);
+
+ int num_scripts = g.get_script_count ();
+ printf (" %d script(s) found in table\n", num_scripts);
+ for (int n_script = 0; n_script < num_scripts; n_script++) {
+ const Script &script = g.get_script (n_script);
+ printf (" Script %2d of %2d: %.4s\n", n_script, num_scripts,
+ (const char *)g.get_script_tag(n_script));
+
+ if (!script.has_default_lang_sys())
+ printf (" No default language system\n");
+ int num_langsys = script.get_lang_sys_count ();
+ printf (" %d language system(s) found in script\n", num_langsys);
+ for (int n_langsys = script.has_default_lang_sys() ? -1 : 0; n_langsys < num_langsys; n_langsys++) {
+ const LangSys &langsys = n_langsys == -1
+ ? script.get_default_lang_sys ()
+ : script.get_lang_sys (n_langsys);
+ printf (n_langsys == -1
+ ? " Default Language System\n"
+ : " Language System %2d of %2d: %.4s\n", n_langsys, num_langsys,
+ (const char *)script.get_lang_sys_tag (n_langsys));
+ if (langsys.get_required_feature_index () == Index::NOT_FOUND_INDEX)
+ printf (" No required feature\n");
+
+ int num_features = langsys.get_feature_count ();
+ printf (" %d feature(s) found in language system\n", num_features);
+ for (int n_feature = 0; n_feature < num_features; n_feature++) {
+ printf (" Feature index %2d of %2d: %d\n", n_feature, num_features,
+ langsys.get_feature_index (n_feature));
+ }
+ }
+ }
+
+ int num_features = g.get_feature_count ();
+ printf (" %d feature(s) found in table\n", num_features);
+ for (int n_feature = 0; n_feature < num_features; n_feature++) {
+ const Feature &feature = g.get_feature (n_feature);
+ printf (" Feature %2d of %2d: %.4s; %d lookup(s)\n", n_feature, num_features,
+ (const char *)g.get_feature_tag(n_feature),
+ feature.get_lookup_count());
+
+ int num_lookups = feature.get_lookup_count ();
+ printf (" %d lookup(s) found in feature\n", num_lookups);
+ for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) {
+ printf (" Lookup index %2d of %2d: %d\n", n_lookup, num_lookups,
+ feature.get_lookup_index (n_lookup));
+ }
+ }
+
+ int num_lookups = g.get_lookup_count ();
+ printf (" %d lookup(s) found in table\n", num_lookups);
+ for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) {
+ const Lookup &lookup = g.get_lookup (n_lookup);
+ printf (" Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups,
+ lookup.get_type(), lookup.get_props());
+ }
+
+ }
+ break;
+
+ case GDEF::Tag:
+ {
+
+ const GDEF &gdef = *CastP<GDEF> (font_data + table.offset);
+
+ printf (" Has %sglyph classes\n",
+ gdef.has_glyph_classes () ? "" : "no ");
+ printf (" Has %smark attachment types\n",
+ gdef.has_mark_attachment_types () ? "" : "no ");
+ printf (" Has %sattach points\n",
+ gdef.has_attach_points () ? "" : "no ");
+ printf (" Has %slig carets\n",
+ gdef.has_lig_carets () ? "" : "no ");
+ printf (" Has %smark sets\n",
+ gdef.has_mark_sets () ? "" : "no ");
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+HB_END_DECLS
diff --git a/third_party/harfbuzz-ng/src/test.c b/third_party/harfbuzz-ng/src/test.c
new file mode 100644
index 0000000..836dd4c
--- /dev/null
+++ b/third_party/harfbuzz-ng/src/test.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hb.h"
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+HB_BEGIN_DECLS
+
+
+int
+main (int argc, char **argv)
+{
+ hb_blob_t *blob = NULL;
+ hb_face_t *face = NULL;
+
+ if (argc != 2) {
+ fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
+ exit (1);
+ }
+
+ /* Create the blob */
+ {
+ const char *font_data;
+ unsigned int len;
+ hb_destroy_func_t destroy;
+ void *user_data;
+
+#ifdef HAVE_GLIB
+ GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL);
+ font_data = g_mapped_file_get_contents (mf);
+ len = g_mapped_file_get_length (mf);
+ destroy = (hb_destroy_func_t) g_mapped_file_unref;
+ user_data = (void *) mf;
+#else
+ FILE *f = fopen (argv[1], "rb");
+ fseek (f, 0, SEEK_END);
+ len = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ font_data = (const char *) malloc (len);
+ if (!font_data) len = 0;
+ len = fread ((char *) font_data, 1, len, f);
+ destroy = free;
+ user_data = (void *) font_data;
+ fclose (f);
+#endif
+
+ blob = hb_blob_create (font_data, len,
+ HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
+ destroy, user_data);
+ }
+
+ /* Create the face */
+ face = hb_face_create_for_data (blob, 0 /* first face */);
+
+ /* So, what now? */
+
+ hb_face_destroy (face);
+ hb_blob_destroy (blob);
+
+ return 0;
+}