Upgrade to latest harfbuzz 2.0alpha master submit/master/20120920.151126
authorAnkush Dua <ankush.d@samsung.com>
Wed, 27 Jun 2012 06:27:25 +0000 (15:27 +0900)
committerAnkush Dua <ankush.d@samsung.com>
Wed, 27 Jun 2012 06:27:25 +0000 (15:27 +0900)
Change-Id: I9d121292a9dee634ebd0a542d1b290c898db09b7

142 files changed:
Makefile.am
TODO
autogen.sh
configure.ac
debian/changelog
debian/control
packaging/harfbuzz.spec
src/Makefile.am
src/check-c-linkage-decls.sh
src/check-header-guards.sh
src/check-internal-symbols.sh
src/check-libstdc++.sh
src/gen-arabic-table.py
src/gen-indic-table.py
src/hb-atomic-private.hh [new file with mode: 0644]
src/hb-blob.cc
src/hb-blob.h
src/hb-buffer-private.hh
src/hb-buffer.cc
src/hb-buffer.h
src/hb-cache-private.hh [new file with mode: 0644]
src/hb-common.cc
src/hb-common.h
src/hb-fallback-shape-private.hh [new file with mode: 0644]
src/hb-fallback-shape.cc [new file with mode: 0644]
src/hb-font-private.hh
src/hb-font.cc
src/hb-font.h
src/hb-ft.cc
src/hb-ft.h
src/hb-glib.cc
src/hb-glib.h
src/hb-gobject-enums.cc.tmpl [new file with mode: 0644]
src/hb-gobject-structs.cc [new file with mode: 0644]
src/hb-gobject.h [new file with mode: 0644]
src/hb-graphite2-private.hh [new file with mode: 0644]
src/hb-graphite2.cc [new file with mode: 0644]
src/hb-graphite2.h [new file with mode: 0644]
src/hb-icu.cc
src/hb-icu.h
src/hb-mutex-private.hh
src/hb-object-private.hh
src/hb-open-file-private.hh
src/hb-open-type-private.hh
src/hb-ot-head-table.hh [moved from src/hb-ot-head-private.hh with 95% similarity]
src/hb-ot-hhea-table.hh [new file with mode: 0644]
src/hb-ot-hmtx-table.hh [new file with mode: 0644]
src/hb-ot-layout-common-private.hh
src/hb-ot-layout-gdef-table.hh [moved from src/hb-ot-layout-gdef-private.hh with 89% similarity]
src/hb-ot-layout-gpos-table.hh [moved from src/hb-ot-layout-gpos-private.hh with 73% similarity]
src/hb-ot-layout-gsub-private.hh [deleted file]
src/hb-ot-layout-gsub-table.hh [new file with mode: 0644]
src/hb-ot-layout-gsubgpos-private.hh
src/hb-ot-layout-private.hh
src/hb-ot-layout.cc
src/hb-ot-layout.h
src/hb-ot-map-private.hh
src/hb-ot-map.cc
src/hb-ot-maxp-table.hh [moved from src/hb-ot-maxp-private.hh with 84% similarity]
src/hb-ot-name-table.hh [new file with mode: 0644]
src/hb-ot-shape-complex-arabic-table.hh
src/hb-ot-shape-complex-arabic.cc
src/hb-ot-shape-complex-indic-machine.hh [new file with mode: 0644]
src/hb-ot-shape-complex-indic-machine.rl
src/hb-ot-shape-complex-indic-private.hh [new file with mode: 0644]
src/hb-ot-shape-complex-indic-table.hh
src/hb-ot-shape-complex-indic.cc
src/hb-ot-shape-complex-misc.cc
src/hb-ot-shape-complex-private.hh
src/hb-ot-shape-normalize-private.hh [new file with mode: 0644]
src/hb-ot-shape-normalize.cc
src/hb-ot-shape-private.hh
src/hb-ot-shape.cc
src/hb-ot-tag.cc
src/hb-ot-tag.h
src/hb-ot.h
src/hb-private.hh
src/hb-set-private.hh [new file with mode: 0644]
src/hb-set.cc [new file with mode: 0644]
src/hb-set.h [new file with mode: 0644]
src/hb-shape.cc
src/hb-shape.h
src/hb-tt-font.cc [new file with mode: 0644]
src/hb-unicode-private.hh
src/hb-unicode.cc
src/hb-unicode.h
src/hb-uniscribe-private.hh [new file with mode: 0644]
src/hb-uniscribe.cc [new file with mode: 0644]
src/hb-uniscribe.h [moved from src/hb-ot-shape.h with 75% similarity]
src/hb-version.h [new file with mode: 0644]
src/hb-version.h.in
src/hb-view.cc [deleted file]
src/hb-warning.cc [new file with mode: 0644]
src/hb.h
src/indic.cc [new file with mode: 0644]
src/main.cc
src/test.cc [deleted file]
test/.valgrind-suppressions [deleted file]
test/Makefile.am
test/api/Makefile.am [new file with mode: 0644]
test/api/hb-test.h [moved from test/hb-test.h with 98% similarity]
test/api/test-blob.c [moved from test/test-blob.c with 100% similarity]
test/api/test-buffer.c [moved from test/test-buffer.c with 98% similarity]
test/api/test-c.c [moved from test/test-c.c with 87% similarity]
test/api/test-common.c [moved from test/test-common.c with 70% similarity]
test/api/test-cplusplus.cc [moved from test/test-cplusplus.cc with 100% similarity]
test/api/test-font.c [moved from test/test-font.c with 100% similarity]
test/api/test-object.c [moved from test/test-object.c with 93% similarity]
test/api/test-ot-tag.c [moved from test/test-ot-tag.c with 89% similarity]
test/api/test-shape-complex.c [moved from test/test-shape-complex.c with 100% similarity]
test/api/test-shape.c [moved from test/test-shape.c with 92% similarity]
test/api/test-unicode.c [moved from test/test-unicode.c with 99% similarity]
test/api/test-version.c [moved from test/test-version.c with 100% similarity]
test/shaping/Makefile.am [new file with mode: 0644]
test/shaping/Makefile.in [new file with mode: 0644]
test/shaping/hb-diff [new file with mode: 0755]
test/shaping/hb-diff-colorize [new file with mode: 0755]
test/shaping/hb-diff-filter-failures [new file with mode: 0755]
test/shaping/hb-diff-ngrams [new file with mode: 0755]
test/shaping/hb-diff-stat [new file with mode: 0755]
test/shaping/hb-manifest-read [new file with mode: 0755]
test/shaping/hb-manifest-update [new file with mode: 0755]
test/shaping/hb-unicode-decode [new file with mode: 0755]
test/shaping/hb-unicode-encode [new file with mode: 0755]
test/shaping/hb-unicode-prettyname [new file with mode: 0755]
test/shaping/hb_test_tools.py [new file with mode: 0644]
util/Makefile.am [new file with mode: 0644]
util/ansi-print.cc [new file with mode: 0644]
util/ansi-print.hh [new file with mode: 0644]
util/hb-ot-shape-closure.cc [new file with mode: 0644]
util/hb-shape.cc [new file with mode: 0644]
util/hb-view.cc [new file with mode: 0644]
util/helper-cairo-ansi.cc [new file with mode: 0644]
util/helper-cairo-ansi.hh [new file with mode: 0644]
util/helper-cairo.cc [new file with mode: 0644]
util/helper-cairo.hh [new file with mode: 0644]
util/main-font-text.hh [new file with mode: 0644]
util/options.cc [new file with mode: 0644]
util/options.hh [new file with mode: 0644]
util/shape-consumer.hh [new file with mode: 0644]
util/view-cairo.cc [new file with mode: 0644]
util/view-cairo.hh [new file with mode: 0644]

index bb68ede..a405dbf 100644 (file)
@@ -2,7 +2,7 @@
 
 NULL =
 
-SUBDIRS = src test
+SUBDIRS = src util test
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = harfbuzz.pc
diff --git a/TODO b/TODO
index 04852c6..4a1ad19 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,33 +1,72 @@
 General fixes:
 =============
 
-- Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS)
+- Warn at compile time (and runtime with HB_DEBUG?) if no Unicode / font
+  funcs found / set.
 
-- Do proper rounding when scaling from font space?
+- In hb_shape(), assert if direction is INVALID.
+
+- Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS).
+
+- Do proper rounding when scaling from font space?  May be a non-issue.
 
 - Misc features:
   * init/medi/fina/isol for non-cursive scripts
-  * vkna,hkna etc for kana, *jmo for hangul, etc
+  * vkna,hkna etc for kana, etc
+
+- Move non-native direction and normalization handling to the generic non-OT
+  layer, such that uniscribe and other backends can use.
+
+- Uniscribe backend needs to enforce one direction only, otherwise cluster
+  values can confuse the user.
+
+- GSUB ligation should call merge_clusters().  Also other places.
+
+- Convert NBSP into space glyph.
+
+- Synthetic GDEF.
+
+- Add Pango backend?
+
+- Add ICUlayout backend?
+
+- Add ICUlayout API?
+
+- Add Old HarfBuzz backend?
+
+- Add Old HarfBuzz API?
 
 
 API issues to fix before 1.0:
 ============================
 
+- Add default font_funcs / Unicode funcs API and to utils.
+
+- Add init_func to font_funcs.  Adjust ft.
+
 - Add pkg-config files for glue codes (harfbuzz-glib, etc)
 
 - Figure out how many .so objects, how to link, etc
 
+- 'const' for getter APIs? (use mutable internally)
+
+- blob_from_file?
+
+
+API additions
+=============
+
+- Buffer (de)serialize API ala hb-shape?
+
+- Move feature parsing from util into the library
+
 - Add hb-cairo glue
 
 - Add sanitize API (and a cached version, that saves result on blob user-data)
 
-- hb_shape() currently does a bit more than hb_ot_shape().  Shouldn't.
-
 - Add glib GBoxedType stuff and introspection
 
-
-API to add (maybe after 1.0):
-============================
+- Add Uniscribe face / font get API
 
 - BCP 47 language handling / API (language_matches?)
 
@@ -35,26 +74,23 @@ API to add (maybe after 1.0):
 
 - Add hb_font_create_linear()?
 
-- Add hb_shape_plan()/hb_shape_execute()
+- Add hb_shape_plan()/hb_shape_planned()
 
 - Add query API for aalt-like features?
 
 - SFNT api? get_num_faces? get_table_tags? (there's something in stash)
 
-- Full matrix instead of scale?
-
 - Add segmentation API
 
-- Add hb-fribidi?
+- Add hb-fribidi glue?
 
 
-hb-view enhancements:
-====================
+hb-view / hb-shape enhancements:
+===============================
 
-- Add --format
 - Add --width, --height, --auto-size, --align, etc?
-- Port to GOption, --help
-- Add XML and JSON formats
+- Add XML and JSON formats to hb-shape
+- --features="init=medi=isol=fina=0"
 
 
 Tests to write:
@@ -66,10 +102,18 @@ Tests to write:
 
 - Finish test-unicode.c, grep for TODO
 
+- GObject, FreeType, etc
+
+- hb_set_t
+
+- hb_cache_t and relatives
+
 
 Optimizations:
 =============
 
 - Avoid allocating blob objects internally for for_data() faces?
 
-- Add caching layer to hb-ft
+- Add caching layer to hb-ft?
+
+- Cache feature-less shape plans internally on the face.
index cdebe07..833a621 100755 (executable)
@@ -13,14 +13,20 @@ which ragel || {
        exit 1
 }
 
+echo -n "checking for pkg-config... "
+which pkg-config || {
+       echo "*** No pkg-config found, please install it ***"
+       exit 1
+}
+
 echo -n "checking for autoreconf... "
 which autoreconf || {
        echo "*** No autoreconf found, please install it ***"
        exit 1
 }
 
-echo "running autoreconf"
-autoreconf --force --install || exit $?
+echo "running autoreconf --force --install --verbose"
+autoreconf --force --install --verbose || exit $?
 
 cd $olddir
 echo "running configure $@"
index d2a2331..2fb058f 100644 (file)
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([harfbuzz],
-        [0.7.0],
+        [0.9.0],
         [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -11,15 +11,15 @@ AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([1.11.1 gnu dist-bzip2 no-dist-gzip -Wall no-define])
 AM_SILENT_RULES([yes])
 
+# Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT([disable-static])
+
 # Check for programs
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CXX
 
-# Initialize libtool
-LT_PREREQ([2.2])
-LT_INIT([disable-static])
-
 # Version
 m4_define(hb_version_triplet,m4_split(AC_PACKAGE_VERSION,[[.]]))
 m4_define(hb_version_major,m4_argn(1,hb_version_triplet))
@@ -49,9 +49,11 @@ m4_define([hb_libtool_current],
 HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age
 AC_SUBST(HB_LIBTOOL_VERSION_INFO)
 
+dnl GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
+
 # Functions and headers
-AC_CHECK_FUNCS(mprotect sysconf getpagesize mmap)
-AC_CHECK_HEADERS(unistd.h sys/mman.h)
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize sched_yield mmap _setmode isatty)
+AC_CHECK_HEADERS(unistd.h sys/mman.h sched.h io.h)
 
 # Compiler flags
 AC_CANONICAL_HOST
@@ -73,13 +75,34 @@ fi
 
 dnl ==========================================================================
 
+have_ot=true
+if $have_ot; then
+       AC_DEFINE(HAVE_OT, 1, [Have native OpenType Layout backend])
+fi
+AM_CONDITIONAL(HAVE_OT, $have_ot)
+
+dnl ===========================================================================
+
 PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, have_glib=true, have_glib=false)
-PKG_CHECK_MODULES(GTHREAD, gthread-2.0, , have_glib=false)
 if $have_glib; then
        AC_DEFINE(HAVE_GLIB, 1, [Have glib2 library])
 fi
 AM_CONDITIONAL(HAVE_GLIB, $have_glib)
 
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0, have_gthread=true, have_gthread=false)
+if $have_gthread; then
+       AC_DEFINE(HAVE_GTHREAD, 1, [Have gthread2 library])
+fi
+AM_CONDITIONAL(HAVE_GTHREAD, $have_gthread)
+
+PKG_CHECK_MODULES(GOBJECT, gobject-2.0 glib-2.0 >= 2.16, have_gobject=true, have_gobject=false)
+if $have_gobject; then
+       AC_DEFINE(HAVE_GOBJECT, 1, [Have gobject2 library])
+       GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
+       AC_SUBST(GLIB_MKENUMS)
+fi
+AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject)
+
 dnl ==========================================================================
 
 PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, have_cairo=false)
@@ -88,12 +111,6 @@ if $have_cairo; then
 fi
 AM_CONDITIONAL(HAVE_CAIRO, $have_cairo)
 
-PKG_CHECK_MODULES(CAIRO_PNG, cairo-png, have_cairo_png=true, have_cairo_png=false)
-if $have_cairo_png; then
-       AC_DEFINE(HAVE_CAIRO_PNG, 1, [Have cairo-png support in cairo graphics library])
-fi
-AM_CONDITIONAL(HAVE_CAIRO_PNG, $have_cairo_png)
-
 PKG_CHECK_MODULES(CAIRO_FT, cairo-ft, have_cairo_ft=true, have_cairo_ft=false)
 if $have_cairo_ft; then
        AC_DEFINE(HAVE_CAIRO_FT, 1, [Have cairo-ft support in cairo graphics library])
@@ -103,12 +120,21 @@ AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft)
 dnl ==========================================================================
 
 PKG_CHECK_MODULES(ICU, icu, have_icu=true, [
-       AC_CHECK_PROG([have_icu], [icu-config], [true], [false])
+       have_icu=true
+       AC_CHECK_HEADERS(unicode/uchar.h,, have_icu=false)
+       AC_MSG_CHECKING([for libicuuc])
+       LIBS_old=$LIBS
+       LIBS="$LIBS -licuuc"
+       AC_TRY_LINK([#include <unicode/uchar.h>],
+                   [u_getIntPropertyValue (0, (UProperty)0);],
+                   AC_MSG_RESULT(yes),
+                   AC_MSG_RESULT(no);have_icu=false)
+       LIBS=$LIBS_old
        if $have_icu; then
-               icu_cflags=`icu-config --cppflags`
-               icu_libs=`icu-config --ldflags-libsonly`
-               AC_SUBST(ICU_CFLAGS, [$icu_cflags])
-               AC_SUBST(ICU_LIBS, [$icu_libs])
+               ICU_CFLAGS=-D_REENTRANT
+               ICU_LIBS="-licuuc"
+               AC_SUBST(ICU_CFLAGS)
+               AC_SUBST(ICU_LIBS)
        fi
 ])
 if $have_icu; then
@@ -118,7 +144,15 @@ AM_CONDITIONAL(HAVE_ICU, $have_icu)
 
 dnl ==========================================================================
 
-PKG_CHECK_MODULES(FREETYPE, freetype2, have_freetype=true, have_freetype=false)
+PKG_CHECK_MODULES(GRAPHITE2, graphite2, have_graphite=true, have_graphite=false)
+if $have_graphite; then
+    AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite library])
+fi
+AM_CONDITIONAL(HAVE_GRAPHITE2, $have_graphite)
+
+dnl ==========================================================================
+
+PKG_CHECK_MODULES(FREETYPE, freetype2 >= 2.3.8, have_freetype=true, have_freetype=false)
 if $have_freetype; then
        AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
        _save_libs="$LIBS"
@@ -133,12 +167,42 @@ AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
 
 dnl ===========================================================================
 
+AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true, have_uniscribe=false)
+if $have_uniscribe; then
+       UNISCRIBE_CFLAGS=
+       UNISCRIBE_LIBS="-lusp10 -lgdi32"
+       AC_SUBST(UNISCRIBE_CFLAGS)
+       AC_SUBST(UNISCRIBE_LIBS)
+       AC_DEFINE(HAVE_UNISCRIBE, 1, [Have Uniscribe backend])
+fi
+AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe)
+
+dnl ===========================================================================
+
+AC_CACHE_CHECK([for Intel atomic primitives], hb_cv_have_intel_atomic_primitives, [
+       hb_cv_have_intel_atomic_primitives=false
+       AC_TRY_LINK([], [
+               void memory_barrier (void) { __sync_synchronize (); }
+               int atomic_add (int i) { return __sync_fetch_and_add (&i, 1); }
+               int atomic_cmpxchg (int *i, int *j, int *k) { return __sync_bool_compare_and_swap (&i, j, k); }
+               ], hb_cv_have_intel_atomic_primitives=true
+       )
+])
+if $hb_cv_have_intel_atomic_primitives; then
+       AC_DEFINE(HAVE_INTEL_ATOMIC_PRIMITIVES, 1, [Have Intel __sync_* atomic primitives])
+fi
+
+dnl ===========================================================================
+
 AC_CONFIG_FILES([
 Makefile
 harfbuzz.pc
 src/Makefile
 src/hb-version.h
+util/Makefile
 test/Makefile
+test/api/Makefile
+test/shaping/Makefile
 ])
 
 AC_OUTPUT
index 34e1d09..f60ddf7 100755 (executable)
@@ -1,3 +1,11 @@
+harfbuzz (0.9.0-1slp2+1) unstable; urgency=low
+
+  * Upgrade to latest harfbuzz
+  * Git: 165.213.180.234:slp/pkgs/h/harfbuzz
+  * Tag: harfbuzz_0.9.1-1slp2+1
+
+ -- Ankush Dua <ankush.d@samsung.com>  Wed, 27 June 2012 15:00:08 +0900
+
 harfbuzz (0.7.0-1slp2+1) unstable; urgency=low
 
   * Upgrade to latest harfbuzz
index 6c61eff..065539e 100755 (executable)
@@ -1,7 +1,7 @@
 Source: harfbuzz
 Section: libs
 Priority: optional
-Maintainer: janani <janani.k@samsung.com>, Kesavardhana Gandla <kesav.gandla@samsung.com>
+Maintainer: janani <janani.k@samsung.com>, Ankush Dua <ankush.d@samsung.com>
 Build-Depends: debhelper (>= 5),libfreetype6-dev, pkg-config, ragel
 Standards-Version: 3.7.2
 
index 076b4a4..cda2d79 100644 (file)
@@ -1,6 +1,6 @@
 Name:           harfbuzz
 Summary:        Hindi Reshaping Library
-Version:        0.7.0
+Version:        0.9.0
 Release:        1
 Group:          TO_BE/FILLED_IN
 License:        TO BE FILLED IN
index b812419..344cc57 100644 (file)
@@ -1,6 +1,7 @@
 # Process this file with automake to produce Makefile.in
 
 NULL =
+BUILT_SOURCES =
 EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
@@ -14,22 +15,34 @@ lib_LTLIBRARIES = libharfbuzz.la
 HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
+       hb-atomic-private.hh \
        hb-blob.cc \
        hb-buffer-private.hh \
        hb-buffer.cc \
+       hb-cache-private.hh \
        hb-common.cc \
+       hb-fallback-shape-private.hh \
+       hb-fallback-shape.cc \
        hb-font-private.hh \
        hb-font.cc \
        hb-mutex-private.hh \
        hb-object-private.hh \
        hb-open-file-private.hh \
        hb-open-type-private.hh \
-       hb-ot-head-private.hh \
-       hb-ot-maxp-private.hh \
+       hb-ot-head-table.hh \
+       hb-ot-hhea-table.hh \
+       hb-ot-hmtx-table.hh \
+       hb-ot-maxp-table.hh \
+       hb-ot-name-table.hh \
+       hb-ot-tag.cc \
        hb-private.hh \
+       hb-set-private.hh \
+       hb-set.cc \
        hb-shape.cc \
+       hb-tt-font.cc \
        hb-unicode-private.hh \
        hb-unicode.cc \
+       hb-warning.cc \
        $(NULL)
 HBHEADERS = \
        hb.h \
@@ -37,18 +50,20 @@ HBHEADERS = \
        hb-buffer.h \
        hb-common.h \
        hb-font.h \
+       hb-set.h \
        hb-shape.h \
        hb-unicode.h \
        hb-version.h \
        $(NULL)
 
+if HAVE_OT
 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-gdef-table.hh \
+       hb-ot-layout-gpos-table.hh \
        hb-ot-layout-gsubgpos-private.hh \
-       hb-ot-layout-gsub-private.hh \
+       hb-ot-layout-gsub-table.hh \
        hb-ot-layout-private.hh \
        hb-ot-map.cc \
        hb-ot-map-private.hh \
@@ -57,59 +72,75 @@ HBSOURCES += \
        hb-ot-shape-complex-arabic-table.hh \
        hb-ot-shape-complex-indic.cc \
        hb-ot-shape-complex-indic-machine.hh \
+       hb-ot-shape-complex-indic-private.hh \
        hb-ot-shape-complex-indic-table.hh \
        hb-ot-shape-complex-misc.cc \
        hb-ot-shape-complex-private.hh \
+       hb-ot-shape-normalize-private.hh \
        hb-ot-shape-normalize.cc \
        hb-ot-shape-private.hh \
-       hb-ot-tag.cc \
        $(NULL)
 HBHEADERS += \
        hb-ot.h \
        hb-ot-layout.h \
-       hb-ot-shape.h \
        hb-ot-tag.h \
        $(NULL)
-
-MAINTAINERCLEANFILES += \
-       $(srcdir)/hb-version.h \
-       $(NULL)
+endif
 
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
-HBSOURCES += \
-       hb-glib.cc \
-       $(NULL)
-HBHEADERS += \
-       hb-glib.h \
-       $(NULL)
+HBSOURCES += hb-glib.cc
+HBHEADERS += hb-glib.h
+endif
+
+if HAVE_GOBJECT
+HBCFLAGS += $(GOBJECT_CFLAGS)
+HBLIBS   += $(GOBJECT_LIBS)
+HBSOURCES += hb-gobject-structs.cc
+nodist_HBSOURCES = hb-gobject-enums.cc
+HBHEADERS += hb-gobject.h
+BUILT_SOURCES += hb-gobject-enums.cc
+EXTRA_DIST += hb-gobject-enums.cc.tmpl
+DISTCLEANFILES += hb-gobject-enums.cc
+
+hb-gobject-enums.cc: hb-gobject-enums.cc.tmpl $(HBHEADERS)
+       $(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > "$@.tmp" && \
+       mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false )
 endif
 
 if HAVE_ICU
 HBCFLAGS += $(ICU_CFLAGS)
 HBLIBS   += $(ICU_LIBS)
-HBSOURCES += \
-       hb-icu.cc \
-       $(NULL)
-HBHEADERS += \
-       hb-icu.h \
-       $(NULL)
+HBSOURCES += hb-icu.cc
+HBHEADERS += hb-icu.h
 endif
 
 if HAVE_FREETYPE
 HBCFLAGS += $(FREETYPE_CFLAGS)
 HBLIBS   += $(FREETYPE_LIBS)
-HBSOURCES += \
-       hb-ft.cc \
-       $(NULL)
-HBHEADERS += \
-       hb-ft.h \
-       $(NULL)
+HBSOURCES += hb-ft.cc
+HBHEADERS += hb-ft.h
 endif
 
-CXXLINK = $(LINK)
+if HAVE_GRAPHITE2
+HBCFLAGS += $(GRAPHITE2_CFLAGS)
+HBLIBS   += $(GRAPHITE2_LIBS)
+HBSOURCES += hb-graphite2.cc hb-graphite2-private.hh
+HBHEADERS += hb-graphite2.h
+endif
+
+if HAVE_UNISCRIBE
+HBCFLAGS += $(UNISCRIBE_CFLAGS)
+HBLIBS   += $(UNISCRIBE_LIBS)
+HBSOURCES += hb-uniscribe.cc hb-uniscribe-private.hh
+HBHEADERS += hb-uniscribe.h
+endif
+
+# Use a C linker, not C++; Don't link to libstdc++
+libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
+nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
 libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
 libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
 libharfbuzz_la_LIBADD = $(HBLIBS)
@@ -124,35 +155,37 @@ GENERATORS = \
 
 EXTRA_DIST += $(GENERATORS)
 
-BUILT_SOURCES = hb-ot-shape-complex-indic-machine.hh
+unicode-tables: arabic-table indic-table
+
+indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
+       $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.hh.tmp && \
+       mv hb-ot-shape-complex-indic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-indic-table.hh || \
+       ($(RM) hb-ot-shape-complex-indic-table.hh.tmp; false)
+
+arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt
+       $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \
+       mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \
+       ($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false)
+
+
+.PHONY: unicode-tables arabic-table indic-table
+
+BUILT_SOURCES += hb-ot-shape-complex-indic-machine.hh
 EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl
 hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
        $(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
-       mv "$@.tmp" "$@"
+       mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
-noinst_PROGRAMS = main test
+noinst_PROGRAMS = main indic
 bin_PROGRAMS =
 
-if HAVE_GLIB
-if HAVE_FREETYPE
-if HAVE_CAIRO_FT
-if HAVE_CAIRO_PNG
-hb_view_SOURCES = hb-view.cc
-hb_view_CPPFLAGS = $(HBCFLAGS) $(CAIRO_FT_CFLAGS) $(CAIRO_PNG_CFLAGS)
-hb_view_LDADD = libharfbuzz.la -lm $(HBLIBS) $(CAIRO_FT_LIBS) $(CAIRO_PNG_LIBS)
-bin_PROGRAMS += hb-view
-endif
-endif
-endif
-endif
-
 main_SOURCES = main.cc
 main_CPPFLAGS = $(HBCFLAGS)
 main_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_SOURCES = test.cc
-test_CPPFLAGS = $(HBCFLAGS)
-test_LDADD = libharfbuzz.la $(HBLIBS)
+indic_SOURCES = indic.cc
+indic_CPPFLAGS = $(HBCFLAGS)
+indic_LDADD = libharfbuzz.la $(HBLIBS)
 
 dist_check_SCRIPTS = \
        check-c-linkage-decls.sh \
@@ -174,4 +207,8 @@ TESTS_ENVIRONMENT = \
        HBHEADERS="$(HBHEADERS)" \
        $(NULL)
 
+scan:
+       g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library libharfbuzz.la
+
+
 -include $(top_srcdir)/git.mk
index 84e77cf..e7c95ab 100755 (executable)
@@ -7,12 +7,10 @@ test -z "$srcdir" && srcdir=.
 stat=0
 
 test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
 
 
-cd "$srcdir"
-
-for x in $HBHEADERS $HBSOURCES; do
+for x in $HBHEADERS; do
+       test -f $srcdir/$x && x=$srcdir/$x
        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
index ddbcd83..af9fa7f 100755 (executable)
@@ -10,13 +10,12 @@ test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
 test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
 
 
-cd "$srcdir"
-
 for x in $HBHEADERS $HBSOURCES; do
+       test -f "$srcdir/$x" && x="$srcdir/$x"
        echo "$x" | grep '[^h]$' -q && continue;
-       x=`echo "$x" | sed 's@.*/@@'`
-       tag=`echo "$x" | tr 'a-z.-' 'A-Z_'`
-       lines=`grep "\<$tag\>" "$x" | wc -l`
+       xx=`echo "$x" | sed 's@.*/@@'`
+       tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'`
+       lines=`grep "\<$tag\>" "$x" | wc -l | sed 's/[  ]*//g'`
        if test "x$lines" != x3; then
                echo "Ouch, header file $x does not have correct preprocessor guards"
                stat=1
index 2885fa4..a24a693 100755 (executable)
@@ -11,18 +11,24 @@ if which nm 2>/dev/null >/dev/null; then
        :
 else
        echo "check-internal-symbols.sh: 'nm' not found; skipping test"
-       exit 0
+       exit 77
 fi
 
-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
+tested=false
+for suffix in so; do
+       so=.libs/libharfbuzz.$suffix
+       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
+               tested=true
        fi
-else
-       echo "check-internal-symbols.sh: libharfbuzz.so not found; skipping test"
+done
+if ! $tested; then
+       echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test"
+       exit 77
 fi
 
 exit $stat
index 40e73b0..0521532 100755 (executable)
@@ -11,18 +11,24 @@ if which ldd 2>/dev/null >/dev/null; then
        :
 else
        echo "check-libstdc++.sh: 'ldd' not found; skipping test"
-       exit 0
+       exit 77
 fi
 
-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
+tested=false
+for suffix in so dylib; do
+       so=.libs/libharfbuzz.$suffix
+       if test -f "$so"; then
+               echo "Checking that we are not linking to libstdc++"
+               if ldd $so | grep 'libstdc[+][+]'; then
+                       echo "Ouch, linked to libstdc++"
+                       stat=1
+               fi
+               tested=true
        fi
-else
-       echo "check-libstdc++.sh: libharfbuzz.so not found; skipping test"
+done
+if ! $tested; then
+       echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test"
+       exit 77
 fi
 
 exit $stat
index 10fb22d..2d3c881 100755 (executable)
 #!/usr/bin/python
 
 import sys
+import os.path
 
-if len (sys.argv) < 2:
-       print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt"
+if len (sys.argv) != 3:
+       print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt"
        sys.exit (1)
 
-f = file (sys.argv[1])
+files = [file (x) for x in sys.argv[1:]]
 
-header = f.readline (), f.readline ()
-while f.readline ().find ('##################') < 0:
+headers = [[files[0].readline (), files[0].readline ()]]
+headers.append (["UnicodeData.txt does not have a header."])
+while files[0].readline ().find ('##################') < 0:
        pass
 
 
+def print_joining_table(f):
+
+       print
+       print "static const uint8_t joining_table[] ="
+       print "{"
+
+       min_u = 0x110000
+       max_u = 0
+       num = 0
+       last = -1
+       block = ''
+       for line in f:
+
+               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 "};"
+       print
+       print "#define JOINING_TABLE_FIRST      0x%04X" % min_u
+       print "#define JOINING_TABLE_LAST       0x%04X" % max_u
+       print
+
+       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)
+
+def print_shaping_table(f):
+
+       shapes = {}
+       ligatures = {}
+       names = {}
+       for line in f:
+
+               fields = [x.strip () for x in line.split (';')]
+               if fields[5][0:1] != '<':
+                       continue
+
+               items = fields[5].split (' ')
+               shape, items = items[0][1:-1], tuple (int (x, 16) for x in items[1:])
+
+               if not shape in ['initial', 'medial', 'isolated', 'final']:
+                       continue
+
+               c = int (fields[0], 16)
+               if len (items) != 1:
+                       # We only care about lam-alef ligatures
+                       if len (items) != 2 or items[0] != 0x0644 or items[1] not in [0x0622, 0x0623, 0x0625, 0x0627]:
+                               continue
+
+                       # Save ligature
+                       names[c] = fields[1]
+                       if items not in ligatures:
+                               ligatures[items] = {}
+                       ligatures[items][shape] = c
+                       pass
+               else:
+                       # Save shape
+                       if items[0] not in names:
+                               names[items[0]] = fields[1]
+                       else:
+                               names[items[0]] = os.path.commonprefix ([names[items[0]], fields[1]]).strip ()
+                       if items[0] not in shapes:
+                               shapes[items[0]] = {}
+                       shapes[items[0]][shape] = c
+
+       print
+       print "static const uint16_t shaping_table[][4] ="
+       print "{"
+
+       keys = shapes.keys ()
+       min_u, max_u = min (keys), max (keys)
+       for u in range (min_u, max_u + 1):
+               s = [shapes[u][shape] if u in shapes and shape in shapes[u] else u
+                    for shape in  ['initial', 'medial', 'final', 'isolated']]
+               value = ', '.join ("0x%04X" % c for c in s)
+               print "  {%s}, /* U+%04X %s */" % (value, u, names[u] if u in names else "")
+
+       print "};"
+       print
+       print "#define SHAPING_TABLE_FIRST      0x%04X" % min_u
+       print "#define SHAPING_TABLE_LAST       0x%04X" % max_u
+       print
+
+       ligas = {}
+       for pair in ligatures.keys ():
+               for shape in ligatures[pair]:
+                       c = ligatures[pair][shape]
+                       if shape == 'isolated':
+                               liga = (shapes[pair[0]]['initial'], shapes[pair[1]]['final'])
+                       elif shape == 'final':
+                               liga = (shapes[pair[0]]['medial'], shapes[pair[1]]['final'])
+                       else:
+                               raise Exception ("Unexpected shape", shape)
+                       if liga[0] not in ligas:
+                               ligas[liga[0]] = []
+                       ligas[liga[0]].append ((liga[1], c))
+       max_i = max (len (ligas[l]) for l in ligas)
+       print
+       print "static const struct {"
+       print " uint16_t first;"
+       print " struct {"
+       print "   uint16_t second;"
+       print "   uint16_t ligature;"
+       print " } ligatures[%d];" % max_i
+       print "} ligature_table[] ="
+       print "{"
+       keys = ligas.keys ()
+       keys.sort ()
+       for first in keys:
+
+               print "  { 0x%04X, {" % (first)
+               for liga in ligas[first]:
+                       print "    { 0x%04X, 0x%04X }, /* %s */" % (liga[0], liga[1], names[liga[1]])
+               print "  }},"
+
+       print "};"
+       print
+
+
+
 print "/* == Start of generated table == */"
 print "/*"
 print " * The following table is generated by running:"
 print " *"
-print " *   ./gen-arabic-table.py ArabicShaping.txt"
+print " *   ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt"
 print " *"
 print " * on files with these headers:"
 print " *"
-for line in header:
-       print " * %s" % (line.strip())
+for h in headers:
+       for l in h:
+               print " * %s" % (l.strip())
 print " */"
-
-print "static const uint8_t joining_table[] ="
-print "{"
-
-
-min_u = 0x110000
-max_u = 0
-num = 0
-last = -1
-block = ''
-for line in f:
-       
-       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 "};"
+print "#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH"
+print "#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH"
 print
 
-print "#define JOINING_TABLE_FIRST     0x%04X" % min_u
-print "#define JOINING_TABLE_LAST      0x%04X" % max_u
-print
+print_joining_table (files[0])
+print_shaping_table (files[1])
 
+print
+print "#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */"
+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)
index f4fdb83..94aa2ab 100755 (executable)
@@ -2,11 +2,11 @@
 
 import sys
 
-if len (sys.argv) < 4:
+if len (sys.argv) != 4:
        print >>sys.stderr, "usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt"
        sys.exit (1)
 
-files = [file (sys.argv[i+1]) for i in range (3)]
+files = [file (x) for x in sys.argv[1:]]
 
 headers = [[f.readline () for i in range (2)] for f in files]
 
@@ -19,7 +19,7 @@ for i, f in enumerate (files):
                j = line.find ('#')
                if j >= 0:
                        line = line[:j]
-               
+
                fields = [x.strip () for x in line.split (';')]
                if len (fields) == 1:
                        continue
@@ -74,9 +74,12 @@ for h in headers:
        for l in h:
                print " * %s" % (l.strip())
 print " */"
+print
+print "#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH"
+print "#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH"
+print
 
 # Shorten values
-print
 short = [{
        "Bindu":                'Bi',
        "Visarga":              'Vs',
@@ -120,6 +123,8 @@ print "#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)"
 print
 print
 
+total = 0
+used = 0
 def print_block (block, start, end, data):
        print
        print
@@ -134,14 +139,9 @@ def print_block (block, start, end, data):
                d = data.get (u, defaults)
                sys.stdout.write ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]])))
 
-       if num == 0:
-               # Filler block, don't check occupancy
-               return
-       total = end - start + 1
-       occupancy = num * 100. / total
-       # Maintain at least 30% occupancy in the table */
-       if occupancy < 30:
-               raise Exception ("Table too sparse, please investigate: ", occupancy, block)
+       global total, used
+       total += end - start + 1
+       used += num
 
 uu = data.keys ()
 uu.sort ()
@@ -179,8 +179,8 @@ print
 print
 print "#define indic_offset_total %d" % offset
 print
-print "};"
-
+occupancy = used * 100. / total
+print "}; /* Table occupancy: %d%% */" % occupancy
 print
 print "static INDIC_TABLE_ELEMENT_TYPE"
 print "get_indic_categories (hb_codepoint_t u)"
@@ -192,7 +192,6 @@ for u,d in singles.items ():
        print "  if (unlikely (u == 0x%04X)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])
 print "  return _(x,x);"
 print "}"
-
 print
 print "#undef _"
 for i in range (2):
@@ -202,7 +201,11 @@ for i in range (2):
        for v in vv:
                print "#undef %s_%s" % \
                        (what_short[i], short[i][v])
-
 print
+print "#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */"
 print
 print "/* == End of generated table == */"
+
+# Maintain at least 30% occupancy in the table */
+if occupancy < 30:
+       raise Exception ("Table too sparse, please investigate: ", occupancy)
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh
new file mode 100644 (file)
index 0000000..c543a8d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2007  Chris Wilson
+ * Copyright © 2009,2010  Red Hat, Inc.
+ * Copyright © 2011,2012  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.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ATOMIC_PRIVATE_HH
+#define HB_ATOMIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* atomic_int */
+
+/* We need external help for these */
+
+#if 0
+
+
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
+
+#include <intrin.h>
+#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchangePointer)
+
+typedef long hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)       _InterlockedExchangeAdd (&(AI), (V))
+
+#define hb_atomic_ptr_get(P)           (MemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)   (_InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
+
+
+#elif !defined(HB_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+typedef int32_t hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)       (OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+
+#define hb_atomic_ptr_get(P)           (OSMemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)   OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) && !defined(__MINGW32__)
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)       __sync_fetch_and_add (&(AI), (V))
+
+#define hb_atomic_ptr_get(P)           (void *) (__sync_synchronize (), *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)   __sync_bool_compare_and_swap ((P), (O), (N))
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
+typedef int hb_atomic_int_t;
+#if GLIB_CHECK_VERSION(2,29,5)
+#define hb_atomic_int_add(AI, V)       g_atomic_int_add (&(AI), (V))
+#else
+#define hb_atomic_int_add(AI, V)       g_atomic_int_exchange_and_add (&(AI), (V))
+#endif
+
+#define hb_atomic_ptr_get(P)           g_atomic_pointer_get (P)
+#define hb_atomic_ptr_cmpexch(P,O,N)   g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (N))
+
+
+#elif !defined(HB_NO_MT)
+
+#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
+typedef volatile int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)       (((AI) += (V)) - (V))
+
+#define hb_atomic_ptr_get(P)           ((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)   (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
+
+
+#else /* HB_NO_MT */
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)       (((AI) += (V)) - (V))
+
+#define hb_atomic_ptr_get(P)           ((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)   (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
+
+#endif
+
+/* TODO Add tracing. */
+
+#endif /* HB_ATOMIC_PRIVATE_HH */
index 2d0c016..3cc2d9d 100644 (file)
@@ -39,7 +39,6 @@
 #include <stdio.h>
 #include <errno.h>
 
-HB_BEGIN_DECLS
 
 
 #ifndef HB_DEBUG_BLOB
@@ -49,6 +48,7 @@ HB_BEGIN_DECLS
 
 struct _hb_blob_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   bool immutable;
 
@@ -60,19 +60,6 @@ struct _hb_blob_t {
   hb_destroy_func_t destroy;
 };
 
-static hb_blob_t _hb_blob_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  TRUE, /* immutable */
-
-  0, /* length */
-  NULL, /* data */
-  HB_MEMORY_MODE_READONLY, /* mode */
-
-  NULL, /* user_data */
-  NULL  /* destroy */
-};
-
 
 static bool _try_writable (hb_blob_t *blob);
 
@@ -98,7 +85,7 @@ hb_blob_create (const char        *data,
   if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
     if (destroy)
       destroy (user_data);
-    return &_hb_blob_nil;
+    return hb_blob_get_empty ();
   }
 
   blob->data = data;
@@ -112,7 +99,7 @@ hb_blob_create (const char        *data,
     blob->mode = HB_MEMORY_MODE_READONLY;
     if (!_try_writable (blob)) {
       hb_blob_destroy (blob);
-      return &_hb_blob_nil;
+      return hb_blob_get_empty ();
     }
   }
 
@@ -127,7 +114,7 @@ hb_blob_create_sub_blob (hb_blob_t    *parent,
   hb_blob_t *blob;
 
   if (!length || offset >= parent->length)
-    return &_hb_blob_nil;
+    return hb_blob_get_empty ();
 
   hb_blob_make_immutable (parent);
 
@@ -143,7 +130,20 @@ hb_blob_create_sub_blob (hb_blob_t    *parent,
 hb_blob_t *
 hb_blob_get_empty (void)
 {
-  return &_hb_blob_nil;
+  static const hb_blob_t _hb_blob_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    NULL, /* data */
+    0, /* length */
+    HB_MEMORY_MODE_READONLY, /* mode */
+
+    NULL, /* user_data */
+    NULL  /* destroy */
+  };
+
+  return const_cast<hb_blob_t *> (&_hb_blob_nil);
 }
 
 hb_blob_t *
@@ -166,9 +166,10 @@ hb_bool_t
 hb_blob_set_user_data (hb_blob_t          *blob,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy)
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace)
 {
-  return hb_object_set_user_data (blob, key, data, destroy);
+  return hb_object_set_user_data (blob, key, data, destroy, replace);
 }
 
 void *
@@ -185,7 +186,7 @@ hb_blob_make_immutable (hb_blob_t *blob)
   if (hb_object_is_inert (blob))
     return;
 
-  blob->immutable = TRUE;
+  blob->immutable = true;
 }
 
 hb_bool_t
@@ -244,7 +245,7 @@ _try_make_writable_inplace_unix (hb_blob_t *blob)
 
   if ((uintptr_t) -1L == pagesize) {
     DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
-    return FALSE;
+    return false;
   }
   DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
 
@@ -256,7 +257,7 @@ _try_make_writable_inplace_unix (hb_blob_t *blob)
                  addr, addr+length, (unsigned long) length);
   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
     DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
-    return FALSE;
+    return false;
   }
 
   blob->mode = HB_MEMORY_MODE_WRITABLE;
@@ -264,9 +265,9 @@ _try_make_writable_inplace_unix (hb_blob_t *blob)
   DEBUG_MSG_FUNC (BLOB, blob,
                  "successfully made [%p..%p] (%lu bytes) writable\n",
                  addr, addr+length, (unsigned long) length);
-  return TRUE;
+  return true;
 #else
-  return FALSE;
+  return false;
 #endif
 }
 
@@ -276,38 +277,38 @@ _try_writable_inplace (hb_blob_t *blob)
   DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
 
   if (_try_make_writable_inplace_unix (blob))
-    return TRUE;
+    return true;
 
   DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
 
   /* Failed to make writable inplace, mark that */
   blob->mode = HB_MEMORY_MODE_READONLY;
-  return FALSE;
+  return false;
 }
 
 static bool
 _try_writable (hb_blob_t *blob)
 {
   if (blob->immutable)
-    return FALSE;
+    return false;
 
   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
-    return TRUE;
+    return true;
 
   if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
-    return TRUE;
+    return true;
 
   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
-    return TRUE;
+    return true;
 
 
-  DEBUG_MSG_FUNC (BLOB, blob, "currect data is -> %p\n", blob->data);
+  DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
 
   char *new_data;
 
   new_data = (char *) malloc (blob->length);
   if (unlikely (!new_data))
-    return FALSE;
+    return false;
 
   DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
 
@@ -318,8 +319,7 @@ _try_writable (hb_blob_t *blob)
   blob->user_data = new_data;
   blob->destroy = free;
 
-  return TRUE;
+  return true;
 }
 
 
-HB_END_DECLS
index f2eaae9..360310b 100644 (file)
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_BLOB_H
 #define HB_BLOB_H
 
@@ -66,7 +70,8 @@ hb_bool_t
 hb_blob_set_user_data (hb_blob_t          *blob,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy);
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace);
 
 
 void *
index 8f2095a..b539f26 100644 (file)
@@ -35,7 +35,6 @@
 #include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
-HB_BEGIN_DECLS
 
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
@@ -45,11 +44,13 @@ typedef struct _hb_segment_properties_t {
     hb_direction_t      direction;
     hb_script_t         script;
     hb_language_t       language;
+    ASSERT_POD ();
 } hb_segment_properties_t;
 
 
 struct _hb_buffer_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
 
@@ -71,6 +72,15 @@ struct _hb_buffer_t {
   hb_glyph_info_t     *out_info;
   hb_glyph_position_t *pos;
 
+  inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
+
+  inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
+
+  inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; }
+  inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; }
+
   unsigned int serial;
   uint8_t allocated_var_bytes[8];
   const char *allocated_var_owner[8];
@@ -95,6 +105,7 @@ struct _hb_buffer_t {
   HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
   HB_INTERNAL void reverse (void);
   HB_INTERNAL void reverse_clusters (void);
+  HB_INTERNAL void guess_properties (void);
 
   HB_INTERNAL void swap_buffers (void);
   HB_INTERNAL void clear_output (void);
@@ -102,6 +113,9 @@ struct _hb_buffer_t {
   HB_INTERNAL void replace_glyphs_be16 (unsigned int num_in,
                                        unsigned int num_out,
                                        const uint16_t *glyph_data_be);
+  HB_INTERNAL void replace_glyphs (unsigned int num_in,
+                                  unsigned int num_out,
+                                  const hb_codepoint_t *glyph_data);
   HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index);
   /* Makes a copy of the glyph at idx to output and replace glyph_index */
   HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index);
@@ -128,13 +142,20 @@ struct _hb_buffer_t {
                              unsigned int cluster_start,
                              unsigned int cluster_end);
 
+  HB_INTERNAL void merge_clusters (unsigned int start,
+                                  unsigned int end);
+  HB_INTERNAL void merge_out_clusters (unsigned int start,
+                                      unsigned int end);
+
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
-  { return likely (size <= allocated) ? TRUE : enlarge (size); }
+  { return likely (size <= allocated) ? true : enlarge (size); }
 
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
+
+  HB_INTERNAL void *get_scratch_buffer (unsigned int *size);
 };
 
 
@@ -147,6 +168,5 @@ struct _hb_buffer_t {
        HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var)
 
 
-HB_END_DECLS
 
 #endif /* HB_BUFFER_PRIVATE_HH */
index ea05307..e2c34f1 100644 (file)
 
 #include <string.h>
 
-HB_BEGIN_DECLS
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
-
-static hb_buffer_t _hb_buffer_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  &_hb_unicode_funcs_default,
-  {
-    HB_DIRECTION_INVALID,
-    HB_SCRIPT_INVALID,
-    NULL,
-  },
-
-  TRUE, /* in_error */
-  TRUE, /* have_output */
-  TRUE  /* have_positions */
-};
+#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default))
+#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID }
 
 /* Here is how the buffer works internally:
  *
@@ -80,7 +66,7 @@ bool
 hb_buffer_t::enlarge (unsigned int size)
 {
   if (unlikely (in_error))
-    return FALSE;
+    return false;
 
   unsigned int new_allocated = allocated;
   hb_glyph_position_t *new_pos = NULL;
@@ -102,7 +88,7 @@ hb_buffer_t::enlarge (unsigned int size)
 
 done:
   if (unlikely (!new_pos || !new_info))
-    in_error = TRUE;
+    in_error = true;
 
   if (likely (new_pos))
     pos = new_pos;
@@ -121,7 +107,7 @@ bool
 hb_buffer_t::make_room_for (unsigned int num_in,
                            unsigned int num_out)
 {
-  if (unlikely (!ensure (out_len + num_out))) return FALSE;
+  if (unlikely (!ensure (out_len + num_out))) return false;
 
   if (out_info == info &&
       out_len + num_out > idx + num_in)
@@ -132,7 +118,17 @@ hb_buffer_t::make_room_for (unsigned int num_in,
     memcpy (out_info, info, out_len * sizeof (out_info[0]));
   }
 
-  return TRUE;
+  return true;
+}
+
+void *
+hb_buffer_t::get_scratch_buffer (unsigned int *size)
+{
+  have_output = false;
+  have_positions = false;
+  out_len = 0;
+  *size = allocated * sizeof (pos[0]);
+  return pos;
 }
 
 
@@ -145,13 +141,14 @@ hb_buffer_t::reset (void)
     return;
 
   hb_unicode_funcs_destroy (unicode);
-  unicode = _hb_buffer_nil.unicode;
+  unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
 
-  props = _hb_buffer_nil.props;
+  hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
+  props = default_props;
 
-  in_error = FALSE;
-  have_output = FALSE;
-  have_positions = FALSE;
+  in_error = false;
+  have_output = false;
+  have_positions = false;
 
   idx = 0;
   len = 0;
@@ -189,8 +186,8 @@ hb_buffer_t::clear_output (void)
   if (unlikely (hb_object_is_inert (this)))
     return;
 
-  have_output = TRUE;
-  have_positions = FALSE;
+  have_output = true;
+  have_positions = false;
 
   out_len = 0;
   out_info = info;
@@ -202,8 +199,8 @@ hb_buffer_t::clear_positions (void)
   if (unlikely (hb_object_is_inert (this)))
     return;
 
-  have_output = FALSE;
-  have_positions = TRUE;
+  have_output = false;
+  have_positions = true;
 
   memset (pos, 0, sizeof (pos[0]) * len);
 }
@@ -214,6 +211,7 @@ hb_buffer_t::swap_buffers (void)
   if (unlikely (in_error)) return;
 
   assert (have_output);
+  have_output = false;
 
   if (out_info != info)
   {
@@ -259,6 +257,32 @@ hb_buffer_t::replace_glyphs_be16 (unsigned int num_in,
 }
 
 void
+hb_buffer_t::replace_glyphs (unsigned int num_in,
+                            unsigned int num_out,
+                            const uint32_t *glyph_data)
+{
+  if (!make_room_for (num_in, num_out)) return;
+
+  hb_glyph_info_t orig_info = info[idx];
+  for (unsigned int i = 1; i < num_in; i++)
+  {
+    hb_glyph_info_t *inf = &info[idx + i];
+    orig_info.cluster = MIN (orig_info.cluster, inf->cluster);
+  }
+
+  hb_glyph_info_t *pinfo = &out_info[out_len];
+  for (unsigned int i = 0; i < num_out; i++)
+  {
+    *pinfo = orig_info;
+    pinfo->codepoint = glyph_data[i];
+    pinfo++;
+  }
+
+  idx  += num_in;
+  out_len += num_out;
+}
+
+void
 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
 {
   if (!make_room_for (0, 1)) return;
@@ -282,6 +306,8 @@ hb_buffer_t::copy_glyph (void)
 void
 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
 {
+  if (!make_room_for (1, 1)) return;
+
   out_info[out_len] = info[idx];
   out_info[out_len].codepoint = glyph_index;
 
@@ -393,6 +419,58 @@ hb_buffer_t::reverse_clusters (void)
   reverse_range (start, i);
 }
 
+void
+hb_buffer_t::merge_clusters (unsigned int start,
+                            unsigned int end)
+{
+  unsigned int cluster = this->info[start].cluster;
+
+  for (unsigned int i = start + 1; i < end; i++)
+    cluster = MIN (cluster, this->info[i].cluster);
+  for (unsigned int i = start; i < end; i++)
+    this->info[i].cluster = cluster;
+}
+void
+hb_buffer_t::merge_out_clusters (unsigned int start,
+                                unsigned int end)
+{
+  unsigned int cluster = this->out_info[start].cluster;
+
+  for (unsigned int i = start + 1; i < end; i++)
+    cluster = MIN (cluster, this->out_info[i].cluster);
+  for (unsigned int i = start; i < end; i++)
+    this->out_info[i].cluster = cluster;
+}
+
+void
+hb_buffer_t::guess_properties (void)
+{
+  /* If script is set to INVALID, guess from buffer contents */
+  if (props.script == HB_SCRIPT_INVALID) {
+    for (unsigned int i = 0; i < len; i++) {
+      hb_script_t script = hb_unicode_script (unicode, info[i].codepoint);
+      if (likely (script != HB_SCRIPT_COMMON &&
+                 script != HB_SCRIPT_INHERITED &&
+                 script != HB_SCRIPT_UNKNOWN)) {
+        props.script = script;
+        break;
+      }
+    }
+  }
+
+  /* If direction is set to INVALID, guess from script */
+  if (props.direction == HB_DIRECTION_INVALID) {
+    props.direction = hb_script_get_horizontal_direction (props.script);
+  }
+
+  /* If language is not set, use default language from locale */
+  if (props.language == HB_LANGUAGE_INVALID) {
+    /* TODO get_default_for_script? using $LANGUAGE */
+    props.language = hb_language_get_default ();
+  }
+}
+
+
 static inline void
 dump_var_allocation (const hb_buffer_t *buffer)
 {
@@ -448,27 +526,33 @@ void hb_buffer_t::deallocate_var_all (void)
 /* Public API */
 
 hb_buffer_t *
-hb_buffer_create (unsigned int pre_alloc_size)
+hb_buffer_create ()
 {
   hb_buffer_t *buffer;
 
   if (!(buffer = hb_object_create<hb_buffer_t> ()))
-    return &_hb_buffer_nil;
+    return hb_buffer_get_empty ();
 
   buffer->reset ();
 
-  if (pre_alloc_size && !buffer->ensure (pre_alloc_size)) {
-    hb_buffer_destroy (buffer);
-    return &_hb_buffer_nil;
-  }
-
   return buffer;
 }
 
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
-  return &_hb_buffer_nil;
+  static const hb_buffer_t _hb_buffer_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    _HB_BUFFER_UNICODE_FUNCS_DEFAULT,
+    _HB_BUFFER_PROPS_DEFAULT,
+
+    true, /* in_error */
+    true, /* have_output */
+    true  /* have_positions */
+  };
+
+  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
 }
 
 hb_buffer_t *
@@ -494,9 +578,10 @@ hb_bool_t
 hb_buffer_set_user_data (hb_buffer_t        *buffer,
                         hb_user_data_key_t *key,
                         void *              data,
-                        hb_destroy_func_t   destroy)
+                        hb_destroy_func_t   destroy,
+                        hb_bool_t           replace)
 {
-  return hb_object_set_user_data (buffer, key, data, destroy);
+  return hb_object_set_user_data (buffer, key, data, destroy, replace);
 }
 
 void *
@@ -515,7 +600,7 @@ hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
     return;
 
   if (!unicode)
-    unicode = _hb_buffer_nil.unicode;
+    unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
 
   hb_unicode_funcs_reference (unicode);
   hb_unicode_funcs_destroy (buffer->unicode);
@@ -609,8 +694,11 @@ hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
                      unsigned int  length)
 {
+  if (unlikely (hb_object_is_inert (buffer)))
+    return length == 0;
+
   if (!buffer->ensure (length))
-    return FALSE;
+    return false;
 
   /* Wipe the new space */
   if (length > buffer->len) {
@@ -620,7 +708,7 @@ hb_buffer_set_length (hb_buffer_t  *buffer,
   }
 
   buffer->len = length;
-  return TRUE;
+  return true;
 }
 
 unsigned int
@@ -666,8 +754,25 @@ hb_buffer_reverse_clusters (hb_buffer_t *buffer)
   buffer->reverse_clusters ();
 }
 
+void
+hb_buffer_guess_properties (hb_buffer_t *buffer)
+{
+  buffer->guess_properties ();
+}
+
 #define ADD_UTF(T) \
        HB_STMT_START { \
+         if (text_length == -1) { \
+           text_length = 0; \
+           const T *p = (const T *) text; \
+           while (*p) { \
+             text_length++; \
+             p++; \
+           } \
+         } \
+         if (item_length == -1) \
+           item_length = text_length - item_offset; \
+         buffer->ensure (buffer->len + item_length * sizeof (T) / 4); \
          const T *next = (const T *) text + item_offset; \
          const T *end = next + item_length; \
          while (next < end) { \
@@ -722,9 +827,9 @@ hb_utf8_next (const uint8_t *text,
 void
 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
                    const char   *text,
-                   unsigned int  text_length HB_UNUSED,
+                   int           text_length,
                    unsigned int  item_offset,
-                   unsigned int  item_length)
+                   int           item_length)
 {
 #define UTF_NEXT(S, E, U)      hb_utf8_next (S, E, &(U))
   ADD_UTF (uint8_t);
@@ -756,9 +861,9 @@ hb_utf16_next (const uint16_t *text,
 void
 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
                     const uint16_t *text,
-                    unsigned int    text_length HB_UNUSED,
+                    int             text_length,
                     unsigned int    item_offset,
-                    unsigned int    item_length)
+                    int            item_length)
 {
 #define UTF_NEXT(S, E, U)      hb_utf16_next (S, E, &(U))
   ADD_UTF (uint16_t);
@@ -768,9 +873,9 @@ hb_buffer_add_utf16 (hb_buffer_t    *buffer,
 void
 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
                     const uint32_t *text,
-                    unsigned int    text_length HB_UNUSED,
+                    int             text_length,
                     unsigned int    item_offset,
-                    unsigned int    item_length)
+                    int             item_length)
 {
 #define UTF_NEXT(S, E, U)      ((U) = *(S), (S)+1)
   ADD_UTF (uint32_t);
@@ -778,4 +883,3 @@ hb_buffer_add_utf32 (hb_buffer_t    *buffer,
 }
 
 
-HB_END_DECLS
index 020a120..fe53197 100644 (file)
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_BUFFER_H
 #define HB_BUFFER_H
 
@@ -60,7 +64,7 @@ typedef struct _hb_glyph_position_t {
 
 
 hb_buffer_t *
-hb_buffer_create (unsigned int pre_alloc_size);
+hb_buffer_create (void);
 
 hb_buffer_t *
 hb_buffer_get_empty (void);
@@ -75,7 +79,8 @@ hb_bool_t
 hb_buffer_set_user_data (hb_buffer_t        *buffer,
                         hb_user_data_key_t *key,
                         void *              data,
-                        hb_destroy_func_t   destroy);
+                        hb_destroy_func_t   destroy,
+                        hb_bool_t           replace);
 
 void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
@@ -116,13 +121,13 @@ hb_buffer_get_language (hb_buffer_t *buffer);
 void
 hb_buffer_reset (hb_buffer_t *buffer);
 
-/* Returns FALSE if allocation failed */
+/* Returns false if allocation failed */
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t  *buffer,
                        unsigned int  size);
 
 
-/* Returns FALSE if allocation has failed before */
+/* Returns false if allocation has failed before */
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer);
 
@@ -132,6 +137,9 @@ hb_buffer_reverse (hb_buffer_t *buffer);
 void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer);
 
+void
+hb_buffer_guess_properties (hb_buffer_t *buffer);
+
 
 /* Filling the buffer in */
 
@@ -144,23 +152,23 @@ hb_buffer_add (hb_buffer_t    *buffer,
 void
 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
                    const char   *text,
-                   unsigned int  text_length,
+                   int           text_length,
                    unsigned int  item_offset,
-                   unsigned int  item_length);
+                   int           item_length);
 
 void
 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
                     const uint16_t *text,
-                    unsigned int    text_length,
+                    int             text_length,
                     unsigned int    item_offset,
-                    unsigned int    item_length);
+                    int             item_length);
 
 void
 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
                     const uint32_t *text,
-                    unsigned int    text_length,
+                    int             text_length,
                     unsigned int    item_offset,
-                    unsigned int    item_length);
+                    int             item_length);
 
 
 /* Clears any new items added at the end */
diff --git a/src/hb-cache-private.hh b/src/hb-cache-private.hh
new file mode 100644 (file)
index 0000000..a0928a0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2012  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_CACHE_PRIVATE_HH
+#define HB_CACHE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* Implements a lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+  ASSERT_STATIC (key_bits >= cache_bits);
+  ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int));
+
+  inline void clear (void)
+  {
+    memset (values, 255, sizeof (values));
+  }
+
+  inline bool get (unsigned int key, unsigned int *value)
+  {
+    unsigned int k = key & ((1<<cache_bits)-1);
+    unsigned int v = values[k];
+    if ((v >> value_bits) != (key >> cache_bits))
+      return false;
+  }
+
+  inline bool set (unsigned int key, unsigned int value)
+  {
+    if (unlikely ((key >> key_bits) || (value >> value_bits)))
+      return false; /* Overflows */
+    unsigned int k = key & ((1<<cache_bits)-1);
+    unsigned int v = ((key>>cache_bits)<<value_bits) | value;
+    values[k] = v;
+    return true;
+  }
+
+  private:
+  unsigned int values[1<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_PRIVATE_HH */
index a886474..331d255 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 
 #include <locale.h>
 
-HB_BEGIN_DECLS
 
 
 /* hb_tag_t */
 
 hb_tag_t
-hb_tag_from_string (const char *s)
+hb_tag_from_string (const char *s, int len)
 {
   char tag[4];
   unsigned int i;
 
-  if (!s || !*s)
+  if (!s || !len || !*s)
     return HB_TAG_NONE;
 
-  for (i = 0; i < 4 && s[i]; i++)
+  if (len < 0 || len > 4)
+    len = 4;
+  for (i = 0; i < (unsigned) len && s[i]; i++)
     tag[i] = s[i];
   for (; i < 4; i++)
     tag[i] = ' ';
@@ -68,9 +69,9 @@ const char direction_strings[][4] = {
 };
 
 hb_direction_t
-hb_direction_from_string (const char *str)
+hb_direction_from_string (const char *str, int len)
 {
-  if (unlikely (!str || !*str))
+  if (unlikely (!str || !len || !*str))
     return HB_DIRECTION_INVALID;
 
   /* Lets match loosely: just match the first letter, such that
@@ -79,7 +80,7 @@ hb_direction_from_string (const char *str)
   char c = TOLOWER (str[0]);
   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
     if (c == direction_strings[i][0])
-      return (hb_direction_t) i;
+      return (hb_direction_t) (HB_DIRECTION_LTR + i);
 
   return HB_DIRECTION_INVALID;
 }
@@ -87,8 +88,9 @@ hb_direction_from_string (const char *str)
 const char *
 hb_direction_to_string (hb_direction_t direction)
 {
-  if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings)))
-    return direction_strings[direction];
+  if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
+             < ARRAY_LENGTH (direction_strings)))
+    return direction_strings[direction - HB_DIRECTION_LTR];
 
   return "invalid";
 }
@@ -112,18 +114,16 @@ static const char canon_map[256] = {
 };
 
 static hb_bool_t
-lang_equal (const void *v1,
-           const void *v2)
+lang_equal (hb_language_t  v1,
+           const void    *v2)
 {
   const unsigned char *p1 = (const unsigned char *) v1;
   const unsigned char *p2 = (const unsigned char *) v2;
 
-  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
-    {
-      p1++, p2++;
-    }
+  while (*p1 && *p1 == canon_map[*p2])
+    p1++, p2++;
 
-  return (canon_map[*p1] == canon_map[*p2]);
+  return *p1 == canon_map[*p2];
 }
 
 #if 0
@@ -145,6 +145,7 @@ lang_hash (const void *key)
 
 struct hb_language_item_t {
 
+  struct hb_language_item_t *next;
   hb_language_t lang;
 
   inline bool operator == (const char *s) const {
@@ -162,16 +163,67 @@ struct hb_language_item_t {
   void finish (void) { free (lang); }
 };
 
-static hb_static_mutex_t langs_lock;
-static hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> langs;
+
+/* Thread-safe lock-free language list */
+
+static hb_language_item_t *langs;
+
+static
+void free_langs (void)
+{
+  while (langs) {
+    hb_language_item_t *next = langs->next;
+    langs->finish ();
+    free (langs);
+    langs = next;
+  }
+}
+
+static hb_language_item_t *
+lang_find_or_insert (const char *key)
+{
+retry:
+  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
+
+  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
+    if (*lang == key)
+      return lang;
+
+  /* Not found; allocate one. */
+  hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
+  if (unlikely (!lang))
+    return NULL;
+  lang->next = first_lang;
+  *lang = key;
+
+  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
+    free (lang);
+    goto retry;
+  }
+
+#ifdef HAVE_ATEXIT
+  if (!first_lang)
+    atexit (free_langs); /* First person registers atexit() callback. */
+#endif
+
+  return lang;
+}
+
 
 hb_language_t
-hb_language_from_string (const char *str)
+hb_language_from_string (const char *str, int len)
 {
-  if (!str || !*str)
+  if (!str || !len || !*str)
     return HB_LANGUAGE_INVALID;
 
-  hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
+  char strbuf[32];
+  if (len >= 0) {
+    len = MIN (len, (int) sizeof (strbuf) - 1);
+    str = (char *) memcpy (strbuf, str, len);
+    strbuf[len] = '\0';
+  }
+
+  hb_language_item_t *item = lang_find_or_insert (str);
 
   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
 }
@@ -196,7 +248,7 @@ hb_language_get_default (void)
     /* I hear that setlocale() doesn't honor env vars on Windows,
      * but for now we ignore that. */
 
-    default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
+    default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
   }
 
   return default_language;
@@ -240,9 +292,9 @@ hb_script_from_iso15924_tag (hb_tag_t tag)
 }
 
 hb_script_t
-hb_script_from_string (const char *s)
+hb_script_from_string (const char *s, int len)
 {
-  return hb_script_from_iso15924_tag (hb_tag_from_string (s));
+  return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
 }
 
 hb_tag_t
@@ -254,20 +306,30 @@ hb_script_to_iso15924_tag (hb_script_t script)
 hb_direction_t
 hb_script_get_horizontal_direction (hb_script_t script)
 {
+  /* http://goo.gl/x9ilM */
   switch ((hb_tag_t) script)
   {
+    /* Unicode-1.1 additions */
     case HB_SCRIPT_ARABIC:
     case HB_SCRIPT_HEBREW:
+
+    /* Unicode-3.0 additions */
     case HB_SCRIPT_SYRIAC:
     case HB_SCRIPT_THAANA:
 
     /* Unicode-4.0 additions */
     case HB_SCRIPT_CYPRIOT:
 
+    /* Unicode-4.1 additions */
+    case HB_SCRIPT_KHAROSHTHI:
+
     /* Unicode-5.0 additions */
     case HB_SCRIPT_PHOENICIAN:
     case HB_SCRIPT_NKO:
 
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_LYDIAN:
+
     /* Unicode-5.2 additions */
     case HB_SCRIPT_AVESTAN:
     case HB_SCRIPT_IMPERIAL_ARAMAIC:
@@ -280,6 +342,10 @@ hb_script_get_horizontal_direction (hb_script_t script)
     /* Unicode-6.0 additions */
     case HB_SCRIPT_MANDAIC:
 
+    /* Unicode-6.1 additions */
+    case HB_SCRIPT_MEROITIC_CURSIVE:
+    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
+
       return HB_DIRECTION_RTL;
   }
 
@@ -289,44 +355,41 @@ hb_script_get_horizontal_direction (hb_script_t script)
 
 /* hb_user_data_array_t */
 
-
-/* NOTE: Currently we use a global lock for user_data access
- * threadsafety.  If one day we add a mutex to any object, we
- * should switch to using that insted for these too.
- */
-
-static hb_static_mutex_t user_data_lock;
-
 bool
 hb_user_data_array_t::set (hb_user_data_key_t *key,
                           void *              data,
-                          hb_destroy_func_t   destroy)
+                          hb_destroy_func_t   destroy,
+                          hb_bool_t           replace,
+                          hb_mutex_t         &lock)
 {
   if (!key)
     return false;
 
-  if (!data && !destroy) {
-    items.remove (key, user_data_lock);
-    return true;
+  if (replace) {
+    if (!data && !destroy) {
+      items.remove (key, lock);
+      return true;
+    }
   }
   hb_user_data_item_t item = {key, data, destroy};
-  bool ret = !!items.replace_or_insert (item, user_data_lock);
+  bool ret = !!items.replace_or_insert (item, lock, replace);
 
   return ret;
 }
 
 void *
-hb_user_data_array_t::get (hb_user_data_key_t *key)
+hb_user_data_array_t::get (hb_user_data_key_t *key,
+                          hb_mutex_t         &lock)
 {
   hb_user_data_item_t item = {NULL };
 
-  return items.find (key, &item, user_data_lock) ? item.data : NULL;
+  return items.find (key, &item, lock) ? item.data : NULL;
 }
 
 void
-hb_user_data_array_t::finish (void)
+hb_user_data_array_t::finish (hb_mutex_t &lock)
 {
-  items.finish (user_data_lock);
+  items.finish (lock);
 }
 
 
@@ -357,4 +420,3 @@ hb_version_check (unsigned int major,
 }
 
 
-HB_END_DECLS
index a0ab17e..562b04c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_COMMON_H
 #define HB_COMMON_H
 
@@ -89,38 +93,42 @@ typedef uint32_t hb_tag_t;
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 
-hb_tag_t hb_tag_from_string (const char *s);
+/* len=-1 means str is NUL-terminated */
+hb_tag_t hb_tag_from_string (const char *str, int len);
 
 
 /* hb_direction_t */
 
-typedef enum _hb_direction_t {
-  HB_DIRECTION_INVALID = -1,
-  HB_DIRECTION_LTR = 0,
+typedef enum {
+  HB_DIRECTION_INVALID = 0,
+  HB_DIRECTION_LTR = 4,
   HB_DIRECTION_RTL,
   HB_DIRECTION_TTB,
   HB_DIRECTION_BTT
 } hb_direction_t;
 
+/* len=-1 means str is NUL-terminated */
 hb_direction_t
-hb_direction_from_string (const char *str);
+hb_direction_from_string (const char *str, int len);
 
 const char *
 hb_direction_to_string (hb_direction_t direction);
 
-#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))
+#define HB_DIRECTION_IS_HORIZONTAL(dir)        ((((unsigned int) (dir)) & ~1U) == 4)
+#define HB_DIRECTION_IS_VERTICAL(dir)  ((((unsigned int) (dir)) & ~1U) == 6)
+#define HB_DIRECTION_IS_FORWARD(dir)   ((((unsigned int) (dir)) & ~2U) == 4)
+#define HB_DIRECTION_IS_BACKWARD(dir)  ((((unsigned int) (dir)) & ~2U) == 5)
+#define HB_DIRECTION_IS_VALID(dir)     ((((unsigned int) (dir)) & ~3U) == 4)
+#define HB_DIRECTION_REVERSE(dir)      ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */
 
 
 /* hb_language_t */
 
 typedef struct _hb_language_t *hb_language_t;
 
+/* len=-1 means str is NUL-terminated */
 hb_language_t
-hb_language_from_string (const char *str);
+hb_language_from_string (const char *str, int len);
 
 const char *
 hb_language_to_string (hb_language_t language);
@@ -171,119 +179,138 @@ typedef enum
 /* hb_script_t */
 
 /* http://unicode.org/iso15924/ */
+/* http://goo.gl/x9ilM */
 typedef enum
 {
-  HB_SCRIPT_COMMON                  = HB_TAG ('Z','y','y','y'),
-  HB_SCRIPT_INHERITED               = HB_TAG ('Z','i','n','h'),
-  HB_SCRIPT_ARABIC                  = HB_TAG ('A','r','a','b'),
-  HB_SCRIPT_ARMENIAN                = HB_TAG ('A','r','m','n'),
-  HB_SCRIPT_BENGALI                 = HB_TAG ('B','e','n','g'),
-  HB_SCRIPT_BOPOMOFO                = HB_TAG ('B','o','p','o'),
-  HB_SCRIPT_CHEROKEE                = HB_TAG ('C','h','e','r'),
-  HB_SCRIPT_COPTIC                  = HB_TAG ('C','o','p','t'),
-  HB_SCRIPT_CYRILLIC                = HB_TAG ('C','y','r','l'),
-  HB_SCRIPT_DESERET                 = HB_TAG ('D','s','r','t'),
-  HB_SCRIPT_DEVANAGARI              = HB_TAG ('D','e','v','a'),
-  HB_SCRIPT_ETHIOPIC                = HB_TAG ('E','t','h','i'),
-  HB_SCRIPT_GEORGIAN                = HB_TAG ('G','e','o','r'),
-  HB_SCRIPT_GOTHIC                  = HB_TAG ('G','o','t','h'),
-  HB_SCRIPT_GREEK                   = HB_TAG ('G','r','e','k'),
-  HB_SCRIPT_GUJARATI                = HB_TAG ('G','u','j','r'),
-  HB_SCRIPT_GURMUKHI                = HB_TAG ('G','u','r','u'),
-  HB_SCRIPT_HAN                     = HB_TAG ('H','a','n','i'),
-  HB_SCRIPT_HANGUL                  = HB_TAG ('H','a','n','g'),
-  HB_SCRIPT_HEBREW                  = HB_TAG ('H','e','b','r'),
-  HB_SCRIPT_HIRAGANA                = HB_TAG ('H','i','r','a'),
-  HB_SCRIPT_KANNADA                 = HB_TAG ('K','n','d','a'),
-  HB_SCRIPT_KATAKANA                = HB_TAG ('K','a','n','a'),
-  HB_SCRIPT_KHMER                   = HB_TAG ('K','h','m','r'),
-  HB_SCRIPT_LAO                     = HB_TAG ('L','a','o','o'),
-  HB_SCRIPT_LATIN                   = HB_TAG ('L','a','t','n'),
-  HB_SCRIPT_MALAYALAM               = HB_TAG ('M','l','y','m'),
-  HB_SCRIPT_MONGOLIAN               = HB_TAG ('M','o','n','g'),
-  HB_SCRIPT_MYANMAR                 = HB_TAG ('M','y','m','r'),
-  HB_SCRIPT_OGHAM                   = HB_TAG ('O','g','a','m'),
-  HB_SCRIPT_OLD_ITALIC              = HB_TAG ('I','t','a','l'),
-  HB_SCRIPT_ORIYA                   = HB_TAG ('O','r','y','a'),
-  HB_SCRIPT_RUNIC                   = HB_TAG ('R','u','n','r'),
-  HB_SCRIPT_SINHALA                 = HB_TAG ('S','i','n','h'),
-  HB_SCRIPT_SYRIAC                  = HB_TAG ('S','y','r','c'),
-  HB_SCRIPT_TAMIL                   = HB_TAG ('T','a','m','l'),
-  HB_SCRIPT_TELUGU                  = HB_TAG ('T','e','l','u'),
-  HB_SCRIPT_THAANA                  = HB_TAG ('T','h','a','a'),
-  HB_SCRIPT_THAI                    = HB_TAG ('T','h','a','i'),
-  HB_SCRIPT_TIBETAN                 = HB_TAG ('T','i','b','t'),
-  HB_SCRIPT_CANADIAN_ABORIGINAL     = HB_TAG ('C','a','n','s'),
-  HB_SCRIPT_YI                      = HB_TAG ('Y','i','i','i'),
-  HB_SCRIPT_TAGALOG                 = HB_TAG ('T','g','l','g'),
-  HB_SCRIPT_HANUNOO                 = HB_TAG ('H','a','n','o'),
-  HB_SCRIPT_BUHID                   = HB_TAG ('B','u','h','d'),
-  HB_SCRIPT_TAGBANWA                = HB_TAG ('T','a','g','b'),
+  /* Unicode-1.1 additions */
+  HB_SCRIPT_COMMON                     = HB_TAG ('Z','y','y','y'),
+  HB_SCRIPT_ARABIC                     = HB_TAG ('A','r','a','b'),
+  HB_SCRIPT_ARMENIAN                   = HB_TAG ('A','r','m','n'),
+  HB_SCRIPT_BENGALI                    = HB_TAG ('B','e','n','g'),
+  HB_SCRIPT_BOPOMOFO                   = HB_TAG ('B','o','p','o'),
+  HB_SCRIPT_CANADIAN_ABORIGINAL                = HB_TAG ('C','a','n','s'),
+  HB_SCRIPT_CHEROKEE                   = HB_TAG ('C','h','e','r'),
+  HB_SCRIPT_COPTIC                     = HB_TAG ('C','o','p','t'),
+  HB_SCRIPT_CYRILLIC                   = HB_TAG ('C','y','r','l'),
+  HB_SCRIPT_DEVANAGARI                 = HB_TAG ('D','e','v','a'),
+  HB_SCRIPT_GEORGIAN                   = HB_TAG ('G','e','o','r'),
+  HB_SCRIPT_GREEK                      = HB_TAG ('G','r','e','k'),
+  HB_SCRIPT_GUJARATI                   = HB_TAG ('G','u','j','r'),
+  HB_SCRIPT_GURMUKHI                   = HB_TAG ('G','u','r','u'),
+  HB_SCRIPT_HANGUL                     = HB_TAG ('H','a','n','g'),
+  HB_SCRIPT_HAN                                = HB_TAG ('H','a','n','i'),
+  HB_SCRIPT_HEBREW                     = HB_TAG ('H','e','b','r'),
+  HB_SCRIPT_HIRAGANA                   = HB_TAG ('H','i','r','a'),
+  HB_SCRIPT_INHERITED                  = HB_TAG ('Z','i','n','h'),
+  HB_SCRIPT_KANNADA                    = HB_TAG ('K','n','d','a'),
+  HB_SCRIPT_KATAKANA                   = HB_TAG ('K','a','n','a'),
+  HB_SCRIPT_LAO                                = HB_TAG ('L','a','o','o'),
+  HB_SCRIPT_LATIN                      = HB_TAG ('L','a','t','n'),
+  HB_SCRIPT_MALAYALAM                  = HB_TAG ('M','l','y','m'),
+  HB_SCRIPT_MONGOLIAN                  = HB_TAG ('M','o','n','g'),
+  HB_SCRIPT_OGHAM                      = HB_TAG ('O','g','a','m'),
+  HB_SCRIPT_ORIYA                      = HB_TAG ('O','r','y','a'),
+  HB_SCRIPT_RUNIC                      = HB_TAG ('R','u','n','r'),
+  HB_SCRIPT_SYRIAC                     = HB_TAG ('S','y','r','c'),
+  HB_SCRIPT_TAMIL                      = HB_TAG ('T','a','m','l'),
+  HB_SCRIPT_TELUGU                     = HB_TAG ('T','e','l','u'),
+  HB_SCRIPT_THAI                       = HB_TAG ('T','h','a','i'),
+  HB_SCRIPT_YI                         = HB_TAG ('Y','i','i','i'),
+
+  /* Unicode-2.0 additions */
+  HB_SCRIPT_TIBETAN                    = HB_TAG ('T','i','b','t'),
+
+  /* Unicode-3.0 additions */
+  HB_SCRIPT_ETHIOPIC                   = HB_TAG ('E','t','h','i'),
+  HB_SCRIPT_KHMER                      = HB_TAG ('K','h','m','r'),
+  HB_SCRIPT_MYANMAR                    = HB_TAG ('M','y','m','r'),
+  HB_SCRIPT_SINHALA                    = HB_TAG ('S','i','n','h'),
+  HB_SCRIPT_THAANA                     = HB_TAG ('T','h','a','a'),
+
+  /* Unicode-3.1 additions */
+  HB_SCRIPT_DESERET                    = HB_TAG ('D','s','r','t'),
+  HB_SCRIPT_GOTHIC                     = HB_TAG ('G','o','t','h'),
+  HB_SCRIPT_OLD_ITALIC                 = HB_TAG ('I','t','a','l'),
+
+  /* Unicode-3.2 additions */
+  HB_SCRIPT_BUHID                      = HB_TAG ('B','u','h','d'),
+  HB_SCRIPT_HANUNOO                    = HB_TAG ('H','a','n','o'),
+  HB_SCRIPT_TAGALOG                    = HB_TAG ('T','g','l','g'),
+  HB_SCRIPT_TAGBANWA                   = HB_TAG ('T','a','g','b'),
 
   /* Unicode-4.0 additions */
-  HB_SCRIPT_BRAILLE                 = HB_TAG ('B','r','a','i'),
-  HB_SCRIPT_CYPRIOT                 = HB_TAG ('C','p','r','t'),
-  HB_SCRIPT_LIMBU                   = HB_TAG ('L','i','m','b'),
-  HB_SCRIPT_OSMANYA                 = HB_TAG ('O','s','m','a'),
-  HB_SCRIPT_SHAVIAN                 = HB_TAG ('S','h','a','w'),
-  HB_SCRIPT_LINEAR_B                = HB_TAG ('L','i','n','b'),
-  HB_SCRIPT_TAI_LE                  = HB_TAG ('T','a','l','e'),
-  HB_SCRIPT_UGARITIC                = HB_TAG ('U','g','a','r'),
+  HB_SCRIPT_BRAILLE                    = HB_TAG ('B','r','a','i'),
+  HB_SCRIPT_CYPRIOT                    = HB_TAG ('C','p','r','t'),
+  HB_SCRIPT_LIMBU                      = HB_TAG ('L','i','m','b'),
+  HB_SCRIPT_LINEAR_B                   = HB_TAG ('L','i','n','b'),
+  HB_SCRIPT_OSMANYA                    = HB_TAG ('O','s','m','a'),
+  HB_SCRIPT_SHAVIAN                    = HB_TAG ('S','h','a','w'),
+  HB_SCRIPT_TAI_LE                     = HB_TAG ('T','a','l','e'),
+  HB_SCRIPT_UGARITIC                   = HB_TAG ('U','g','a','r'),
 
   /* Unicode-4.1 additions */
-  HB_SCRIPT_NEW_TAI_LUE             = HB_TAG ('T','a','l','u'),
-  HB_SCRIPT_BUGINESE                = HB_TAG ('B','u','g','i'),
-  HB_SCRIPT_GLAGOLITIC              = HB_TAG ('G','l','a','g'),
-  HB_SCRIPT_TIFINAGH                = HB_TAG ('T','f','n','g'),
-  HB_SCRIPT_SYLOTI_NAGRI            = HB_TAG ('S','y','l','o'),
-  HB_SCRIPT_OLD_PERSIAN             = HB_TAG ('X','p','e','o'),
-  HB_SCRIPT_KHAROSHTHI              = HB_TAG ('K','h','a','r'),
+  HB_SCRIPT_BUGINESE                   = HB_TAG ('B','u','g','i'),
+  HB_SCRIPT_GLAGOLITIC                 = HB_TAG ('G','l','a','g'),
+  HB_SCRIPT_KHAROSHTHI                 = HB_TAG ('K','h','a','r'),
+  HB_SCRIPT_NEW_TAI_LUE                        = HB_TAG ('T','a','l','u'),
+  HB_SCRIPT_OLD_PERSIAN                        = HB_TAG ('X','p','e','o'),
+  HB_SCRIPT_SYLOTI_NAGRI               = HB_TAG ('S','y','l','o'),
+  HB_SCRIPT_TIFINAGH                   = HB_TAG ('T','f','n','g'),
 
   /* Unicode-5.0 additions */
-  HB_SCRIPT_UNKNOWN                 = HB_TAG ('Z','z','z','z'),
-  HB_SCRIPT_BALINESE                = HB_TAG ('B','a','l','i'),
-  HB_SCRIPT_CUNEIFORM               = HB_TAG ('X','s','u','x'),
-  HB_SCRIPT_PHOENICIAN              = HB_TAG ('P','h','n','x'),
-  HB_SCRIPT_PHAGS_PA                = HB_TAG ('P','h','a','g'),
-  HB_SCRIPT_NKO                     = HB_TAG ('N','k','o','o'),
+  HB_SCRIPT_BALINESE                   = HB_TAG ('B','a','l','i'),
+  HB_SCRIPT_CUNEIFORM                  = HB_TAG ('X','s','u','x'),
+  HB_SCRIPT_NKO                                = HB_TAG ('N','k','o','o'),
+  HB_SCRIPT_PHAGS_PA                   = HB_TAG ('P','h','a','g'),
+  HB_SCRIPT_PHOENICIAN                 = HB_TAG ('P','h','n','x'),
+  HB_SCRIPT_UNKNOWN                    = HB_TAG ('Z','z','z','z'),
 
   /* Unicode-5.1 additions */
-  HB_SCRIPT_KAYAH_LI                = HB_TAG ('K','a','l','i'),
-  HB_SCRIPT_LEPCHA                  = HB_TAG ('L','e','p','c'),
-  HB_SCRIPT_REJANG                  = HB_TAG ('R','j','n','g'),
-  HB_SCRIPT_SUNDANESE               = HB_TAG ('S','u','n','d'),
-  HB_SCRIPT_SAURASHTRA              = HB_TAG ('S','a','u','r'),
-  HB_SCRIPT_CHAM                    = HB_TAG ('C','h','a','m'),
-  HB_SCRIPT_OL_CHIKI                = HB_TAG ('O','l','c','k'),
-  HB_SCRIPT_VAI                     = HB_TAG ('V','a','i','i'),
-  HB_SCRIPT_CARIAN                  = HB_TAG ('C','a','r','i'),
-  HB_SCRIPT_LYCIAN                  = HB_TAG ('L','y','c','i'),
-  HB_SCRIPT_LYDIAN                  = HB_TAG ('L','y','d','i'),
+  HB_SCRIPT_CARIAN                     = HB_TAG ('C','a','r','i'),
+  HB_SCRIPT_CHAM                       = HB_TAG ('C','h','a','m'),
+  HB_SCRIPT_KAYAH_LI                   = HB_TAG ('K','a','l','i'),
+  HB_SCRIPT_LEPCHA                     = HB_TAG ('L','e','p','c'),
+  HB_SCRIPT_LYCIAN                     = HB_TAG ('L','y','c','i'),
+  HB_SCRIPT_LYDIAN                     = HB_TAG ('L','y','d','i'),
+  HB_SCRIPT_OL_CHIKI                   = HB_TAG ('O','l','c','k'),
+  HB_SCRIPT_REJANG                     = HB_TAG ('R','j','n','g'),
+  HB_SCRIPT_SAURASHTRA                 = HB_TAG ('S','a','u','r'),
+  HB_SCRIPT_SUNDANESE                  = HB_TAG ('S','u','n','d'),
+  HB_SCRIPT_VAI                                = HB_TAG ('V','a','i','i'),
 
   /* Unicode-5.2 additions */
-  HB_SCRIPT_AVESTAN                 = HB_TAG ('A','v','s','t'),
-  HB_SCRIPT_BAMUM                   = HB_TAG ('B','a','m','u'),
-  HB_SCRIPT_EGYPTIAN_HIEROGLYPHS    = HB_TAG ('E','g','y','p'),
-  HB_SCRIPT_IMPERIAL_ARAMAIC        = HB_TAG ('A','r','m','i'),
-  HB_SCRIPT_INSCRIPTIONAL_PAHLAVI   = HB_TAG ('P','h','l','i'),
-  HB_SCRIPT_INSCRIPTIONAL_PARTHIAN  = HB_TAG ('P','r','t','i'),
-  HB_SCRIPT_JAVANESE                = HB_TAG ('J','a','v','a'),
-  HB_SCRIPT_KAITHI                  = HB_TAG ('K','t','h','i'),
-  HB_SCRIPT_LISU                    = HB_TAG ('L','i','s','u'),
-  HB_SCRIPT_MEETEI_MAYEK            = HB_TAG ('M','t','e','i'),
-  HB_SCRIPT_OLD_SOUTH_ARABIAN       = HB_TAG ('S','a','r','b'),
-  HB_SCRIPT_OLD_TURKIC              = HB_TAG ('O','r','k','h'),
-  HB_SCRIPT_SAMARITAN               = HB_TAG ('S','a','m','r'),
-  HB_SCRIPT_TAI_THAM                = HB_TAG ('L','a','n','a'),
-  HB_SCRIPT_TAI_VIET                = HB_TAG ('T','a','v','t'),
+  HB_SCRIPT_AVESTAN                    = HB_TAG ('A','v','s','t'),
+  HB_SCRIPT_BAMUM                      = HB_TAG ('B','a','m','u'),
+  HB_SCRIPT_EGYPTIAN_HIEROGLYPHS       = HB_TAG ('E','g','y','p'),
+  HB_SCRIPT_IMPERIAL_ARAMAIC           = HB_TAG ('A','r','m','i'),
+  HB_SCRIPT_INSCRIPTIONAL_PAHLAVI      = HB_TAG ('P','h','l','i'),
+  HB_SCRIPT_INSCRIPTIONAL_PARTHIAN     = HB_TAG ('P','r','t','i'),
+  HB_SCRIPT_JAVANESE                   = HB_TAG ('J','a','v','a'),
+  HB_SCRIPT_KAITHI                     = HB_TAG ('K','t','h','i'),
+  HB_SCRIPT_LISU                       = HB_TAG ('L','i','s','u'),
+  HB_SCRIPT_MEETEI_MAYEK               = HB_TAG ('M','t','e','i'),
+  HB_SCRIPT_OLD_SOUTH_ARABIAN          = HB_TAG ('S','a','r','b'),
+  HB_SCRIPT_OLD_TURKIC                 = HB_TAG ('O','r','k','h'),
+  HB_SCRIPT_SAMARITAN                  = HB_TAG ('S','a','m','r'),
+  HB_SCRIPT_TAI_THAM                   = HB_TAG ('L','a','n','a'),
+  HB_SCRIPT_TAI_VIET                   = HB_TAG ('T','a','v','t'),
 
   /* Unicode-6.0 additions */
-  HB_SCRIPT_BATAK                   = HB_TAG ('B','a','t','k'),
-  HB_SCRIPT_BRAHMI                  = HB_TAG ('B','r','a','h'),
-  HB_SCRIPT_MANDAIC                 = HB_TAG ('M','a','n','d'),
+  HB_SCRIPT_BATAK                      = HB_TAG ('B','a','t','k'),
+  HB_SCRIPT_BRAHMI                     = HB_TAG ('B','r','a','h'),
+  HB_SCRIPT_MANDAIC                    = HB_TAG ('M','a','n','d'),
+
+  /* Unicode-6.1 additions */
+  HB_SCRIPT_CHAKMA                     = HB_TAG ('C','a','k','m'),
+  HB_SCRIPT_MEROITIC_CURSIVE           = HB_TAG ('M','e','r','c'),
+  HB_SCRIPT_MEROITIC_HIEROGLYPHS       = HB_TAG ('M','e','r','o'),
+  HB_SCRIPT_MIAO                       = HB_TAG ('P','l','r','d'),
+  HB_SCRIPT_SHARADA                    = HB_TAG ('S','h','r','d'),
+  HB_SCRIPT_SORA_SOMPENG               = HB_TAG ('S','o','r','a'),
+  HB_SCRIPT_TAKRI                      = HB_TAG ('T','a','k','r'),
 
   /* No script set */
-  HB_SCRIPT_INVALID                 = HB_TAG_NONE
+  HB_SCRIPT_INVALID                    = HB_TAG_NONE
 } hb_script_t;
 
 
@@ -293,8 +320,9 @@ hb_script_t
 hb_script_from_iso15924_tag (hb_tag_t tag);
 
 /* suger for tag_from_string() then script_from_iso15924_tag */
+/* len=-1 means s is NUL-terminated */
 hb_script_t
-hb_script_from_string (const char *s);
+hb_script_from_string (const char *s, int len);
 
 hb_tag_t
 hb_script_to_iso15924_tag (hb_script_t script);
@@ -306,6 +334,7 @@ hb_script_get_horizontal_direction (hb_script_t script);
 /* User data */
 
 typedef struct _hb_user_data_key_t {
+  /*< private >*/
   char unused;
 } hb_user_data_key_t;
 
diff --git a/src/hb-fallback-shape-private.hh b/src/hb-fallback-shape-private.hh
new file mode 100644 (file)
index 0000000..159456d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2011  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_FALLBACK_SHAPE_PRIVATE_HH
+#define HB_FALLBACK_SHAPE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-shape.h"
+
+
+HB_BEGIN_DECLS
+
+
+HB_INTERNAL hb_bool_t
+_hb_fallback_shape (hb_font_t          *font,
+                   hb_buffer_t        *buffer,
+                   const hb_feature_t *features,
+                   unsigned int        num_features);
+
+
+HB_END_DECLS
+
+#endif /* HB_FALLBACK_SHAPE_PRIVATE_HH */
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
new file mode 100644 (file)
index 0000000..5939887
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2011  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-fallback-shape-private.hh"
+
+#include "hb-buffer-private.hh"
+
+hb_bool_t
+_hb_fallback_shape (hb_font_t          *font,
+                   hb_buffer_t        *buffer,
+                   const hb_feature_t *features HB_UNUSED,
+                   unsigned int        num_features HB_UNUSED)
+{
+  buffer->guess_properties ();
+
+  unsigned int count = buffer->len;
+
+  for (unsigned int i = 0; i < count; i++)
+    hb_font_get_glyph (font, buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
+
+  buffer->clear_positions ();
+
+  for (unsigned int i = 0; i < count; i++) {
+    hb_font_get_glyph_advance_for_direction (font, buffer->info[i].codepoint,
+                                            buffer->props.direction,
+                                            &buffer->pos[i].x_advance,
+                                            &buffer->pos[i].y_advance);
+    hb_font_subtract_glyph_origin_for_direction (font, buffer->info[i].codepoint,
+                                                buffer->props.direction,
+                                                &buffer->pos[i].x_offset,
+                                                &buffer->pos[i].y_offset);
+  }
+
+  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    hb_buffer_reverse (buffer);
+
+  return true;
+}
index dd71b67..91a4304 100644 (file)
@@ -34,7 +34,6 @@
 #include "hb-font.h"
 #include "hb-object-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -51,10 +50,13 @@ HB_BEGIN_DECLS
   HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \
   HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
   HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_name) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
   /* ^--- Add new callbacks here */
 
 struct _hb_font_funcs_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
@@ -86,15 +88,17 @@ struct _hb_font_funcs_t {
 
 struct _hb_face_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
-  hb_get_table_func_t  get_table;
-  void                *user_data;
-  hb_destroy_func_t    destroy;
+  hb_reference_table_func_t  reference_table;
+  void                      *user_data;
+  hb_destroy_func_t          destroy;
 
   struct hb_ot_layout_t *ot_layout;
 
+  unsigned int index;
   unsigned int upem;
 };
 
@@ -105,6 +109,7 @@ struct _hb_face_t {
 
 struct _hb_font_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
@@ -155,10 +160,9 @@ struct _hb_font_t {
 
 
   private:
-  inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / this->face->upem; }
+  inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); }
 };
 
 
-HB_END_DECLS
 
 #endif /* HB_FONT_PRIVATE_HH */
index 0406e10..109caff 100644 (file)
 #include "hb-font-private.hh"
 #include "hb-blob.h"
 #include "hb-open-file-private.hh"
+#include "hb-ot-head-table.hh"
+
+#include "hb-cache-private.hh"
 
 #include <string.h>
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -42,7 +44,7 @@ HB_BEGIN_DECLS
  */
 
 static hb_bool_t
-hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_nil (hb_font_t *font,
                       void *font_data HB_UNUSED,
                       hb_codepoint_t unicode,
                       hb_codepoint_t variation_selector,
@@ -53,11 +55,11 @@ hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
     return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
 
   *glyph = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_position_t
-hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_advance_nil (hb_font_t *font,
                                 void *font_data HB_UNUSED,
                                 hb_codepoint_t glyph,
                                 void *user_data HB_UNUSED)
@@ -69,7 +71,7 @@ hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
 }
 
 static hb_position_t
-hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_advance_nil (hb_font_t *font,
                                 void *font_data HB_UNUSED,
                                 hb_codepoint_t glyph,
                                 void *user_data HB_UNUSED)
@@ -81,7 +83,7 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
 }
 
 static hb_bool_t
-hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_origin_nil (hb_font_t *font,
                                void *font_data HB_UNUSED,
                                hb_codepoint_t glyph,
                                hb_position_t *x,
@@ -89,20 +91,18 @@ hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
                                void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent,
-                                               glyph,
-                                               x, y);
+    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_bool_t
-hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_origin_nil (hb_font_t *font,
                                void *font_data HB_UNUSED,
                                hb_codepoint_t glyph,
                                hb_position_t *x,
@@ -110,20 +110,18 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
                                void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent,
-                                               glyph,
-                                               x, y);
+    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_position_t
-hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_kerning_nil (hb_font_t *font,
                                 void *font_data HB_UNUSED,
                                 hb_codepoint_t left_glyph,
                                 hb_codepoint_t right_glyph,
@@ -136,7 +134,7 @@ hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
 }
 
 static hb_position_t
-hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_kerning_nil (hb_font_t *font,
                                 void *font_data HB_UNUSED,
                                 hb_codepoint_t top_glyph,
                                 hb_codepoint_t bottom_glyph,
@@ -149,7 +147,7 @@ hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
 }
 
 static hb_bool_t
-hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_extents_nil (hb_font_t *font,
                               void *font_data HB_UNUSED,
                               hb_codepoint_t glyph,
                               hb_glyph_extents_t *extents,
@@ -167,11 +165,11 @@ hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
   }
 
   memset (extents, 0, sizeof (*extents));
-  return FALSE;
+  return false;
 }
 
 static hb_bool_t
-hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_contour_point_nil (hb_font_t *font,
                                     void *font_data HB_UNUSED,
                                     hb_codepoint_t glyph,
                                     unsigned int point_index,
@@ -180,23 +178,49 @@ hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
                                     void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent,
-                                                    glyph, point_index,
-                                                    x, y);
+    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, glyph, point_index, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_name_nil (hb_font_t *font,
+                           void *font_data HB_UNUSED,
+                           hb_codepoint_t glyph,
+                           char *name, unsigned int size,
+                           void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return hb_font_get_glyph_name (font->parent, glyph, name, size);
+
+  snprintf (name, size, "gid%u", glyph);
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_from_name_nil (hb_font_t *font,
+                                void *font_data HB_UNUSED,
+                                const char *name, int len, /* -1 means nul-terminated */
+                                hb_codepoint_t *glyph,
+                                void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return hb_font_get_glyph_from_name (font->parent, name, len, glyph);
+
+  *glyph = 0;
+  return false;
 }
 
 
-static hb_font_funcs_t _hb_font_funcs_nil = {
+static const hb_font_funcs_t _hb_font_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
 
-  TRUE, /* immutable */
+  true, /* immutable */
 
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
@@ -212,7 +236,7 @@ hb_font_funcs_create (void)
   hb_font_funcs_t *ffuncs;
 
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
-    return &_hb_font_funcs_nil;
+    return hb_font_funcs_get_empty ();
 
   ffuncs->get = _hb_font_funcs_nil.get;
 
@@ -222,7 +246,7 @@ hb_font_funcs_create (void)
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
-  return &_hb_font_funcs_nil;
+  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil);
 }
 
 hb_font_funcs_t *
@@ -248,9 +272,10 @@ hb_bool_t
 hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
                             hb_user_data_key_t *key,
                             void *              data,
-                            hb_destroy_func_t   destroy)
+                            hb_destroy_func_t   destroy,
+                            hb_bool_t           replace)
 {
-  return hb_object_set_user_data (ffuncs, key, data, destroy);
+  return hb_object_set_user_data (ffuncs, key, data, destroy, replace);
 }
 
 void *
@@ -267,7 +292,7 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
   if (hb_object_is_inert (ffuncs))
     return;
 
-  ffuncs->immutable = TRUE;
+  ffuncs->immutable = true;
 }
 
 hb_bool_t
@@ -285,8 +310,11 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t             *ffuncs,    \
                                  void                        *user_data, \
                                  hb_destroy_func_t            destroy)   \
 {                                                                        \
-  if (ffuncs->immutable)                                                 \
+  if (ffuncs->immutable) {                                               \
+    if (destroy)                                                         \
+      destroy (user_data);                                               \
     return;                                                              \
+  }                                                                      \
                                                                          \
   if (ffuncs->destroy.name)                                              \
     ffuncs->destroy.name (ffuncs->user_data.name);                       \
@@ -399,6 +427,28 @@ hb_font_get_glyph_contour_point (hb_font_t *font,
                                               font->klass->user_data.glyph_contour_point);
 }
 
+hb_bool_t
+hb_font_get_glyph_name (hb_font_t *font,
+                       hb_codepoint_t glyph,
+                       char *name, unsigned int size)
+{
+  return font->klass->get.glyph_name (font, font->user_data,
+                                     glyph,
+                                     name, size,
+                                     font->klass->user_data.glyph_name);
+}
+
+hb_bool_t
+hb_font_get_glyph_from_name (hb_font_t *font,
+                            const char *name, int len, /* -1 means nul-terminated */
+                            hb_codepoint_t *glyph)
+{
+  return font->klass->get.glyph_from_name (font, font->user_data,
+                                          name, len,
+                                          glyph,
+                                          font->klass->user_data.glyph_from_name);
+}
+
 
 /* A bit higher-level, and with fallback */
 
@@ -528,41 +578,42 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
  * hb_face_t
  */
 
-static hb_face_t _hb_face_nil = {
+static const hb_face_t _hb_face_nil = {
   HB_OBJECT_HEADER_STATIC,
 
-  TRUE, /* immutable */
+  true, /* immutable */
 
-  NULL, /* get_table */
+  NULL, /* reference_table */
   NULL, /* user_data */
   NULL, /* destroy */
 
   NULL, /* ot_layout */
 
-  1000
+  0,    /* index */
+  1000  /* upem */
 };
 
 
 hb_face_t *
-hb_face_create_for_tables (hb_get_table_func_t  get_table,
-                          void                *user_data,
-                          hb_destroy_func_t    destroy)
+hb_face_create_for_tables (hb_reference_table_func_t  reference_table,
+                          void                      *user_data,
+                          hb_destroy_func_t          destroy)
 {
   hb_face_t *face;
 
-  if (!get_table || !(face = hb_object_create<hb_face_t> ())) {
+  if (!reference_table || !(face = hb_object_create<hb_face_t> ())) {
     if (destroy)
       destroy (user_data);
-    return &_hb_face_nil;
+    return hb_face_get_empty ();
   }
 
-  face->get_table = get_table;
+  face->reference_table = reference_table;
   face->user_data = user_data;
   face->destroy = destroy;
 
   face->ot_layout = _hb_ot_layout_create (face);
 
-  face->upem = _hb_ot_layout_get_upem (face);
+  face->upem = 0;
 
   return face;
 }
@@ -596,10 +647,13 @@ _hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure)
 }
 
 static hb_blob_t *
-_hb_face_for_data_get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data;
 
+  if (tag == HB_TAG_NONE)
+    return hb_blob_reference (data->blob);
+
   const OpenTypeFontFile &ot_file = *Sanitizer<OpenTypeFontFile>::lock_instance (data->blob);
   const OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
 
@@ -614,23 +668,29 @@ hb_face_t *
 hb_face_create (hb_blob_t    *blob,
                unsigned int  index)
 {
+  hb_face_t *face;
+
   if (unlikely (!blob || !hb_blob_get_length (blob)))
-    return &_hb_face_nil;
+    return hb_face_get_empty ();
 
   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_get_empty ();
 
-  return hb_face_create_for_tables (_hb_face_for_data_get_table,
+  face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
                                    closure,
                                    (hb_destroy_func_t) _hb_face_for_data_closure_destroy);
+
+  hb_face_set_index (face, index);
+
+  return face;
 }
 
 hb_face_t *
 hb_face_get_empty (void)
 {
-  return &_hb_face_nil;
+  return const_cast<hb_face_t *> (&_hb_face_nil);
 }
 
 
@@ -657,9 +717,10 @@ hb_bool_t
 hb_face_set_user_data (hb_face_t          *face,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy)
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace)
 {
-  return hb_object_set_user_data (face, key, data, destroy);
+  return hb_object_set_user_data (face, key, data, destroy, replace);
 }
 
 void *
@@ -691,45 +752,64 @@ hb_face_reference_table (hb_face_t *face,
 {
   hb_blob_t *blob;
 
-  if (unlikely (!face || !face->get_table))
+  if (unlikely (!face || !face->reference_table))
     return hb_blob_get_empty ();
 
-  blob = face->get_table (face, tag, face->user_data);
+  blob = face->reference_table (face, tag, face->user_data);
   if (unlikely (!blob))
     return hb_blob_get_empty ();
 
   return blob;
 }
 
-unsigned int
-hb_face_get_upem (hb_face_t *face)
+hb_blob_t *
+hb_face_reference_blob (hb_face_t *face)
 {
-  return _hb_ot_layout_get_upem (face);
+  return hb_face_reference_table (face, HB_TAG_NONE);
 }
 
+void
+hb_face_set_index (hb_face_t    *face,
+                  unsigned int  index)
+{
+  if (hb_object_is_inert (face))
+    return;
 
-/*
- * hb_font_t
- */
+  face->index = index;
+}
 
-static hb_font_t _hb_font_nil = {
-  HB_OBJECT_HEADER_STATIC,
+unsigned int
+hb_face_get_index (hb_face_t    *face)
+{
+  return face->index;
+}
 
-  TRUE, /* immutable */
+void
+hb_face_set_upem (hb_face_t    *face,
+                 unsigned int  upem)
+{
+  if (hb_object_is_inert (face))
+    return;
 
-  NULL, /* parent */
-  &_hb_face_nil,
+  face->upem = upem;
+}
 
-  0, /* x_scale */
-  0, /* y_scale */
+unsigned int
+hb_face_get_upem (hb_face_t *face)
+{
+  if (unlikely (!face->upem)) {
+    hb_blob_t *head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
+    const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+    face->upem = head_table->get_upem ();
+    hb_blob_destroy (head_blob);
+  }
+  return face->upem;
+}
 
-  0, /* x_ppem */
-  0, /* y_ppem */
 
-  &_hb_font_funcs_nil, /* klass */
-  NULL, /* user_data */
-  NULL  /* destroy */
-};
+/*
+ * hb_font_t
+ */
 
 hb_font_t *
 hb_font_create (hb_face_t *face)
@@ -737,15 +817,15 @@ hb_font_create (hb_face_t *face)
   hb_font_t *font;
 
   if (unlikely (!face))
-    face = &_hb_face_nil;
+    face = hb_face_get_empty ();
   if (unlikely (hb_object_is_inert (face)))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
   if (!(font = hb_object_create<hb_font_t> ()))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
 
   hb_face_make_immutable (face);
   font->face = hb_face_reference (face);
-  font->klass = &_hb_font_funcs_nil;
+  font->klass = hb_font_funcs_get_empty ();
 
   return font;
 }
@@ -754,7 +834,7 @@ hb_font_t *
 hb_font_create_sub_font (hb_font_t *parent)
 {
   if (unlikely (!parent))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
 
   hb_font_t *font = hb_font_create (parent->face);
 
@@ -769,15 +849,32 @@ hb_font_create_sub_font (hb_font_t *parent)
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
 
-  font->klass = &_hb_font_funcs_nil;
-
   return font;
 }
 
 hb_font_t *
 hb_font_get_empty (void)
 {
-  return &_hb_font_nil;
+  static const hb_font_t _hb_font_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    NULL, /* parent */
+    const_cast<hb_face_t *> (&_hb_face_nil),
+
+    0, /* x_scale */
+    0, /* y_scale */
+
+    0, /* x_ppem */
+    0, /* y_ppem */
+
+    const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
+    NULL, /* user_data */
+    NULL  /* destroy */
+  };
+
+  return const_cast<hb_font_t *> (&_hb_font_nil);
 }
 
 hb_font_t *
@@ -804,9 +901,10 @@ hb_bool_t
 hb_font_set_user_data (hb_font_t          *font,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy)
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace)
 {
-  return hb_object_set_user_data (font, key, data, destroy);
+  return hb_object_set_user_data (font, key, data, destroy, replace);
 }
 
 void *
@@ -850,14 +948,17 @@ hb_font_set_funcs (hb_font_t         *font,
                   void              *user_data,
                   hb_destroy_func_t  destroy)
 {
-  if (font->immutable)
+  if (font->immutable) {
+    if (destroy)
+      destroy (user_data);
     return;
+  }
 
   if (font->destroy)
     font->destroy (font->user_data);
 
   if (!klass)
-    klass = &_hb_font_funcs_nil;
+    klass = hb_font_funcs_get_empty ();
 
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
@@ -866,6 +967,25 @@ hb_font_set_funcs (hb_font_t         *font,
   font->destroy = destroy;
 }
 
+void
+hb_font_set_funcs_data (hb_font_t         *font,
+                       void              *user_data,
+                       hb_destroy_func_t  destroy)
+{
+  /* Destroy user_data? */
+  if (font->immutable) {
+    if (destroy)
+      destroy (user_data);
+    return;
+  }
+
+  if (font->destroy)
+    font->destroy (font->user_data);
+
+  font->user_data = user_data;
+  font->destroy = destroy;
+}
+
 
 void
 hb_font_set_scale (hb_font_t *font,
@@ -910,4 +1030,3 @@ hb_font_get_ppem (hb_font_t *font,
 }
 
 
-HB_END_DECLS
index 37d36b4..b98759b 100644 (file)
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_FONT_H
 #define HB_FONT_H
 
@@ -44,13 +48,13 @@ hb_face_t *
 hb_face_create (hb_blob_t    *blob,
                unsigned int  index);
 
-typedef hb_blob_t * (*hb_get_table_func_t)  (hb_face_t *face, hb_tag_t tag, void *user_data);
+typedef hb_blob_t * (*hb_reference_table_func_t)  (hb_face_t *face, 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,
-                          void                *user_data,
-                          hb_destroy_func_t    destroy);
+hb_face_create_for_tables (hb_reference_table_func_t  reference_table,
+                          void                      *user_data,
+                          hb_destroy_func_t          destroy);
 
 hb_face_t *
 hb_face_get_empty (void);
@@ -65,7 +69,8 @@ hb_bool_t
 hb_face_set_user_data (hb_face_t          *face,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy);
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace);
 
 
 void *
@@ -83,6 +88,20 @@ hb_blob_t *
 hb_face_reference_table (hb_face_t *face,
                         hb_tag_t   tag);
 
+hb_blob_t *
+hb_face_reference_blob (hb_face_t *face);
+
+void
+hb_face_set_index (hb_face_t    *face,
+                  unsigned int  index);
+
+unsigned int
+hb_face_get_index (hb_face_t    *face);
+
+void
+hb_face_set_upem (hb_face_t    *face,
+                 unsigned int  upem);
+
 unsigned int
 hb_face_get_upem (hb_face_t *face);
 
@@ -109,7 +128,8 @@ hb_bool_t
 hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
                             hb_user_data_key_t *key,
                             void *              data,
-                            hb_destroy_func_t   destroy);
+                            hb_destroy_func_t   destroy,
+                            hb_bool_t           replace);
 
 
 void *
@@ -172,6 +192,16 @@ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, vo
                                                             void *user_data);
 
 
+typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data,
+                                                   hb_codepoint_t glyph,
+                                                   char *name, unsigned int size,
+                                                   void *user_data);
+typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data,
+                                                        const char *name, int len, /* -1 means nul-terminated */
+                                                        hb_codepoint_t *glyph,
+                                                        void *user_data);
+
+
 /* func setters */
 
 void
@@ -215,6 +245,15 @@ hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs,
                                            hb_font_get_glyph_contour_point_func_t func,
                                            void *user_data, hb_destroy_func_t destroy);
 
+void
+hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs,
+                                  hb_font_get_glyph_name_func_t glyph_func,
+                                  void *user_data, hb_destroy_func_t destroy);
+void
+hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
+                                       hb_font_get_glyph_from_name_func_t glyph_func,
+                                       void *user_data, hb_destroy_func_t destroy);
+
 
 /* func dispatch */
 
@@ -256,6 +295,15 @@ hb_font_get_glyph_contour_point (hb_font_t *font,
                                 hb_codepoint_t glyph, unsigned int point_index,
                                 hb_position_t *x, hb_position_t *y);
 
+hb_bool_t
+hb_font_get_glyph_name (hb_font_t *font,
+                       hb_codepoint_t glyph,
+                       char *name, unsigned int size);
+hb_bool_t
+hb_font_get_glyph_from_name (hb_font_t *font,
+                            const char *name, int len, /* -1 means nul-terminated */
+                            hb_codepoint_t *glyph);
+
 
 /* high-level funcs, with fallback */
 
@@ -324,7 +372,8 @@ hb_bool_t
 hb_font_set_user_data (hb_font_t          *font,
                       hb_user_data_key_t *key,
                       void *              data,
-                      hb_destroy_func_t   destroy);
+                      hb_destroy_func_t   destroy,
+                      hb_bool_t           replace);
 
 
 void *
@@ -350,6 +399,12 @@ hb_font_set_funcs (hb_font_t         *font,
                   void              *font_data,
                   hb_destroy_func_t  destroy);
 
+/* Be *very* careful with this function! */
+void
+hb_font_set_funcs_data (hb_font_t         *font,
+                       void              *font_data,
+                       hb_destroy_func_t  destroy);
+
 
 void
 hb_font_set_scale (hb_font_t *font,
index 4a180ee..0589c9e 100644 (file)
 
 #include "hb-font-private.hh"
 
+#include FT_ADVANCES_H
 #include FT_TRUETYPE_TABLES_H
 
-HB_BEGIN_DECLS
+
+
+#ifndef HB_DEBUG_FT
+#define HB_DEBUG_FT (HB_DEBUG+0)
+#endif
 
 
 /* TODO:
@@ -43,15 +48,21 @@ HB_BEGIN_DECLS
  *
  *   - We don't handle any load_flags.  That definitely has API implications. :(
  *     I believe hb_ft_font_create() should take load_flags input.
+ *     In particular, FT_Get_Advance() without the NO_HINTING flag seems to be
+ *     buggy.
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
+ *   - Rounding, etc?
+ *
  *   - In the future, we should add constructors to create fonts in font space.
  *
  *   - I believe transforms are not correctly implemented.  FreeType does not
  *     provide any API to get to the transform/delta set on the face. :(
  *
  *   - Always use FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH?
+ *
+ *   - FT_Load_Glyph() is exteremely costly.  Do something about it?
  */
 
 
@@ -70,7 +81,7 @@ hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
   if (unlikely (variation_selector)) {
     *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
     if (*glyph)
-      return TRUE;
+      return true;
   }
 #endif
 
@@ -85,12 +96,13 @@ hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
                           void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT;
+  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
+  FT_Fixed v;
 
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+  if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
     return 0;
 
-  return ft_face->glyph->metrics.horiAdvance;
+  return v >> 10;
 }
 
 static hb_position_t
@@ -100,14 +112,15 @@ hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
                           void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT;
+  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_VERTICAL_LAYOUT;
+  FT_Fixed v;
 
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+  if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
     return 0;
 
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
-  return -ft_face->glyph->metrics.vertAdvance;
+  return -v >> 10;
 }
 
 static hb_bool_t
@@ -119,7 +132,7 @@ hb_ft_get_glyph_h_origin (hb_font_t *font HB_UNUSED,
                          void *user_data HB_UNUSED)
 {
   /* We always work in the horizontal coordinates. */
-  return TRUE;
+  return true;
 }
 
 static hb_bool_t
@@ -134,14 +147,14 @@ hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED,
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-    return FALSE;
+    return false;
 
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
   *x = ft_face->glyph->metrics.horiBearingX -   ft_face->glyph->metrics.vertBearingX;
   *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY);
 
-  return TRUE;
+  return true;
 }
 
 static hb_position_t
@@ -182,13 +195,13 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-    return FALSE;
+    return false;
 
   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;
-  return TRUE;
+  return true;
 }
 
 static hb_bool_t
@@ -204,55 +217,88 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED,
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-      return FALSE;
+      return false;
 
   if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
-      return FALSE;
+      return false;
 
   if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
-      return FALSE;
+      return false;
 
   *x = ft_face->glyph->outline.points[point_index].x;
   *y = ft_face->glyph->outline.points[point_index].y;
 
-  return TRUE;
+  return true;
 }
 
-static hb_font_funcs_t ft_ffuncs = {
-  HB_OBJECT_HEADER_STATIC,
+static hb_bool_t
+hb_ft_get_glyph_name (hb_font_t *font,
+                     void *font_data,
+                     hb_codepoint_t glyph,
+                     char *name, unsigned int size,
+                     void *user_data HB_UNUSED)
+{
+  FT_Face ft_face = (FT_Face) font_data;
 
-  TRUE, /* immutable */
+  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
+  if (!ret)
+    snprintf (name, size, "gid%u", glyph);
 
-  {
-    hb_ft_get_glyph,
-    hb_ft_get_glyph_h_advance,
-    hb_ft_get_glyph_v_advance,
-    hb_ft_get_glyph_h_origin,
-    hb_ft_get_glyph_v_origin,
-    hb_ft_get_glyph_h_kerning,
-    hb_ft_get_glyph_v_kerning,
-    hb_ft_get_glyph_extents,
-    hb_ft_get_glyph_contour_point,
+  return ret;
+}
+
+static hb_bool_t
+hb_ft_get_glyph_from_name (hb_font_t *font,
+                          void *font_data,
+                          const char *name, int len, /* -1 means nul-terminated */
+                          hb_codepoint_t *glyph,
+                          void *user_data HB_UNUSED)
+{
+  FT_Face ft_face = (FT_Face) font_data;
+
+  if (len < 0)
+    *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name);
+  else {
+    /* Make a nul-terminated version. */
+    char buf[128];
+    len = MIN (len, (int) sizeof (buf) - 1);
+    strncpy (buf, name, len);
+    buf[len] = '\0';
+    *glyph = FT_Get_Name_Index (ft_face, buf);
   }
-};
 
-hb_font_funcs_t *
-hb_ft_get_font_funcs (void)
+  return *glyph != 0;
+}
+
+
+static hb_font_funcs_t *
+_hb_ft_get_font_funcs (void)
 {
-  return &ft_ffuncs;
+  static const hb_font_funcs_t ft_ffuncs = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name,
+      HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+    }
+  };
+
+  return const_cast<hb_font_funcs_t *> (&ft_ffuncs);
 }
 
 
 static hb_blob_t *
-get_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+reference_table  (hb_face_t *face HB_UNUSED, 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;
+  /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
   if (error)
@@ -292,9 +338,12 @@ hb_ft_face_create (FT_Face           ft_face,
     face = hb_face_create (blob, ft_face->face_index);
     hb_blob_destroy (blob);
   } else {
-    face = hb_face_create_for_tables (get_table, ft_face, destroy);
+    face = hb_face_create_for_tables (reference_table, ft_face, destroy);
   }
 
+  hb_face_set_index (face, ft_face->face_index);
+  hb_face_set_upem (face, ft_face->units_per_EM);
+
   return face;
 }
 
@@ -319,6 +368,11 @@ hb_ft_face_create_cached (FT_Face ft_face)
   return hb_face_reference ((hb_face_t *) ft_face->generic.data);
 }
 
+static void
+_do_nothing (void)
+{
+}
+
 
 hb_font_t *
 hb_ft_font_create (FT_Face           ft_face,
@@ -331,8 +385,8 @@ hb_ft_font_create (FT_Face           ft_face,
   font = hb_font_create (face);
   hb_face_destroy (face);
   hb_font_set_funcs (font,
-                    hb_ft_get_font_funcs (),
-                    ft_face, NULL);
+                    _hb_ft_get_font_funcs (),
+                    ft_face, (hb_destroy_func_t) _do_nothing);
   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);
@@ -344,4 +398,91 @@ hb_ft_font_create (FT_Face           ft_face,
 }
 
 
-HB_END_DECLS
+/* Thread-safe, lock-free, FT_Library */
+
+static FT_Library ft_library;
+
+static
+void free_ft_library (void)
+{
+  FT_Done_FreeType (ft_library);
+}
+
+static FT_Library
+get_ft_library (void)
+{
+retry:
+  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
+
+  if (unlikely (!library))
+  {
+    /* Not found; allocate one. */
+    if (FT_Init_FreeType (&library))
+      return NULL;
+
+    if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) {
+      FT_Done_FreeType (library);
+      goto retry;
+    }
+
+#ifdef HAVE_ATEXIT
+    atexit (free_ft_library); /* First person registers atexit() callback. */
+#endif
+  }
+
+  return library;
+}
+
+static void
+_release_blob (FT_Face ft_face)
+{
+  hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
+}
+
+void
+hb_ft_font_set_funcs (hb_font_t *font)
+{
+  hb_blob_t *blob = hb_face_reference_blob (font->face);
+  unsigned int blob_length;
+  const char *blob_data = hb_blob_get_data (blob, &blob_length);
+  if (unlikely (!blob_length))
+    DEBUG_MSG (FT, font, "Font face has empty blob");
+
+  FT_Face ft_face = NULL;
+  FT_Error err = FT_New_Memory_Face (get_ft_library (),
+                                    (const FT_Byte *) blob_data,
+                                    blob_length,
+                                    hb_face_get_index (font->face),
+                                    &ft_face);
+
+  if (unlikely (err)) {
+    hb_blob_destroy (blob);
+    DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed");
+    return;
+  }
+
+  FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
+
+  FT_Set_Char_Size (ft_face,
+                   font->x_scale, font->y_scale,
+                   font->x_ppem * 72 * 64 / font->x_scale,
+                   font->y_ppem * 72 * 64 / font->y_scale);
+
+  ft_face->generic.data = blob;
+  ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
+
+  hb_font_set_funcs (font,
+                    _hb_ft_get_font_funcs (),
+                    ft_face,
+                    (hb_destroy_func_t) FT_Done_Face);
+}
+
+FT_Face
+hb_ft_font_get_face (hb_font_t *font)
+{
+  if (font->destroy == (hb_destroy_func_t) FT_Done_Face ||
+      font->destroy == (hb_destroy_func_t) _do_nothing)
+    return (FT_Face) font->user_data;
+
+  return NULL;
+}
index 54cce2a..696251e 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "hb.h"
 
-#include "hb-font.h"
-
 #include <ft2build.h>
 #include FT_FREETYPE_H
 
@@ -38,9 +36,6 @@ HB_BEGIN_DECLS
 
 /* Note: FreeType is not thread-safe.  Hence, these functions are not either. */
 
-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);
@@ -53,6 +48,15 @@ hb_ft_font_create (FT_Face           ft_face,
                   hb_destroy_func_t destroy);
 
 
+
+/* Makes an hb_font_t use FreeType internally to implement font functions. */
+void
+hb_ft_font_set_funcs (hb_font_t *font);
+
+FT_Face
+hb_ft_font_get_face (hb_font_t *font);
+
+
 HB_END_DECLS
 
 #endif /* HB_FT_H */
index 76e1dfd..6b655dd 100644 (file)
@@ -32,9 +32,6 @@
 
 #include "hb-unicode-private.hh"
 
-#include <glib.h>
-
-HB_BEGIN_DECLS
 
 #if !GLIB_CHECK_VERSION(2,29,14)
 static const hb_script_t
@@ -147,7 +144,16 @@ glib_script_to_script[] =
   /* Unicode-6.0 additions */
   HB_SCRIPT_BATAK,
   HB_SCRIPT_BRAHMI,
-  HB_SCRIPT_MANDAIC
+  HB_SCRIPT_MANDAIC,
+
+  /* Unicode-6.1 additions */
+  HB_SCRIPT_CHAKMA,
+  HB_SCRIPT_MEROITIC_CURSIVE,
+  HB_SCRIPT_MEROITIC_HIEROGLYPHS,
+  HB_SCRIPT_MIAO,
+  HB_SCRIPT_SHARADA,
+  HB_SCRIPT_SORA_SOMPENG,
+  HB_SCRIPT_TAKRI
 };
 #endif
 
@@ -245,7 +251,7 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
    * sees it and makes sure it's compilable. */
 
   if (!a || !b)
-    return FALSE;
+    return false;
 
   gchar utf8[12];
   gchar *normalized;
@@ -255,13 +261,15 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
   len = g_unichar_to_utf8 (a, utf8);
   len += g_unichar_to_utf8 (b, utf8 + len);
   normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC);
-
   len = g_utf8_strlen (normalized, -1);
+  if (unlikely (!len))
+    return false;
+
   if (len == 1) {
     *ab = g_utf8_get_char (normalized);
-    ret = TRUE;
+    ret = true;
   } else {
-    ret = FALSE;
+    ret = false;
   }
 
   g_free (normalized);
@@ -289,8 +297,10 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 
   len = g_unichar_to_utf8 (ab, utf8);
   normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD);
-
   len = g_utf8_strlen (normalized, -1);
+  if (unlikely (!len))
+    return false;
+
   if (len == 1) {
     *a = g_utf8_get_char (normalized);
     *b = 0;
@@ -308,7 +318,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
       *b = 0;
     }
     g_free (recomposed);
-    ret = TRUE;
+    ret = true;
   } else {
     /* If decomposed to more than two characters, take the last one,
      * and recompose the rest to get the first component. */
@@ -319,7 +329,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
     /* We expect that recomposed has exactly one character now. */
     *a = g_utf8_get_char (recomposed);
     g_free (recomposed);
-    ret = TRUE;
+    ret = true;
   }
 
   g_free (normalized);
@@ -327,12 +337,12 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 }
 
 
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_glib;
-hb_unicode_funcs_t _hb_glib_unicode_funcs = {
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
+const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
-  TRUE, /* immutable */
+  true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
@@ -343,8 +353,6 @@ hb_unicode_funcs_t _hb_glib_unicode_funcs = {
 hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void)
 {
-  return &_hb_glib_unicode_funcs;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
-
-HB_END_DECLS
index 3bc3ebf..63a9d33 100644 (file)
@@ -30,6 +30,7 @@
 #define HB_GLIB_H
 
 #include "hb.h"
+
 #include <glib.h>
 
 HB_BEGIN_DECLS
diff --git a/src/hb-gobject-enums.cc.tmpl b/src/hb-gobject-enums.cc.tmpl
new file mode 100644 (file)
index 0000000..05abd89
--- /dev/null
@@ -0,0 +1,74 @@
+/*** BEGIN file-header ***/
+/*
+ * Copyright © 2011  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-private.hh"
+
+/* g++ didn't like older gtype.h gcc-only code path. */
+#include <glib.h>
+#if !GLIB_CHECK_VERSION(2,29,16)
+#undef __GNUC__
+#undef __GNUC_MINOR__
+#define __GNUC__ 2
+#define __GNUC_MINOR__ 6
+#endif
+
+#include "hb-gobject.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+inline static /* TODO(behdad) disable these for now until we fix them... */
+GType
+@enum_name@_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
new file mode 100644 (file)
index 0000000..cec4854
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2011  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-private.hh"
+
+/* g++ didn't like older gtype.h gcc-only code path. */
+#include <glib.h>
+#if !GLIB_CHECK_VERSION(2,29,16)
+#undef __GNUC__
+#undef __GNUC_MINOR__
+#define __GNUC__ 2
+#define __GNUC_MINOR__ 6
+#endif
+
+#include "hb-gobject.h"
+
+#define _HB_DEFINE_BOXED_TYPE(Name,underscore_name,copy_func,free_func) \
+GType \
+underscore_name##_get_type (void) \
+{ \
+   static volatile gsize type = 0; \
+   if (g_once_init_enter (&type)) { \
+      GType t = g_boxed_type_register_static (g_intern_static_string (#Name), \
+                                             (GBoxedCopyFunc) copy_func, \
+                                             (GBoxedFreeFunc) free_func); \
+      g_once_init_leave (&type, t); \
+   } \
+   return type; \
+}
+
+#define HB_DEFINE_BOXED_TYPE(name) \
+       _HB_DEFINE_BOXED_TYPE (hb_##name, hb_gobject_##name, hb_##name##_reference, hb_##name##_destroy);
+
+HB_DEFINE_BOXED_TYPE (buffer)
+HB_DEFINE_BOXED_TYPE (blob)
+HB_DEFINE_BOXED_TYPE (face)
+HB_DEFINE_BOXED_TYPE (font)
+HB_DEFINE_BOXED_TYPE (font_funcs)
+HB_DEFINE_BOXED_TYPE (unicode_funcs)
+
diff --git a/src/hb-gobject.h b/src/hb-gobject.h
new file mode 100644 (file)
index 0000000..4f23fdd
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2011  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_GOBJECT_H
+#define HB_GOBJECT_H
+
+#include "hb.h"
+
+#include <glib-object.h>
+
+HB_BEGIN_DECLS
+
+
+/* Objects */
+
+#define HB_GOBJECT_TYPE_BLOB hb_gobject_blob_get_type ()
+GType
+hb_gobject_blob_get_type (void);
+
+#define HB_GOBJECT_TYPE_BUFFER hb_gobject_buffer_get_type ()
+GType
+hb_gobject_buffer_get_type (void);
+
+#define HB_GOBJECT_TYPE_FACE hb_gobject_face_get_type ()
+GType
+hb_gobject_face_get_type (void);
+
+#define HB_GOBJECT_TYPE_FONT hb_gobject_font_get_type ()
+GType
+hb_gobject_font_get_type (void);
+
+#define HB_GOBJECT_TYPE_FONT_FUNCS hb_gobject_font_funcs_get_type ()
+GType
+hb_gobject_font_funcs_get_type (void);
+
+#define HB_GOBJECT_TYPE_UNICODE_FUNCS hb_gobject_unicode_funcs_get_type ()
+GType
+hb_gobject_unicode_funcs_get_type (void);
+
+
+/* Enums */
+
+
+HB_END_DECLS
+
+#endif /* HB_GOBJECT_H */
diff --git a/src/hb-graphite2-private.hh b/src/hb-graphite2-private.hh
new file mode 100644 (file)
index 0000000..644ea75
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2012  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_GRAPHITE2_PRIVATE_HH
+#define HB_GRAPHITE2_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-graphite2.h"
+
+
+HB_INTERNAL hb_bool_t
+_hb_graphite2_shape (hb_font_t          *font,
+                    hb_buffer_t        *buffer,
+                    const hb_feature_t *features,
+                    unsigned int        num_features);
+
+
+#endif /* HB_GRAPHITE2_PRIVATE_HH */
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
new file mode 100644 (file)
index 0000000..3fa9f79
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright © 2011  Martin Hosken
+ * Copyright © 2011  SIL International
+ * Copyright © 2011  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-private.hh"
+
+#include "hb-graphite2.h"
+
+#include "hb-buffer-private.hh"
+#include "hb-font-private.hh"
+#include "hb-ot-tag.h"
+
+#include <graphite2/Font.h>
+#include <graphite2/Segment.h>
+
+
+struct hb_gr_cluster_t {
+  unsigned int base_char;
+  unsigned int num_chars;
+  unsigned int base_glyph;
+  unsigned int num_glyphs;
+};
+
+
+typedef struct hb_gr_tablelist_t {
+  hb_blob_t   *blob;
+  struct hb_gr_tablelist_t *next;
+  unsigned int tag;
+} hb_gr_tablelist_t;
+
+static struct hb_gr_face_data_t {
+  hb_face_t         *face;
+  gr_face           *grface;
+  hb_gr_tablelist_t *tlist;
+} _hb_gr_face_data_nil = {NULL, NULL};
+
+static struct hb_gr_font_data_t {
+  gr_font   *grfont;
+  gr_face   *grface;
+} _hb_gr_font_data_nil = {NULL, NULL};
+
+
+static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len)
+{
+  hb_gr_tablelist_t *pl = NULL, *p;
+  hb_gr_face_data_t *face = (hb_gr_face_data_t *) data;
+  hb_gr_tablelist_t *tlist = face->tlist;
+
+  for (p = tlist; p; p = p->next)
+    if (p->tag == tag ) {
+      unsigned int tlen;
+      const char *d = hb_blob_get_data (p->blob, &tlen);
+      *len = tlen;
+      return d;
+    } else
+      pl = p;
+
+  if (!face->face)
+    return NULL;
+  hb_blob_t *blob = hb_face_reference_table (face->face, tag);
+
+  if (!pl || pl->blob)
+  {
+    p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t));
+    if (!p) {
+      hb_blob_destroy (blob);
+      return NULL;
+    }
+    p->next = NULL;
+    if (pl)
+      pl->next = p;
+    else
+      face->tlist = p;
+    pl = p;
+  }
+  pl->blob = blob;
+  pl->tag = tag;
+
+  unsigned int tlen;
+  const char *d = hb_blob_get_data (blob, &tlen);
+  *len = tlen;
+  return d;
+}
+
+static float hb_gr_get_advance (const void *hb_font, unsigned short gid)
+{
+  return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid);
+}
+
+static void _hb_gr_face_data_destroy (void *data)
+{
+  hb_gr_face_data_t *f = (hb_gr_face_data_t *) data;
+  hb_gr_tablelist_t *tlist = f->tlist;
+  while (tlist)
+  {
+    hb_gr_tablelist_t *old = tlist;
+    hb_blob_destroy (tlist->blob);
+    tlist = tlist->next;
+    free (old);
+  }
+  gr_face_destroy (f->grface);
+}
+
+static void _hb_gr_font_data_destroy (void *data)
+{
+  hb_gr_font_data_t *f = (hb_gr_font_data_t *) data;
+
+  gr_font_destroy (f->grfont);
+  free (f);
+}
+
+static hb_user_data_key_t hb_gr_data_key;
+
+static hb_gr_face_data_t *
+_hb_gr_face_get_data (hb_face_t *face)
+{
+  hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
+  if (likely (data)) return data;
+
+  data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t));
+  if (unlikely (!data))
+    return &_hb_gr_face_data_nil;
+
+
+  hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf);
+  if (!hb_blob_get_length (silf_blob))
+  {
+    hb_blob_destroy (silf_blob);
+    return &_hb_gr_face_data_nil;
+  }
+
+  data->face = face;
+  data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default);
+
+
+  if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data,
+                                       (hb_destroy_func_t) _hb_gr_face_data_destroy,
+                                       false)))
+  {
+    _hb_gr_face_data_destroy (data);
+    data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
+    if (data)
+      return data;
+    else
+      return &_hb_gr_face_data_nil;
+  }
+
+  return data;
+}
+
+static hb_gr_font_data_t *
+_hb_gr_font_get_data (hb_font_t *font)
+{
+  hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
+  if (likely (data)) return data;
+
+  data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t));
+  if (unlikely (!data))
+    return &_hb_gr_font_data_nil;
+
+
+  hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf);
+  if (!hb_blob_get_length (silf_blob))
+  {
+    hb_blob_destroy (silf_blob);
+    return &_hb_gr_font_data_nil;
+  }
+
+  data->grface = _hb_gr_face_get_data (font->face)->grface;
+  int scale;
+  hb_font_get_scale (font, &scale, NULL);
+  data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance, data->grface);
+
+
+  if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data,
+                                       (hb_destroy_func_t) _hb_gr_font_data_destroy,
+                                       false)))
+  {
+    _hb_gr_font_data_destroy (data);
+    data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
+    if (data)
+      return data;
+    else
+      return &_hb_gr_font_data_nil;
+  }
+
+  return data;
+}
+
+
+hb_bool_t
+_hb_graphite_shape (hb_font_t          *font,
+                  hb_buffer_t        *buffer,
+                  const hb_feature_t *features,
+                  unsigned int        num_features)
+{
+
+  buffer->guess_properties ();
+
+  /* XXX We do a hell of a lot of stuff just to figure out this font
+   * is not graphite!  Shouldn't do. */
+
+  hb_gr_font_data_t *data = _hb_gr_font_get_data (font);
+  if (!data->grface) return false;
+
+  unsigned int charlen;
+  hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen);
+
+  int success = 0;
+
+  if (!charlen) return true;
+
+  const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
+  const char *lang_end = strchr (lang, '-');
+  int lang_len = lang_end ? lang_end - lang : -1;
+  gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
+
+  while (num_features--)
+  {
+    const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag);
+    if (fref)
+      gr_fref_set_feature_value (fref, features->value, feats);
+    features++;
+  }
+
+  hb_codepoint_t *gids = NULL, *pg;
+  hb_gr_cluster_t *clusters = NULL;
+  gr_segment *seg = NULL;
+  uint32_t *text = NULL;
+  const gr_slot *is;
+  unsigned int ci = 0, ic = 0;
+  float curradvx = 0., curradvy = 0.;
+  unsigned int glyphlen = 0;
+  unsigned int *p;
+
+  text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t));
+  if (!text) goto dieout;
+
+  p = text;
+  for (unsigned int i = 0; i < charlen; ++i)
+    *p++ = bufferi++->codepoint;
+  *p = 0;
+
+  hb_tag_t script_tag[2];
+  hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
+
+  seg = gr_make_seg (data->grfont, data->grface,
+                    script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
+                    feats,
+                    gr_utf32, text, charlen,
+                    2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
+  if (!seg) goto dieout;
+
+  glyphlen = gr_seg_n_slots (seg);
+  clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t));
+  if (!glyphlen || !clusters) goto dieout;
+
+  gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t));
+  if (!gids) goto dieout;
+
+  pg = gids;
+  for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
+  {
+    unsigned int before = gr_slot_before (is);
+    unsigned int after = gr_slot_after (is);
+    *pg = gr_slot_gid (is);
+    pg++;
+    while (clusters[ci].base_char > before && ci)
+    {
+      clusters[ci-1].num_chars += clusters[ci].num_chars;
+      clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
+      ci--;
+    }
+
+    if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
+    {
+      hb_gr_cluster_t *c = clusters + ci + 1;
+      c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
+      c->num_chars = before - c->base_char;
+      c->base_glyph = ic;
+      c->num_glyphs = 0;
+      ci++;
+    }
+    clusters[ci].num_glyphs++;
+
+    if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
+       clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
+  }
+  ci++;
+
+  buffer->clear_output ();
+  for (unsigned int i = 0; i < ci; ++i)
+    buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph);
+  buffer->swap_buffers ();
+
+  hb_glyph_position_t *pPos;
+  for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
+       is; pPos++, is = gr_slot_next_in_segment (is))
+  {
+    pPos->x_offset = gr_slot_origin_X(is) - curradvx;
+    pPos->y_offset = gr_slot_origin_Y(is) - curradvy;
+    pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont);
+    pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont);
+//    if (pPos->x_advance < 0 && gr_slot_attached_to(is))
+//      pPos->x_advance = 0;
+    curradvx += pPos->x_advance;
+    curradvy += pPos->y_advance;
+  }
+  pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
+
+  /* TODO(behdad):
+   * This shaper is badly broken with RTL text.  It returns glyphs
+   * in the logical order!
+   */
+//  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+//    hb_buffer_reverse (buffer);
+
+  success = 1;
+
+dieout:
+  if (gids) free (gids);
+  if (clusters) free (clusters);
+  if (seg) gr_seg_destroy (seg);
+  if (text) free (text);
+  return success;
+}
diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h
new file mode 100644 (file)
index 0000000..2d16cc8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011  Martin Hosken
+ * Copyright (C) 2011  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.
+ */
+
+#ifndef HB_GRAPHITE2_H
+#define HB_GRAPHITE2_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f')
+
+/* TODO add gr_font/face etc getters and other glue API */
+
+HB_END_DECLS
+
+#endif /* HB_GRAPHITE2_H */
index 7fe78d2..aead6dd 100644 (file)
@@ -36,9 +36,8 @@
 #include <unicode/uversion.h>
 #include <unicode/uchar.h>
 #include <unicode/unorm.h>
-#include <unicode/unistr.h>
+#include <unicode/ustring.h>
 
-HB_BEGIN_DECLS
 
 
 hb_script_t
@@ -47,7 +46,7 @@ hb_icu_script_to_script (UScriptCode script)
   if (unlikely (script == USCRIPT_INVALID_CODE))
     return HB_SCRIPT_INVALID;
 
-  return hb_script_from_string (uscript_getShortName (script));
+  return hb_script_from_string (uscript_getShortName (script), -1);
 }
 
 UScriptCode
@@ -159,7 +158,7 @@ hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED,
   UErrorCode status = U_ZERO_ERROR;
   UScriptCode scriptCode = uscript_getScript(unicode, &status);
 
-  if (unlikely (status != U_ZERO_ERROR))
+  if (unlikely (U_FAILURE (status)))
     return HB_SCRIPT_UNKNOWN;
 
   return hb_icu_script_to_script (scriptCode);
@@ -173,30 +172,29 @@ hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
                        void               *user_data HB_UNUSED)
 {
   if (!a || !b)
-    return FALSE;
+    return false;
 
   UChar utf16[4], normalized[5];
-  gint len;
+  int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
   len = 0;
-  err = FALSE;
+  err = false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
-  if (err) return FALSE;
+  if (err) return false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
-  if (err) return FALSE;
+  if (err) return false;
 
   icu_err = U_ZERO_ERROR;
   len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
-  if (icu_err)
-    return FALSE;
-  normalized[len] = 0;
-  if (u_strlen (normalized) == 1) {
+  if (U_FAILURE (icu_err))
+    return false;
+  if (u_countChar32 (normalized, len) == 1) {
     U16_GET_UNSAFE (normalized, 0, *ab);
-    ret = TRUE;
+    ret = true;
   } else {
-    ret = FALSE;
+    ret = false;
   }
 
   return ret;
@@ -210,7 +208,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
                          void               *user_data HB_UNUSED)
 {
   UChar utf16[2], normalized[20];
-  gint len;
+  int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
@@ -219,17 +217,16 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
   /* Watchout for the dragons.  Err, watchout for macros changing len. */
 
   len = 0;
-  err = FALSE;
+  err = false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err);
-  if (err) return FALSE;
+  if (err) return false;
 
   icu_err = U_ZERO_ERROR;
   len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
-  if (icu_err)
-    return FALSE;
+  if (U_FAILURE (icu_err))
+    return false;
 
-  normalized[len] = 0;
-  len = u_strlen (normalized);
+  len = u_countChar32 (normalized, len);
 
   if (len == 1) {
     U16_GET_UNSAFE (normalized, 0, *a);
@@ -246,15 +243,15 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
     UChar recomposed[20];
     icu_err = U_ZERO_ERROR;
     unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
-    if (icu_err)
-      return FALSE;
+    if (U_FAILURE (icu_err))
+      return false;
     hb_codepoint_t c;
     U16_GET_UNSAFE (recomposed, 0, c);
     if (c != *a && c != ab) {
       *a = c;
       *b = 0;
     }
-    ret = TRUE;
+    ret = true;
   } else {
     /* If decomposed to more than two characters, take the last one,
      * and recompose the rest to get the first component. */
@@ -262,22 +259,23 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
     UChar recomposed[20];
     icu_err = U_ZERO_ERROR;
     len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
-    if (icu_err)
-      return FALSE;
+    if (U_FAILURE (icu_err))
+      return false;
     /* We expect that recomposed has exactly one character now. */
     U16_GET_UNSAFE (recomposed, 0, *a);
-    ret = TRUE;
+    ret = true;
   }
 
   return ret;
 }
 
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_icu;
-hb_unicode_funcs_t _hb_icu_unicode_funcs = {
+
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
+const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
-  TRUE, /* immutable */
+  true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
@@ -288,8 +286,7 @@ hb_unicode_funcs_t _hb_icu_unicode_funcs = {
 hb_unicode_funcs_t *
 hb_icu_get_unicode_funcs (void)
 {
-  return &_hb_icu_unicode_funcs;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
 }
 
 
-HB_END_DECLS
index ecabec2..d22a8e1 100644 (file)
@@ -30,6 +30,7 @@
 #define HB_ICU_H
 
 #include "hb.h"
+
 #include <unicode/uscript.h>
 
 
index 91c9438..f9bd679 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright © 2007  Chris Wilson
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 
 #include "hb-private.hh"
 
-HB_BEGIN_DECLS
-
 
 /* mutex */
 
 /* We need external help for these */
 
-#ifdef HAVE_GLIB
+#if 0
+
+
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+
+#include <windows.h>
+typedef CRITICAL_SECTION hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT     { NULL, 0, 0, NULL, NULL, 0 }
+#define hb_mutex_impl_init(M)  InitializeCriticalSection (M)
+#define hb_mutex_impl_lock(M)  EnterCriticalSection (M)
+#define hb_mutex_impl_unlock(M)        LeaveCriticalSection (M)
+#define hb_mutex_impl_finish(M)        DeleteCriticalSection (M)
 
-#include <glib.h>
 
+#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__))
+
+#include <pthread.h>
+typedef pthread_mutex_t hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT     PTHREAD_MUTEX_INITIALIZER
+#define hb_mutex_impl_init(M)  pthread_mutex_init (M, NULL)
+#define hb_mutex_impl_lock(M)  pthread_mutex_lock (M)
+#define hb_mutex_impl_unlock(M)        pthread_mutex_unlock (M)
+#define hb_mutex_impl_finish(M)        pthread_mutex_destroy (M)
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
 typedef GStaticMutex hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT     G_STATIC_MUTEX_INIT
 #define hb_mutex_impl_init(M)  g_static_mutex_init (M)
 #define hb_mutex_impl_lock(M)  g_static_mutex_lock (M)
 #define hb_mutex_impl_unlock(M)        g_static_mutex_unlock (M)
-#define hb_mutex_impl_free(M)  g_static_mutex_free (M)
+#define hb_mutex_impl_finish(M)        g_static_mutex_free (M)
 
 
-#elif defined(_MSC_VER)
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
-#include <Windows.h>
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
 
-typedef CRITICAL_SECTION hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT     { NULL, 0, 0, NULL, NULL, 0 }
-#define hb_mutex_impl_init(M)  InitializeCriticalSection (M)
-#define hb_mutex_impl_lock(M)  EnterCriticalSection (M)
-#define hb_mutex_impl_unlock(M)        LeaveCriticalSection (M)
-#define hb_mutex_impl_free(M)  DeleteCriticalSection (M)
+/* This actually is not a totally awful implementation. */
+typedef volatile int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT     0
+#define hb_mutex_impl_init(M)  *(M) = 0
+#define hb_mutex_impl_lock(M)  HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END
+#define hb_mutex_impl_unlock(M)        __sync_lock_release (M)
+#define hb_mutex_impl_finish(M)        HB_STMT_START {} HB_STMT_END
 
 
-#else
+#elif !defined(HB_NO_MT)
 
-#warning "Could not find any system to define platform macros, library will NOT be thread-safe"
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
 
+#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */
 typedef volatile int hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT     0
-#define hb_mutex_impl_init(M)  ((void) (*(M) = 0))
-#define hb_mutex_impl_lock(M)  ((void) (*(M) = 1))
-#define hb_mutex_impl_unlock(M)        ((void) (*(M) = 0))
-#define hb_mutex_impl_free(M)  ((void) (*(M) = 2))
+#define hb_mutex_impl_init(M)  *(M) = 0
+#define hb_mutex_impl_lock(M)  HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END
+#define hb_mutex_impl_unlock(M)        (*(M))--;
+#define hb_mutex_impl_finish(M)        HB_STMT_START {} HB_STMT_END
+
 
+#else /* HB_NO_MT */
+
+typedef int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT     0
+#define hb_mutex_impl_init(M)  HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_lock(M)  HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_unlock(M)        HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_finish(M)        HB_STMT_START {} HB_STMT_END
 
 #endif
 
 
+#define HB_MUTEX_INIT          {HB_MUTEX_IMPL_INIT}
 struct hb_mutex_t
 {
+  /* TODO Add tracing. */
+
   hb_mutex_impl_t m;
 
   inline void init   (void) { hb_mutex_impl_init   (&m); }
   inline void lock   (void) { hb_mutex_impl_lock   (&m); }
   inline void unlock (void) { hb_mutex_impl_unlock (&m); }
-  inline void free   (void) { hb_mutex_impl_free   (&m); }
-};
-
-#define HB_MUTEX_INIT          {HB_MUTEX_IMPL_INIT}
-#define hb_mutex_init(M)       (M)->init ()
-#define hb_mutex_lock(M)       (M)->lock ()
-#define hb_mutex_unlock(M)     (M)->unlock ()
-#define hb_mutex_free(M)       (M)->free ()
-
-
-struct hb_static_mutex_t : hb_mutex_t
-{
-  hb_static_mutex_t (void)  { this->init (); }
-  ~hb_static_mutex_t (void) { this->free (); }
-
-  private:
-  NO_COPY (hb_static_mutex_t);
+  inline void finish (void) { hb_mutex_impl_finish (&m); }
 };
 
 
-HB_END_DECLS
-
 #endif /* HB_MUTEX_PRIVATE_HH */
index 1b25443..96d1bd3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright © 2007  Chris Wilson
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 
 #include "hb-private.hh"
 
+#include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
-HB_BEGIN_DECLS
-
 
 /* Debug */
 
@@ -46,67 +45,30 @@ HB_BEGIN_DECLS
 #endif
 
 
-/* atomic_int */
-
-/* We need external help for these */
-
-#ifdef HAVE_GLIB
-
-#include <glib.h>
-
-typedef volatile 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)
-
-
-#elif defined(_MSC_VER)
-
-#include <intrin.h>
-
-typedef long hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V)     _InterlockedExchangeAdd (&(AI), V)
-#define hb_atomic_int_get(AI)                  (_ReadBarrier (), (AI))
-#define hb_atomic_int_set(AI, V)               ((void) _InterlockedExchange (&(AI), (V)))
-
-
-#else
-
-#warning "Could not find any system to define atomic_int macros, library will NOT be thread-safe"
-
-typedef volatile 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)               ((void) ((AI) = (V)))
-
-
-#endif
-
-
-
-
 /* reference_count */
 
-typedef struct {
-  hb_atomic_int_t ref_count;
-
 #define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
 #define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+struct hb_reference_count_t
+{
+  hb_atomic_int_t ref_count;
 
-  inline void init (int v) { ref_count = v; /* non-atomic is fine */ }
-  inline int inc (void) { return hb_atomic_int_fetch_and_add (ref_count,  1); }
-  inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); }
-  inline void set (int v) { hb_atomic_int_set (ref_count, v); }
+  inline void init (int v) { ref_count = v; }
+  inline int inc (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count),  1); }
+  inline int dec (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count), -1); }
+  inline void finish (void) { ref_count = HB_REFERENCE_COUNT_INVALID_VALUE; }
 
-  inline int get (void) const { return hb_atomic_int_get (ref_count); }
-  inline bool is_invalid (void) const { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; }
+  inline bool is_invalid (void) const { return ref_count == HB_REFERENCE_COUNT_INVALID_VALUE; }
 
-} hb_reference_count_t;
+};
 
 
 /* user_data */
 
-struct hb_user_data_array_t {
+#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT}
+struct hb_user_data_array_t
+{
+  /* TODO Add tracing. */
 
   struct hb_user_data_item_t {
     hb_user_data_key_t *key;
@@ -119,27 +81,32 @@ struct hb_user_data_array_t {
     void finish (void) { if (destroy) destroy (data); }
   };
 
-  hb_lockable_set_t<hb_user_data_item_t, hb_static_mutex_t> items;
+  hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items;
+
+  inline void init (void) { items.init (); }
 
   HB_INTERNAL bool set (hb_user_data_key_t *key,
                        void *              data,
-                       hb_destroy_func_t   destroy);
+                       hb_destroy_func_t   destroy,
+                       hb_bool_t           replace,
+                       hb_mutex_t         &lock);
 
-  HB_INTERNAL void *get (hb_user_data_key_t *key);
+  HB_INTERNAL void *get (hb_user_data_key_t *key,
+                       hb_mutex_t          &lock);
 
-  HB_INTERNAL void finish (void);
+  HB_INTERNAL void finish (hb_mutex_t &lock);
 };
 
 
 /* object_header */
 
-typedef struct _hb_object_header_t hb_object_header_t;
-
-struct _hb_object_header_t {
+struct hb_object_header_t
+{
   hb_reference_count_t ref_count;
+  hb_mutex_t lock;
   hb_user_data_array_t user_data;
 
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT}
 
   static inline void *create (unsigned int size) {
     hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
@@ -152,6 +119,8 @@ struct _hb_object_header_t {
 
   inline void init (void) {
     ref_count.init (1);
+    lock.init ();
+    user_data.init ();
   }
 
   inline bool is_inert (void) const {
@@ -170,39 +139,45 @@ struct _hb_object_header_t {
     if (ref_count.dec () != 1)
       return false;
 
-    ref_count.init (HB_REFERENCE_COUNT_INVALID_VALUE);
-
-    user_data.finish ();
+    ref_count.finish (); /* Do this before user_data */
+    user_data.finish (lock);
+    lock.finish ();
 
     return true;
   }
 
   inline bool set_user_data (hb_user_data_key_t *key,
                             void *              data,
-                            hb_destroy_func_t   destroy_func) {
+                            hb_destroy_func_t   destroy_func,
+                            hb_bool_t           replace) {
     if (unlikely (!this || this->is_inert ()))
       return false;
 
-    return user_data.set (key, data, destroy_func);
+    return user_data.set (key, data, destroy_func, replace, lock);
   }
 
   inline void *get_user_data (hb_user_data_key_t *key) {
-    return user_data.get (key);
+    if (unlikely (!this || this->is_inert ()))
+      return NULL;
+
+    return user_data.get (key, lock);
   }
 
   inline void trace (const char *function) const {
+    if (unlikely (!this)) return;
+    /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only
+     * prints the class name and throws away the template info. */
     DEBUG_MSG (OBJECT, (void *) this,
-              "refcount=%d %s",
-              this ? ref_count.get () : 0,
-              function);
+              "%s refcount=%d",
+              function,
+              this ? ref_count.ref_count : 0);
   }
 
+  private:
+  ASSERT_POD ();
 };
 
 
-HB_END_DECLS
-
-
 /* object */
 
 template <typename Type>
@@ -239,9 +214,10 @@ template <typename Type>
 static inline bool hb_object_set_user_data (Type               *obj,
                                            hb_user_data_key_t *key,
                                            void *              data,
-                                           hb_destroy_func_t   destroy)
+                                           hb_destroy_func_t   destroy,
+                                           hb_bool_t           replace)
 {
-  return obj->header.set_user_data (key, data, destroy);
+  return obj->header.set_user_data (key, data, destroy, replace);
 }
 
 template <typename Type>
@@ -252,9 +228,4 @@ static inline void *hb_object_get_user_data (Type               *obj,
 }
 
 
-HB_BEGIN_DECLS
-
-
-HB_END_DECLS
-
 #endif /* HB_OBJECT_PRIVATE_HH */
index 7d43e46..ce18580 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_FILE_PRIVATE_HH
@@ -29,7 +31,6 @@
 
 #include "hb-open-type-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -52,7 +53,7 @@ typedef struct TableRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   Tag          tag;            /* 4-byte identifier. */
@@ -101,8 +102,7 @@ typedef struct OffsetTable
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && c->check_array (tables, TableRecord::static_size, numTables);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
   private:
@@ -130,7 +130,7 @@ struct TTCHeaderVersion1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return table.sanitize (c, this);
+    return TRACE_RETURN (table.sanitize (c, this));
   }
 
   private:
@@ -169,11 +169,11 @@ struct TTCHeader
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.header.version.sanitize (c))) return false;
+    if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false);
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
-    case 1: return u.version1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.version1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -231,14 +231,14 @@ struct OpenTypeFontFile
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.tag.sanitize (c))) return false;
+    if (unlikely (!u.tag.sanitize (c))) return TRACE_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;
+    case TrueTypeTag:  return TRACE_RETURN (u.fontFace.sanitize (c));
+    case TTCTag:       return TRACE_RETURN (u.ttcHeader.sanitize (c));
+    default:           return TRACE_RETURN (true);
     }
   }
 
@@ -253,6 +253,5 @@ struct OpenTypeFontFile
 };
 
 
-HB_END_DECLS
 
 #endif /* HB_OPEN_FILE_PRIVATE_HH */
index ab2a346..5d90e5b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
@@ -31,8 +33,6 @@
 
 #include "hb-blob.h"
 
-HB_BEGIN_DECLS
-HB_END_DECLS
 
 
 /*
@@ -80,17 +80,25 @@ inline Type& StructAfter(TObject &X)
  */
 
 /* Check _assertion in a method environment */
-#define _DEFINE_SIZE_ASSERTION(_assertion) \
-  inline void _size_assertion (void) const \
-  { ASSERT_STATIC (_assertion); }
+#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
+  inline void _instance_assertion_on_line_##_line (void) const \
+  { \
+    ASSERT_STATIC (_assertion); \
+    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
+  }
+# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
+# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
+
 /* Check that _code compiles in a method environment */
-#define _DEFINE_COMPILES_ASSERTION(_code) \
-  inline void _compiles_assertion (void) const \
+#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
+  inline void _compiles_assertion_on_line_##_line (void) const \
   { _code; }
+# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
+# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
 
 
 #define DEFINE_SIZE_STATIC(size) \
-  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size)); \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
   static const unsigned int static_size = (size); \
   static const unsigned int min_size = (size)
 
@@ -98,21 +106,21 @@ inline Type& StructAfter(TObject &X)
 #define VAR 1
 
 #define DEFINE_SIZE_UNION(size, _member) \
-  _DEFINE_SIZE_ASSERTION (this->u._member.static_size == (size)); \
+  DEFINE_INSTANCE_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)); \
+  DEFINE_INSTANCE_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) \
+  DEFINE_INSTANCE_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) \
+  DEFINE_INSTANCE_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)
 
 
@@ -155,7 +163,7 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
 
 
 #define TRACE_SANITIZE() \
-       hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, NULL, HB_FUNC);
+       hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
 
 
 struct hb_sanitize_context_t
@@ -166,24 +174,24 @@ struct hb_sanitize_context_t
     this->writable = false;
   }
 
-  inline void setup (void)
+  inline void start_processing (void)
   {
     this->start = hb_blob_get_data (this->blob, NULL);
     this->end = this->start + hb_blob_get_length (this->blob);
     this->edit_count = 0;
     this->debug_depth = 0;
 
-    DEBUG_MSG (SANITIZE, this->blob,
-              "init [%p..%p] (%lu bytes)",
-              this->start, this->end,
-              (unsigned long) (this->end - this->start));
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
+                    "start [%p..%p] (%lu bytes)",
+                    this->start, this->end,
+                    (unsigned long) (this->end - this->start));
   }
 
-  inline void finish (void)
+  inline void end_processing (void)
   {
-    DEBUG_MSG (SANITIZE, this->blob,
-              "fini [%p..%p] %u edit requests",
-              this->start, this->end, this->edit_count);
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
+                    "end [%p..%p] %u edit requests",
+                    this->start, this->end, this->edit_count);
 
     hb_blob_destroy (this->blob);
     this->blob = NULL;
@@ -193,18 +201,13 @@ struct hb_sanitize_context_t
   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;
-
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-                    "%-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-                    this->debug_depth, this->debug_depth,
-                    p, p + len, len,
-                    this->start, this->end,
-                    ret ? "pass" : "FAIL");
 
-    return likely (ret);
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+                                             "check_range [%p..%p] (%d bytes) in [%p..%p]",
+                                             p, p + len, len,
+                                             this->start, this->end);
+
+    return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
   }
 
   inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
@@ -212,14 +215,12 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-                    "%-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
-                    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");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+                                             "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
+                                             p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
+                                             this->start, this->end);
 
-    return likely (!overflows && this->check_range (base, record_size * len));
+    return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
   }
 
   template <typename Type>
@@ -228,23 +229,21 @@ struct hb_sanitize_context_t
     return likely (this->check_range (obj, obj->min_size));
   }
 
-  inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
+  inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
   {
     const char *p = (const char *) base;
     this->edit_count++;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-                    "%-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
-                    this->debug_depth, this->debug_depth,
-                    this->edit_count,
-                    p, p + len, len,
-                    this->start, this->end,
-                    this->writable ? "granted" : "REJECTED");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+                                             "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
+                                             this->edit_count,
+                                             p, p + len, len,
+                                             this->start, this->end);
 
-    return this->writable;
+    return TRACE_RETURN (this->writable);
   }
 
-  unsigned int debug_depth;
+  mutable unsigned int debug_depth;
   const char *start, *end;
   bool writable;
   unsigned int edit_count;
@@ -268,10 +267,10 @@ struct Sanitizer
   retry:
     DEBUG_MSG_FUNC (SANITIZE, blob, "start");
 
-    c->setup ();
+    c->start_processing ();
 
     if (unlikely (!c->start)) {
-      c->finish ();
+      c->end_processing ();
       return blob;
     }
 
@@ -305,7 +304,7 @@ struct Sanitizer
       }
     }
 
-    c->finish ();
+    c->end_processing ();
 
     DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
     if (sane)
@@ -340,12 +339,12 @@ struct Sanitizer
  */
 
 
-template <typename Type, int Bytes> class BEInt;
+template <typename Type, int Bytes> struct 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>
+struct BEInt<Type, 2>
 {
   public:
   inline void set (Type i) { hb_be_uint16_put (v,i); }
@@ -355,7 +354,7 @@ class BEInt<Type, 2>
   private: uint8_t v[2];
 };
 template <typename Type>
-class BEInt<Type, 4>
+struct BEInt<Type, 4>
 {
   public:
   inline void set (Type i) { hb_be_uint32_put (v,i); }
@@ -376,7 +375,7 @@ struct IntType
   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));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   protected:
   BEInt<Type, sizeof (Type)> v;
@@ -384,18 +383,29 @@ struct IntType
   DEFINE_SIZE_STATIC (sizeof (Type));
 };
 
+/* Typedef these to avoid clash with windows.h */
+#define USHORT HB_USHORT
+#define SHORT  HB_SHORT
+#define ULONG  HB_ULONG
+#define LONG   HB_LONG
 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. */
 
+/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */
+typedef SHORT FWORD;
+
+/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */
+typedef USHORT UFWORD;
+
 /* 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));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   private:
   LONG major;
@@ -459,7 +469,7 @@ struct FixedVersion
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT major;
@@ -487,26 +497,26 @@ struct GenericOffsetTo : OffsetType
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!c->check_struct (this))) return false;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c)) || neuter (c);
+    return TRACE_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;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c, user_data)) || neuter (c);
+    return TRACE_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)) {
+    if (c->may_edit (this, this->static_size)) {
       this->set (0); /* 0 is Null offset */
       return true;
     }
@@ -552,7 +562,7 @@ struct GenericArrayOf
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
@@ -563,33 +573,32 @@ struct GenericArrayOf
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_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;
+        return TRACE_RETURN (false);
+    return TRACE_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;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_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;
+        return TRACE_RETURN (false);
+    return TRACE_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);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
   }
 
   public:
@@ -631,12 +640,12 @@ struct OffsetListOf : OffsetArrayOf<Type>
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return OffsetArrayOf<Type>::sanitize (c, this);
+    return TRACE_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);
+    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
 
@@ -661,7 +670,7 @@ struct HeadlessArrayOf
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
@@ -672,7 +681,7 @@ struct HeadlessArrayOf
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT len;
@@ -688,8 +697,8 @@ 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); }
+    struct Cmp {
+      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;
@@ -697,7 +706,5 @@ struct SortedArrayOf : ArrayOf<Type> {
 };
 
 
-HB_BEGIN_DECLS
-HB_END_DECLS
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
similarity index 95%
rename from src/hb-ot-head-private.hh
rename to src/hb-ot-head-table.hh
index 436666f..32d64ca 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_HEAD_PRIVATE_HH
-#define HB_OT_HEAD_PRIVATE_HH
+#ifndef HB_OT_HEAD_TABLE_HH
+#define HB_OT_HEAD_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
- * head
+ * head -- Font Header
  */
 
 #define HB_OT_TAG_head HB_TAG('h','e','a','d')
@@ -50,7 +51,7 @@ struct head
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) && likely (version.major == 1);
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
   private:
@@ -140,6 +141,5 @@ struct head
 };
 
 
-HB_END_DECLS
 
-#endif /* HB_OT_HEAD_PRIVATE_HH */
+#endif /* HB_OT_HEAD_TABLE_HH */
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
new file mode 100644 (file)
index 0000000..2eea05a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2011,2012  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_HHEA_TABLE_HH
+#define HB_OT_HHEA_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+
+/*
+ * hhea -- The Horizontal Header Table
+ */
+
+#define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
+
+
+struct hhea
+{
+  static const hb_tag_t Tag    = HB_OT_TAG_hhea;
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
+  }
+
+  private:
+  FixedVersion version;                /* 0x00010000 for version 1.0. */
+  FWORD                ascender;               /* Typographic ascent. <a
+                                        * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
+                                        * (Distance from baseline of highest
+                                        * ascender)</a> */
+  FWORD                descender;              /* Typographic descent. <a
+                                        * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
+                                        * (Distance from baseline of lowest
+                                        * descender)</a> */
+  FWORD                lineGap;                /* Typographic line gap. Negative
+                                        * LineGap values are treated as zero
+                                        * in Windows 3.1, System 6, and
+                                        * System 7. */
+  UFWORD       advanceWidthMax;        /* Maximum advance width value in
+                                        * 'hmtx' table. */
+  FWORD                minLeftSideBearing;     /* Minimum left sidebearing value in
+                                        * 'hmtx' table. */
+  FWORD                minRightSideBearing;    /* Minimum right sidebearing value;
+                                        * calculated as Min(aw - lsb -
+                                        * (xMax - xMin)). */
+  FWORD                xMaxExtent;             /* Max(lsb + (xMax - xMin)). */
+  SHORT                caretSlopeRise;         /* Used to calculate the slope of the
+                                        * cursor (rise/run); 1 for vertical. */
+  SHORT                caretSlopeRun;          /* 0 for vertical. */
+  SHORT                caretOffset;            /* The amount by which a slanted
+                                        * highlight on a glyph needs
+                                        * to be shifted to produce the
+                                        * best appearance. Set to 0 for
+                                        * non--slanted fonts */
+  SHORT                reserved1;              /* set to 0 */
+  SHORT                reserved2;              /* set to 0 */
+  SHORT                reserved3;              /* set to 0 */
+  SHORT                reserved4;              /* set to 0 */
+  SHORT                metricDataFormat;       /* 0 for current format. */
+  USHORT       numberOfHMetrics;       /* Number of hMetric entries in 'hmtx'
+                                        * table */
+  public:
+  DEFINE_SIZE_STATIC (36);
+};
+
+
+#endif /* HB_OT_HHEA_TABLE_HH */
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
new file mode 100644 (file)
index 0000000..35cfb48
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2011,2012  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_HMTX_TABLE_HH
+#define HB_OT_HMTX_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+
+/*
+ * hmtx -- The Horizontal Metrics Table
+ */
+
+#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
+
+
+struct LongHorMetric
+{
+  USHORT       advanceWidth;
+  SHORT                lsb;
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct hmtx
+{
+  static const hb_tag_t Tag    = HB_OT_TAG_hmtx;
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    /* We don't check for anything specific here.  The users of the
+     * struct do all the hard work... */
+    return TRACE_RETURN (true);
+  }
+
+  private:
+  LongHorMetric        longHorMetric[VAR];     /* Paired advance width and left side
+                                        * bearing values for each glyph. The
+                                        * value numOfHMetrics comes from
+                                        * the 'hhea' table. If the font is
+                                        * monospaced, only one entry need
+                                        * be in the array, but that entry is
+                                        * required. The last entry applies to
+                                        * all subsequent glyphs. */
+  SHORT                leftSideBearingX[VAR];  /* Here the advanceWidth is assumed
+                                        * to be the same as the advanceWidth
+                                        * for the last entry above. The
+                                        * number of entries in this array is
+                                        * derived from numGlyphs (from 'maxp'
+                                        * table) minus numberOfHMetrics. This
+                                        * generally is used with a run of
+                                        * monospaced glyphs (e.g., Kanji
+                                        * fonts or Courier fonts). Only one
+                                        * run is allowed and it must be at
+                                        * the end. This allows a monospaced
+                                        * font to vary the left side bearing
+                                        * values for each glyph. */
+  public:
+  DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX);
+};
+
+#endif /* HB_OT_HMTX_TABLE_HH */
index ce47e22..2943a7f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
 #include "hb-ot-layout-private.hh"
-
 #include "hb-open-type-private.hh"
+#include "hb-set-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
 
 
 /*
@@ -62,8 +59,7 @@ struct Record
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && offset.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base));
   }
 
   Tag          tag;            /* 4-byte Tag identifier */
@@ -117,7 +113,7 @@ struct RecordListOf : RecordArrayOf<Type>
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return RecordArrayOf<Type>::sanitize (c, this);
+    return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
   }
 };
 
@@ -131,7 +127,11 @@ struct RangeRecord
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
+  }
+
+  inline bool intersects (const hb_set_t *glyphs) const {
+    return glyphs->intersects (start, end);
   }
 
   GlyphID      start;          /* First GlyphID in the range */
@@ -186,8 +186,7 @@ struct LangSys
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && featureIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
   Offset       lookupOrder;    /* = Null (reserved for an offset to a
@@ -225,8 +224,7 @@ struct Script
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return defaultLangSys.sanitize (c, this)
-       && langSys.sanitize (c, this);
+    return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
   private:
@@ -256,8 +254,7 @@ struct Feature
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && lookupIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c));
   }
 
   Offset       featureParams;  /* Offset to Feature Parameters table (if one
@@ -274,7 +271,7 @@ typedef RecordListOf<Feature> FeatureList;
 
 struct LookupFlag : USHORT
 {
-  enum {
+  enum Flags {
     RightToLeft                = 0x0001u,
     IgnoreBaseGlyphs   = 0x0002u,
     IgnoreLigatures    = 0x0004u,
@@ -311,14 +308,13 @@ struct Lookup
   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 (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
     if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
-      if (!markFilteringSet.sanitize (c)) return false;
+      if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT       lookupType;             /* Different enumerations for GSUB and GPOS */
@@ -354,9 +350,25 @@ struct CoverageFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return glyphArray.sanitize (c);
+    return TRACE_RETURN (glyphArray.sanitize (c));
   }
 
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+    return glyphs->has (glyphArray[index]);
+  }
+
+  struct Iter {
+    inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
+    inline bool more (void) { return i < c->glyphArray.len; }
+    inline void next (void) { i++; }
+    inline uint16_t get_glyph (void) { return c->glyphArray[i]; }
+    inline uint16_t get_coverage (void) { return i; }
+
+    private:
+    const struct CoverageFormat1 *c;
+    unsigned int i;
+  };
+
   private:
   USHORT       coverageFormat; /* Format identifier--format = 1 */
   SortedArrayOf<GlyphID>
@@ -382,9 +394,50 @@ struct CoverageFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
+  }
+
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+    unsigned int i;
+    unsigned int count = rangeRecord.len;
+    for (i = 0; i < count; i++) {
+      const RangeRecord &range = rangeRecord[i];
+      if (range.value <= index &&
+         index < (unsigned int) range.value + (range.end - range.start) &&
+         range.intersects (glyphs))
+        return true;
+      else if (index < range.value)
+        return false;
+    }
+    return false;
   }
 
+  struct Iter {
+    inline void init (const CoverageFormat2 &c_) {
+      c = &c_;
+      coverage = 0;
+      i = 0;
+      j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
+    }
+    inline bool more (void) { return i < c->rangeRecord.len; }
+    inline void next (void) {
+      coverage++;
+      if (j == c->rangeRecord[i].end) {
+        i++;
+       if (more ())
+         j = c->rangeRecord[i].start;
+       return;
+      }
+      j++;
+    }
+    inline uint16_t get_glyph (void) { return j; }
+    inline uint16_t get_coverage (void) { return coverage; }
+
+    private:
+    const struct CoverageFormat2 *c;
+    unsigned int i, j, coverage;
+  };
+
   private:
   USHORT       coverageFormat; /* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
@@ -410,14 +463,79 @@ struct Coverage
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  inline bool intersects (const hb_set_t *glyphs) const {
+    /* TODO speed this up */
+    Coverage::Iter iter;
+    for (iter.init (*this); iter.more (); iter.next ()) {
+      if (glyphs->has (iter.get_glyph ()))
+        return true;
+    }
+    return false;
+  }
+
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return u.format1.intersects_coverage (glyphs, index);
+    case 2: return u.format2.intersects_coverage (glyphs, index);
+    default:return false;
     }
   }
 
+  struct Iter {
+    Iter (void) : format (0) {};
+    inline void init (const Coverage &c_) {
+      format = c_.u.format;
+      switch (format) {
+      case 1: return u.format1.init (c_.u.format1);
+      case 2: return u.format2.init (c_.u.format2);
+      default:return;
+      }
+    }
+    inline bool more (void) {
+      switch (format) {
+      case 1: return u.format1.more ();
+      case 2: return u.format2.more ();
+      default:return true;
+      }
+    }
+    inline void next (void) {
+      switch (format) {
+      case 1: u.format1.next (); break;
+      case 2: u.format2.next (); break;
+      default:                   break;
+      }
+    }
+    inline uint16_t get_glyph (void) {
+      switch (format) {
+      case 1: return u.format1.get_glyph ();
+      case 2: return u.format2.get_glyph ();
+      default:return true;
+      }
+    }
+    inline uint16_t get_coverage (void) {
+      switch (format) {
+      case 1: return u.format1.get_coverage ();
+      case 2: return u.format2.get_coverage ();
+      default:return true;
+      }
+    }
+
+    private:
+    unsigned int format;
+    union {
+    CoverageFormat1::Iter      format1;
+    CoverageFormat2::Iter      format2;
+    } u;
+  };
+
   private:
   union {
   USHORT               format;         /* Format identifier */
@@ -447,8 +565,15 @@ struct ClassDefFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && classValue.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
+  }
+
+  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
+    unsigned int count = classValue.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (classValue[i] == klass && glyphs->has (startGlyph + i))
+        return true;
+    return false;
   }
 
   USHORT       classFormat;            /* Format identifier--format = 1 */
@@ -474,7 +599,15 @@ struct ClassDefFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
+  }
+
+  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
+        return true;
+    return false;
   }
 
   USHORT       classFormat;    /* Format identifier--format = 2 */
@@ -500,11 +633,19 @@ struct ClassDef
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return u.format1.intersects_class (glyphs, klass);
+    case 2: return u.format2.intersects_class (glyphs, klass);
+    default:return false;
     }
   }
 
@@ -576,8 +717,7 @@ struct Device
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && c->check_range (this, this->get_size ());
+    return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
   private:
@@ -594,7 +734,5 @@ struct Device
 };
 
 
-HB_BEGIN_DECLS
-HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
similarity index 89%
rename from src/hb-ot-layout-gdef-private.hh
rename to src/hb-ot-layout-gdef-table.hh
index 08fd757..f29fc14 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2010,2011  Google, Inc.
+ * Copyright © 2010,2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_LAYOUT_GDEF_PRIVATE_HH
-#define HB_OT_LAYOUT_GDEF_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_GDEF_TABLE_HH
+#define HB_OT_LAYOUT_GDEF_TABLE_HH
 
 #include "hb-ot-layout-common-private.hh"
 
 #include "hb-font-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -72,8 +71,7 @@ struct AttachList
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-       && attachPoint.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
   private:
@@ -103,7 +101,7 @@ struct CaretValueFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -129,7 +127,7 @@ struct CaretValueFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -152,8 +150,7 @@ struct CaretValueFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && deviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   private:
@@ -181,12 +178,12 @@ struct CaretValue
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_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;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -222,7 +219,7 @@ struct LigGlyph
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return carets.sanitize (c, this);
+    return TRACE_RETURN (carets.sanitize (c, this));
   }
 
   private:
@@ -256,8 +253,7 @@ struct LigCaretList
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-       && ligGlyph.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
   private:
@@ -279,7 +275,7 @@ struct MarkGlyphSetsFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this));
   }
 
   private:
@@ -303,10 +299,10 @@ struct MarkGlyphSets
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -321,14 +317,14 @@ struct MarkGlyphSets
 
 
 /*
- * GDEF
+ * GDEF -- The Glyph Definition Table
  */
 
 struct GDEF
 {
   static const hb_tag_t Tag    = HB_OT_TAG_GDEF;
 
-  enum {
+  enum GlyphClasses {
     UnclassifiedGlyph  = 0,
     BaseGlyph          = 1,
     LigatureGlyph      = 2,
@@ -366,12 +362,13 @@ struct GDEF
 
   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.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this));
+    return TRACE_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.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this)));
   }
 
 
@@ -424,6 +421,5 @@ struct GDEF
 };
 
 
-HB_END_DECLS
 
-#endif /* HB_OT_LAYOUT_GDEF_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */
similarity index 73%
rename from src/hb-ot-layout-gpos-private.hh
rename to src/hb-ot-layout-gpos-table.hh
index e5edb3c..71c13a2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
-#define HB_OT_LAYOUT_GPOS_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
+#define HB_OT_LAYOUT_GPOS_TABLE_HH
 
 #include "hb-ot-layout-gsubgpos-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /* buffer **position** var allocations */
@@ -47,8 +46,7 @@ typedef Value ValueRecord[VAR];
 
 struct ValueFormat : USHORT
 {
-  enum
-  {
+  enum Flags {
     xPlacement = 0x0001,       /* Includes horizontal adjustment for placement */
     yPlacement = 0x0002,       /* Includes vertical adjustment for placement */
     xAdvance   = 0x0004,       /* Includes horizontal adjustment for advance */
@@ -173,40 +171,39 @@ struct ValueFormat : USHORT
 
   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));
+    return TRACE_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 (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
 
-    if (!has_device ()) return true;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += len;
     }
 
-    return true;
+    return TRACE_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;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += stride;
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 };
 
@@ -225,7 +222,7 @@ struct AnchorFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -257,7 +254,7 @@ struct AnchorFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -288,9 +285,7 @@ struct AnchorFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && xDeviceTable.sanitize (c, this)
-       && yDeviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
   private:
@@ -325,12 +320,12 @@ struct Anchor
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_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;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -355,13 +350,13 @@ struct AnchorMatrix
 
   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;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
+    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
+    if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!matrix[i].sanitize (c, this)) return false;
-    return true;
+      if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   USHORT       rows;                   /* Number of rows */
@@ -380,8 +375,7 @@ struct MarkRecord
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-       && markAnchor.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
   private:
@@ -409,21 +403,21 @@ struct MarkArray : ArrayOf<MarkRecord>    /* Array of MarkRecords--in Coverage orde
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
-    mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->idx].codepoint, &mark_x, &mark_y);
+    mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
-    hb_glyph_position_t &o = c->buffer->pos[c->buffer->idx];
+    hb_glyph_position_t &o = c->buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
     o.attach_lookback() = c->buffer->idx - glyph_pos;
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ArrayOf<MarkRecord>::sanitize (c, this);
+    return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
 
@@ -438,22 +432,19 @@ struct SinglePosFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
-                            values, c->buffer->pos[c->buffer->idx]);
+                            values, c->buffer->cur_pos());
 
     c->buffer->idx++;
-    return true;
+    return TRACE_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);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
   private:
@@ -478,26 +469,22 @@ struct SinglePosFormat2
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (likely (index >= valueCount))
-      return false;
+    if (likely (index >= valueCount)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
                             &values[index * valueFormat.get_len ()],
-                            c->buffer->pos[c->buffer->idx]);
+                            c->buffer->cur_pos());
 
     c->buffer->idx++;
-    return true;
+    return TRACE_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);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
   private:
@@ -523,19 +510,19 @@ struct SinglePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -582,18 +569,18 @@ struct PairSet
       if (c->buffer->info[pos].codepoint == record->secondGlyph)
       {
        valueFormats[0].apply_value (c->font, c->direction, this,
-                                    &record->values[0], c->buffer->pos[c->buffer->idx]);
+                                    &record->values[0], c->buffer->cur_pos());
        valueFormats[1].apply_value (c->font, c->direction, this,
                                     &record->values[len1], c->buffer->pos[pos]);
        if (len2)
          pos++;
        c->buffer->idx = pos;
-       return true;
+       return TRACE_RETURN (true);
       }
       record = &StructAtOffset<PairValueRecord> (record, record_size);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   struct sanitize_closure_t {
@@ -606,12 +593,12 @@ struct PairSet
   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;
+       && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_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);
+    return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
+                     && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
   private:
@@ -630,23 +617,15 @@ struct PairPosFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
-    if (unlikely (c->buffer->idx + 2 > end))
-      return false;
+    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    unsigned int j = c->buffer->idx + 1;
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j == end))
-       return false;
-      j++;
-    }
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    return (this+pairSet[index]).apply (c, &valueFormat1, j);
+    return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -661,9 +640,7 @@ struct PairPosFormat1
       1 + len1 + len2
     };
 
-    return c->check_struct (this)
-       && coverage.sanitize (c, this)
-       && pairSet.sanitize (c, this, &closure);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
   private:
@@ -692,42 +669,33 @@ struct PairPosFormat2
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
-    if (unlikely (c->buffer->idx + 2 > end))
-      return false;
+    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    unsigned int j = c->buffer->idx + 1;
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j == end))
-       return false;
-      j++;
-    }
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     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->idx].codepoint);
-    unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
-    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
-      return false;
+    unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint);
+    unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c->font, c->direction, this,
-                             v, c->buffer->pos[c->buffer->idx]);
+                             v, c->buffer->cur_pos());
     valueFormat2.apply_value (c->font, c->direction, this,
-                             v + len1, c->buffer->pos[j]);
+                             v + len1, c->buffer->pos[skippy_iter.idx]);
 
+    c->buffer->idx = skippy_iter.idx;
     if (len2)
-      j++;
-    c->buffer->idx = j;
+      c->buffer->idx++;
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -735,16 +703,16 @@ struct PairPosFormat2
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
-       && classDef2.sanitize (c, this))) return false;
+       && classDef2.sanitize (c, this))) return TRACE_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);
+    return TRACE_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:
@@ -786,19 +754,19 @@ struct PairPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -817,8 +785,7 @@ struct EntryExitRecord
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return entryAnchor.sanitize (c, base)
-       && exitAnchor.sanitize (c, base);
+    return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
   private:
@@ -844,30 +811,21 @@ struct CursivePosFormat1
     TRACE_APPLY ();
 
     /* We don't handle mark glyphs here. */
-    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
-      return false;
+    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
 
-    unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
-    if (unlikely (c->buffer->idx + 2 > end))
-      return false;
+    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->idx].codepoint)];
-    if (!this_record.exitAnchor)
-      return false;
+    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)];
+    if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
-    unsigned int j = c->buffer->idx + 1;
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j == end))
-       return false;
-      j++;
-    }
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)];
-    if (!next_record.entryAnchor)
-      return false;
+    const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
+    if (!next_record.entryAnchor) return TRACE_RETURN (false);
 
     unsigned int i = c->buffer->idx;
+    unsigned int j = skippy_iter.idx;
 
     hb_position_t entry_x, entry_y, exit_x, exit_y;
     (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, &exit_x, &exit_y);
@@ -927,13 +885,12 @@ struct CursivePosFormat1
     }
 
     c->buffer->idx = j;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-       && entryExitRecord.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
   private:
@@ -957,17 +914,17 @@ struct CursivePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -992,38 +949,27 @@ struct MarkBasePosFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
     unsigned int property;
-    unsigned int j = c->buffer->idx;
-    do
-    {
-      if (unlikely (!j))
-       return false;
-      j--;
-    } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
+    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
-    {/*return false;*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
-    unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
-    if (base_index == NOT_COVERED)
-      return false;
+    unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
+    if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
+    return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   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);
+    return TRACE_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:
@@ -1054,17 +1000,17 @@ struct MarkBasePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1094,59 +1040,51 @@ struct MarkLigPosFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
     unsigned int property;
-    unsigned int j = c->buffer->idx;
-    do
-    {
-      if (unlikely (!j))
-       return false;
-      j--;
-    } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
+    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
-    {/*return false;*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
+    unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
-    if (lig_index == NOT_COVERED)
-      return false;
+    if (lig_index == NOT_COVERED) return TRACE_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;
+    if (unlikely (!comp_count)) return TRACE_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->idx].lig_id() && c->buffer->info[c->buffer->idx].lig_comp())
+    if (get_lig_id (c->buffer->info[j]) &&
+       get_lig_id (c->buffer->cur()) &&
+       get_lig_comp (c->buffer->cur()) > 0)
     {
-      comp_index = c->buffer->info[c->buffer->idx].lig_comp() - 1;
+      comp_index = get_lig_comp (c->buffer->cur()) - 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);
+    return TRACE_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);
+    return TRACE_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:
@@ -1178,17 +1116,17 @@ struct MarkLigPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1213,44 +1151,37 @@ struct MarkMarkPosFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark1_index == NOT_COVERED))
-      return false;
+    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint);
+    if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
     unsigned int property;
-    unsigned int j = c->buffer->idx;
-    do
-    {
-      if (unlikely (!j))
-       return false;
-      j--;
-    } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property));
+    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-      return false;
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
+
+    unsigned int j = skippy_iter.idx;
 
     /* 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->idx].lig_comp()) ||
-       (c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->idx].lig_id()))
-      return false;
+    if ((get_lig_comp (c->buffer->cur())) ||
+       (get_lig_comp (c->buffer->info[j]) > 0 &&
+        get_lig_id (c->buffer->cur())))
+      return TRACE_RETURN (false);
 
     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
-    if (mark2_index == NOT_COVERED)
-      return false;
+    if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
+    return TRACE_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);
+    return TRACE_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:
@@ -1283,17 +1214,17 @@ struct MarkMarkPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1305,9 +1236,7 @@ struct MarkMarkPos
 };
 
 
-HB_BEGIN_DECLS
 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
-HB_END_DECLS
 
 struct ContextPos : Context
 {
@@ -1317,7 +1246,7 @@ struct ContextPos : Context
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return Context::apply (c, position_lookup);
+    return TRACE_RETURN (Context::apply (c, position_lookup));
   }
 };
 
@@ -1329,7 +1258,7 @@ struct ChainContextPos : ChainContext
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return ChainContext::apply (c, position_lookup);
+    return TRACE_RETURN (ChainContext::apply (c, position_lookup));
   }
 };
 
@@ -1362,7 +1291,7 @@ struct PosLookupSubTable
 {
   friend struct PosLookup;
 
-  enum {
+  enum Type {
     Single             = 1,
     Pair               = 2,
     Cursive            = 3,
@@ -1378,32 +1307,32 @@ struct PosLookupSubTable
   {
     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;
+    case Single:               return TRACE_RETURN (u.single.apply (c));
+    case Pair:                 return TRACE_RETURN (u.pair.apply (c));
+    case Cursive:              return TRACE_RETURN (u.cursive.apply (c));
+    case MarkBase:             return TRACE_RETURN (u.markBase.apply (c));
+    case MarkLig:              return TRACE_RETURN (u.markLig.apply (c));
+    case MarkMark:             return TRACE_RETURN (u.markMark.apply (c));
+    case Context:              return TRACE_RETURN (u.c.apply (c));
+    case ChainContext:         return TRACE_RETURN (u.chainContext.apply (c));
+    case Extension:            return TRACE_RETURN (u.extension.apply (c));
+    default:                   return TRACE_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;
+    case Single:               return TRACE_RETURN (u.single.sanitize (c));
+    case Pair:                 return TRACE_RETURN (u.pair.sanitize (c));
+    case Cursive:              return TRACE_RETURN (u.cursive.sanitize (c));
+    case MarkBase:             return TRACE_RETURN (u.markBase.sanitize (c));
+    case MarkLig:              return TRACE_RETURN (u.markLig.sanitize (c));
+    case MarkMark:             return TRACE_RETURN (u.markMark.sanitize (c));
+    case Context:              return TRACE_RETURN (u.c.sanitize (c));
+    case ChainContext:         return TRACE_RETURN (u.chainContext.sanitize (c));
+    case Extension:            return TRACE_RETURN (u.extension.sanitize (c));
+    default:                   return TRACE_RETURN (true);
     }
   }
 
@@ -1430,25 +1359,11 @@ struct PosLookup : Lookup
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
-  inline bool apply_once (hb_font_t *font,
-                         hb_buffer_t *buffer,
-                         hb_mask_t lookup_mask,
-                         unsigned int context_length,
-                         unsigned int nesting_level_left) const
+  inline bool apply_once (hb_apply_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
-    hb_apply_context_t c[1] = {{0}};
-
-    c->font = font;
-    c->face = font->face;
-    c->buffer = buffer;
-    c->direction = buffer->props.direction;
-    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->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property))
+
+    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
     for (unsigned int i = 0; i < get_subtable_count (); i++)
@@ -1458,23 +1373,22 @@ struct PosLookup : Lookup
     return false;
   }
 
-   inline bool apply_string (hb_font_t   *font,
-                            hb_buffer_t *buffer,
-                            hb_mask_t    mask) const
+  inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
-    if (unlikely (!buffer->len))
+    if (unlikely (!c->buffer->len))
       return false;
 
-    buffer->idx = 0;
-    while (buffer->idx < buffer->len)
+    c->set_lookup (*this);
+
+    c->buffer->idx = 0;
+    while (c->buffer->idx < c->buffer->len)
     {
-      if ((buffer->info[buffer->idx].mask & mask) &&
-         apply_once (font, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
+      if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
        ret = true;
       else
-       buffer->idx++;
+       c->buffer->idx++;
     }
 
     return ret;
@@ -1482,16 +1396,16 @@ struct PosLookup : Lookup
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!Lookup::sanitize (c))) return false;
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
-    return list.sanitize (c, this, get_type ());
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
   }
 };
 
 typedef OffsetListOf<PosLookup> PosLookupList;
 
 /*
- * GPOS
+ * GPOS -- The Glyph Positioning Table
  */
 
 struct GPOS : GSUBGPOS
@@ -1501,20 +1415,17 @@ struct GPOS : GSUBGPOS
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
-  inline bool position_lookup (hb_font_t    *font,
-                              hb_buffer_t  *buffer,
-                              unsigned int  lookup_index,
-                              hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
+  inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
+  { return get_lookup (lookup_index).apply_string (c); }
 
   static inline void position_start (hb_buffer_t *buffer);
   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;
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
-    return list.sanitize (c, this);
+    return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
@@ -1590,8 +1501,8 @@ GPOS::position_finish (hb_buffer_t *buffer)
   for (unsigned int i = 0; i < len; i++)
     fix_mark_attachment (pos, i, direction);
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_comp);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_id);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
 }
 
@@ -1601,16 +1512,16 @@ GPOS::position_finish (hb_buffer_t *buffer)
 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
-  return get_subtable ().apply (c, get_type ());
+  return TRACE_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;
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return true;
-  return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
+  if (unlikely (!offset)) return TRACE_RETURN (true);
+  return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
 }
 
 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
@@ -1621,10 +1532,10 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i
   if (unlikely (c->nesting_level_left == 0))
     return false;
 
-  if (unlikely (c->context_length < 1))
-    return false;
-
-  return l.apply_once (c->font, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
+  return l.apply_once (&new_c);
 }
 
 
@@ -1632,6 +1543,5 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i
 #undef cursive_chain
 
 
-HB_END_DECLS
 
-#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
diff --git a/src/hb-ot-layout-gsub-private.hh b/src/hb-ot-layout-gsub-private.hh
deleted file mode 100644 (file)
index aca45ff..0000000
+++ /dev/null
@@ -1,947 +0,0 @@
-/*
- * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 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->idx].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->idx].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->idx].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->idx].codepoint;
-    hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].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->idx + c->context_length);
-    if (unlikely (count < 2 || c->buffer->idx + 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->idx + 1; i < count; i++, j++)
-    {
-      unsigned int property;
-      while (_hb_ot_layout_skip_mark (c->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->idx].lig_comp() = 0;
-    c->buffer->info[c->buffer->idx].lig_id() = lig_id;
-
-    if (j == c->buffer->idx + 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->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL))
-       {
-         c->buffer->info[c->buffer->idx].lig_comp() = i;
-         c->buffer->info[c->buffer->idx].lig_id() = lig_id;
-         c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
-       }
-
-       /* Skip the base glyph */
-       c->buffer->idx++;
-      }
-    }
-
-    return true;
-  }
-
-  public:
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
-    return ligGlyph.sanitize (c)
-        && component.sanitize (c);
-  }
-
-  private:
-  GlyphID      ligGlyph;               /* GlyphID of ligature to substitute */
-  HeadlessArrayOf<GlyphID>
-               component;              /* Array of component GlyphIDs--start
-                                        * with the second  component--ordered
-                                        * in writing direction */
-  public:
-  DEFINE_SIZE_ARRAY (4, component);
-};
-
-struct LigatureSet
-{
-  friend struct LigatureSubstFormat1;
-
-  private:
-  inline bool apply (hb_apply_context_t *c) 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->idx].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->idx].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->idx].codepoint = substitute[index];
-      c->buffer->idx--; /* 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_face_t *face,
-                         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->face = face;
-    c->buffer = buffer;
-    c->direction = buffer->props.direction;
-    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->face, &c->buffer->info[c->buffer->idx], 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_face_t   *face,
-                           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->idx = 0;
-       while (buffer->idx < buffer->len)
-       {
-         if ((buffer->info[buffer->idx].mask & mask) &&
-             apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
-           ret = true;
-         else
-           buffer->next_glyph ();
-
-       }
-       if (ret)
-         buffer->swap_buffers ();
-    }
-    else
-    {
-       /* in-place backward substitution */
-       buffer->idx = buffer->len - 1;
-       do
-       {
-         if ((buffer->info[buffer->idx].mask & mask) &&
-             apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
-           ret = true;
-         else
-           buffer->idx--;
-
-       }
-       while ((int) buffer->idx >= 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_face_t    *face,
-                                hb_buffer_t  *buffer,
-                                unsigned int  lookup_index,
-                                hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
-
-  static inline void substitute_start (hb_buffer_t *buffer);
-  static inline void substitute_finish (hb_buffer_t *buffer);
-
-  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);
-};
-
-
-void
-GSUB::substitute_start (hb_buffer_t *buffer)
-{
-  HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
-
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
-}
-
-void
-GSUB::substitute_finish (hb_buffer_t *buffer)
-{
-}
-
-
-/* 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->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->face, 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/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
new file mode 100644 (file)
index 0000000..f6a7575
--- /dev/null
@@ -0,0 +1,1253 @@
+/*
+ * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2010,2012  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_TABLE_HH
+#define HB_OT_LAYOUT_GSUB_TABLE_HH
+
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+
+
+struct SingleSubstFormat1
+{
+  friend struct SingleSubst;
+
+  private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      hb_codepoint_t glyph_id = iter.get_glyph ();
+      if (c->glyphs->has (glyph_id))
+       c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF);
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    /* According to the Adobe Annotated OpenType Suite, result is always
+     * limited to 16bit. */
+    glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
+    c->replace_glyph (glyph_id);
+
+    return TRACE_RETURN (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+       c->glyphs->add (substitute[iter.get_coverage ()]);
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
+
+    glyph_id = substitute[index];
+    c->replace_glyph (glyph_id);
+
+    return TRACE_RETURN (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    case 2: u.format2.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    case 2: return u.format2.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  private:
+  union {
+  USHORT               format;         /* Format identifier */
+  SingleSubstFormat1   format1;
+  SingleSubstFormat2   format2;
+  } u;
+};
+
+
+struct Sequence
+{
+  friend struct MultipleSubstFormat1;
+
+  private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int count = substitute.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->glyphs->add (substitute[i]);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    if (unlikely (!substitute.len)) return TRACE_RETURN (false);
+
+    unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
+    c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass);
+
+    return TRACE_RETURN (true);
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+       (this+sequence[iter.get_coverage ()]).closure (c);
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    return TRACE_RETURN ((this+sequence[index]).apply (c));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ())) {
+       const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
+       unsigned int count = alt_set.len;
+       for (unsigned int i = 0; i < count; i++)
+         c->glyphs->add (alt_set[i]);
+      }
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    const AlternateSet &alt_set = this+alternateSet[index];
+
+    if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
+
+    hb_mask_t glyph_mask = c->buffer->cur().mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
+
+    /* 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 TRACE_RETURN (false);
+
+    glyph_id = alt_set[alt_index - 1];
+
+    c->replace_glyph (glyph_id);
+
+    return TRACE_RETURN (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  private:
+  union {
+  USHORT               format;         /* Format identifier */
+  AlternateSubstFormat1        format1;
+  } u;
+};
+
+
+struct Ligature
+{
+  friend struct LigatureSet;
+
+  private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int count = component.len;
+    for (unsigned int i = 1; i < count; i++)
+      if (!c->glyphs->has (component[i]))
+        return;
+    c->glyphs->add (ligGlyph);
+  }
+
+  inline bool would_apply (hb_codepoint_t second) const
+  {
+    return component.len == 2 && component[1] == second;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    unsigned int count = component.len;
+    if (unlikely (count < 2)) return TRACE_RETURN (false);
+
+    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
+
+    bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+    bool found_non_mark = false;
+
+    for (unsigned int i = 1; i < count; i++)
+    {
+      unsigned int property;
+
+      if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
+
+      found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
+    }
+
+    unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0;
+
+    /* Allocate new ligature id */
+    unsigned int lig_id = allocate_lig_id (c->buffer);
+    set_lig_props (c->buffer->cur(), lig_id, 0);
+
+    if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
+    {
+      c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass);
+    }
+    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 (unsigned int i = 1; i < count; i++)
+      {
+       while (c->should_mark_skip_current_glyph ())
+       {
+         set_lig_props (c->buffer->cur(),  lig_id, i);
+         c->replace_glyph (c->buffer->cur().codepoint);
+       }
+
+       /* Skip the base glyph */
+       c->buffer->idx++;
+      }
+    }
+
+    return TRACE_RETURN (true);
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+      (this+ligature[i]).closure (c);
+  }
+
+  inline bool would_apply (hb_codepoint_t second) const
+  {
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+    {
+      const Ligature &lig = this+ligature[i];
+      if (lig.would_apply (second))
+        return true;
+    }
+    return false;
+  }
+
+  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 TRACE_RETURN (true);
+    }
+
+    return TRACE_RETURN (false);
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+       (this+ligatureSet[iter.get_coverage ()]).closure (c);
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    unsigned int index;
+    return (index = (this+coverage) (first)) != NOT_COVERED &&
+          (this+ligatureSet[index]).would_apply (second);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+
+    unsigned int index = (this+coverage) (glyph_id);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    const LigatureSet &lig_set = this+ligatureSet[index];
+    return TRACE_RETURN (lig_set.apply (c));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (first, second);
+    default:return false;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  private:
+  union {
+  USHORT               format;         /* Format identifier */
+  LigatureSubstFormat1 format1;
+  } u;
+};
+
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
+
+struct ContextSubst : Context
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return Context::closure (c, closure_lookup);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return TRACE_RETURN (Context::apply (c, substitute_lookup));
+  }
+};
+
+struct ChainContextSubst : ChainContext
+{
+  friend struct SubstLookupSubTable;
+
+  private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return ChainContext::closure (c, closure_lookup);
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    return TRACE_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 void closure (hb_closure_context_t *c) const;
+  inline bool would_apply (hb_codepoint_t glyph_id) const;
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
+
+  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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+backtrack[i]).intersects (c->glyphs))
+        return;
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+lookahead[i]).intersects (c->glyphs))
+        return;
+
+    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+       c->glyphs->add (substitute[iter.get_coverage ()]);
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
+      return TRACE_RETURN (false); /* No chaining to this type */
+
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_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->cur().codepoint = substitute[index];
+      c->buffer->idx--; /* Reverse! */
+      return TRACE_RETURN (true);
+    }
+
+    return TRACE_RETURN (false);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+      return TRACE_RETURN (false);
+    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    if (!lookahead.sanitize (c, this))
+      return TRACE_RETURN (false);
+    ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY ();
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  private:
+  union {
+  USHORT                               format;         /* Format identifier */
+  ReverseChainSingleSubstFormat1       format1;
+  } u;
+};
+
+
+
+/*
+ * SubstLookup
+ */
+
+struct SubstLookupSubTable
+{
+  friend struct SubstLookup;
+
+  enum Type {
+    Single             = 1,
+    Multiple           = 2,
+    Alternate          = 3,
+    Ligature           = 4,
+    Context            = 5,
+    ChainContext       = 6,
+    Extension          = 7,
+    ReverseChainSingle = 8
+  };
+
+  inline void closure (hb_closure_context_t *c,
+                      unsigned int    lookup_type) const
+  {
+    TRACE_CLOSURE ();
+    switch (lookup_type) {
+    case Single:               u.single.closure (c); break;
+    case Multiple:             u.multiple.closure (c); break;
+    case Alternate:            u.alternate.closure (c); break;
+    case Ligature:             u.ligature.closure (c); break;
+    case Context:              u.c.closure (c); break;
+    case ChainContext:         u.chainContext.closure (c); break;
+    case Extension:            u.extension.closure (c); break;
+    case ReverseChainSingle:   u.reverseChainContextSingle.closure (c); break;
+    default:                    break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id,
+                          unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Single:               return u.single.would_apply (glyph_id);
+    case Multiple:             return u.multiple.would_apply (glyph_id);
+    case Alternate:            return u.alternate.would_apply (glyph_id);
+    case Extension:            return u.extension.would_apply (glyph_id);
+    default:                   return false;
+    }
+  }
+  inline bool would_apply (hb_codepoint_t first,
+                          hb_codepoint_t second,
+                          unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Ligature:             return u.ligature.would_apply (first, second);
+    case Extension:            return u.extension.would_apply (first, second);
+    default:                   return false;
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
+  {
+    TRACE_APPLY ();
+    switch (lookup_type) {
+    case Single:               return TRACE_RETURN (u.single.apply (c));
+    case Multiple:             return TRACE_RETURN (u.multiple.apply (c));
+    case Alternate:            return TRACE_RETURN (u.alternate.apply (c));
+    case Ligature:             return TRACE_RETURN (u.ligature.apply (c));
+    case Context:              return TRACE_RETURN (u.c.apply (c));
+    case ChainContext:         return TRACE_RETURN (u.chainContext.apply (c));
+    case Extension:            return TRACE_RETURN (u.extension.apply (c));
+    case ReverseChainSingle:   return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
+    default:                   return TRACE_RETURN (false);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
+    TRACE_SANITIZE ();
+    switch (lookup_type) {
+    case Single:               return TRACE_RETURN (u.single.sanitize (c));
+    case Multiple:             return TRACE_RETURN (u.multiple.sanitize (c));
+    case Alternate:            return TRACE_RETURN (u.alternate.sanitize (c));
+    case Ligature:             return TRACE_RETURN (u.ligature.sanitize (c));
+    case Context:              return TRACE_RETURN (u.c.sanitize (c));
+    case ChainContext:         return TRACE_RETURN (u.chainContext.sanitize (c));
+    case Extension:            return TRACE_RETURN (u.extension.sanitize (c));
+    case ReverseChainSingle:   return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
+    default:                   return TRACE_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 void closure (hb_closure_context_t *c) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      get_subtable (i).closure (c, lookup_type);
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (get_subtable (i).would_apply (glyph_id, lookup_type))
+       return true;
+    return false;
+  }
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (get_subtable (i).would_apply (first, second, lookup_type))
+       return true;
+    return false;
+  }
+
+  inline bool apply_once (hb_apply_context_t *c) const
+  {
+    unsigned int lookup_type = get_type ();
+
+    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), 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 type = get_subtable(0).u.extension.get_type ();
+      unsigned int count = get_subtable_count ();
+      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_apply_context_t *c) const
+  {
+    bool ret = false;
+
+    if (unlikely (!c->buffer->len))
+      return false;
+
+    c->set_lookup (*this);
+
+    if (likely (!is_reverse ()))
+    {
+       /* in/out forward substitution */
+       c->buffer->clear_output ();
+       c->buffer->idx = 0;
+       while (c->buffer->idx < c->buffer->len)
+       {
+         if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+           ret = true;
+         else
+           c->buffer->next_glyph ();
+
+       }
+       if (ret)
+         c->buffer->swap_buffers ();
+    }
+    else
+    {
+       /* in-place backward substitution */
+       c->buffer->idx = c->buffer->len - 1;
+       do
+       {
+         if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+           ret = true;
+         else
+           c->buffer->idx--;
+
+       }
+       while ((int) c->buffer->idx >= 0);
+    }
+
+    return ret;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
+    OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
+  }
+};
+
+typedef OffsetListOf<SubstLookup> SubstLookupList;
+
+/*
+ * GSUB -- The Glyph Substitution Table
+ */
+
+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_apply_context_t *c, unsigned int lookup_index) const
+  { return get_lookup (lookup_index).apply_string (c); }
+
+  static inline void substitute_start (hb_buffer_t *buffer);
+  static inline void substitute_finish (hb_buffer_t *buffer);
+
+  inline void closure_lookup (hb_closure_context_t *c,
+                             unsigned int          lookup_index) const
+  { return get_lookup (lookup_index).closure (c); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
+    OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
+    return TRACE_RETURN (list.sanitize (c, this));
+  }
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+
+void
+GSUB::substitute_start (hb_buffer_t *buffer)
+{
+  HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
+  HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
+
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+}
+
+void
+GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED)
+{
+}
+
+
+/* Out-of-class implementation for methods recursing */
+
+inline void ExtensionSubst::closure (hb_closure_context_t *c) const
+{
+  get_subtable ().closure (c, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
+{
+  return get_subtable ().would_apply (glyph_id, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+{
+  return get_subtable ().would_apply (first, second, get_type ());
+}
+
+inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
+{
+  TRACE_APPLY ();
+  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
+}
+
+inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
+{
+  TRACE_SANITIZE ();
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
+  unsigned int offset = get_offset ();
+  if (unlikely (!offset)) return TRACE_RETURN (true);
+  return TRACE_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 void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
+{
+  const GSUB &gsub = *(c->face->ot_layout->gsub);
+  const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+  if (unlikely (c->nesting_level_left == 0))
+    return;
+
+  c->nesting_level_left--;
+  l.closure (c);
+  c->nesting_level_left++;
+}
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+{
+  const GSUB &gsub = *(c->face->ot_layout->gsub);
+  const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+  if (unlikely (c->nesting_level_left == 0))
+    return false;
+
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
+  return l.apply_once (&new_c);
+}
+
+
+
+#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
index 3dcfb21..12cb694 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
 #include "hb-buffer-private.hh"
-#include "hb-ot-layout-gdef-private.hh"
+#include "hb-ot-layout-gdef-table.hh"
 
-HB_BEGIN_DECLS
 
-/* buffer var allocations */
-#define lig_id() var2.u8[2] /* unique ligature id */
-#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
+
+/* unique ligature id */
+/* component number in the ligature (0 = base) */
+static inline void
+set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
+{
+  info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F);
+}
+static inline unsigned int
+get_lig_id (hb_glyph_info_t &info)
+{
+  return info.lig_props() >> 4;
+}
+static inline unsigned int
+get_lig_comp (hb_glyph_info_t &info)
+{
+  return info.lig_props() & 0x0F;
+}
 
 static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
-  uint8_t lig_id = buffer->next_serial ();
-  if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
+  uint8_t lig_id = buffer->next_serial () & 0x0F;
+  if (unlikely (!lig_id))
+    lig_id = allocate_lig_id (buffer); /* in case of overflow */
   return lig_id;
 }
 
 
 
+#ifndef HB_DEBUG_CLOSURE
+#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
+#endif
+
+#define TRACE_CLOSURE() \
+       hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
+
+
+/* TODO Add TRACE_RETURN annotation for would_apply */
+
+
+struct hb_closure_context_t
+{
+  hb_face_t *face;
+  hb_set_t *glyphs;
+  unsigned int nesting_level_left;
+  unsigned int debug_depth;
+
+
+  hb_closure_context_t (hb_face_t *face_,
+                       hb_set_t *glyphs_,
+                       unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
+                         face (face_), glyphs (glyphs_),
+                         nesting_level_left (nesting_level_left_),
+                         debug_depth (0) {}
+};
+
+
+
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
 
 #define TRACE_APPLY() \
-       hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC);
+       hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint);
 
 
-HB_BEGIN_DECLS
 
 struct hb_apply_context_t
 {
-  unsigned int debug_depth;
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_direction_t direction;
   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 */
+  unsigned int debug_depth;
 
 
-  inline void replace_glyph (hb_codepoint_t glyph_index) const
-  {
-    clear_property ();
-    buffer->replace_glyph (glyph_index);
+  hb_apply_context_t (hb_font_t *font_,
+                     hb_face_t *face_,
+                     hb_buffer_t *buffer_,
+                     hb_mask_t lookup_mask_) :
+                       font (font_), face (face_), buffer (buffer_),
+                       direction (buffer_->props.direction),
+                       lookup_mask (lookup_mask_),
+                       nesting_level_left (MAX_NESTING_LEVEL),
+                       lookup_props (0), property (0), debug_depth (0) {}
+
+  void set_lookup (const Lookup &l) {
+    lookup_props = l.get_props ();
   }
-  inline void replace_glyphs_be16 (unsigned int num_in,
-                                  unsigned int num_out,
-                                  const uint16_t *glyph_data_be) const
+
+  struct mark_skipping_forward_iterator_t
   {
-    clear_property ();
-    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
-  }
+    inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
+                                            unsigned int start_index_,
+                                            unsigned int num_items_,
+                                            bool context_match = false)
+    {
+      c = c_;
+      idx = start_index_;
+      num_items = num_items_;
+      mask = context_match ? -1 : c->lookup_mask;
+      syllable = context_match ? 0 : c->buffer->cur().syllable ();
+      end = c->buffer->len;
+    }
+    inline bool has_no_chance (void) const
+    {
+      return unlikely (num_items && idx + num_items >= end);
+    }
+    inline bool next (unsigned int *property_out,
+                     unsigned int lookup_props)
+    {
+      assert (num_items > 0);
+      do
+      {
+       if (has_no_chance ())
+         return false;
+       idx++;
+      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
+      num_items--;
+      return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ());
+    }
+    inline bool next (unsigned int *property_out = NULL)
+    {
+      return next (property_out, c->lookup_props);
+    }
 
-  inline void guess_glyph_class (unsigned int klass)
+    unsigned int idx;
+    private:
+    hb_apply_context_t *c;
+    unsigned int num_items;
+    hb_mask_t mask;
+    uint8_t syllable;
+    unsigned int end;
+  };
+
+  struct mark_skipping_backward_iterator_t
   {
-    /* XXX if ! has gdef */
-    buffer->info[buffer->idx].props_cache() = klass;
+    inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
+                                             unsigned int start_index_,
+                                             unsigned int num_items_,
+                                             hb_mask_t mask_ = 0,
+                                             bool match_syllable_ = true)
+    {
+      c = c_;
+      idx = start_index_;
+      num_items = num_items_;
+      mask = mask_ ? mask_ : c->lookup_mask;
+      syllable = match_syllable_ ? c->buffer->cur().syllable () : 0;
+    }
+    inline bool has_no_chance (void) const
+    {
+      return unlikely (idx < num_items);
+    }
+    inline bool prev (unsigned int *property_out,
+                     unsigned int lookup_props)
+    {
+      assert (num_items > 0);
+      do
+      {
+       if (has_no_chance ())
+         return false;
+       idx--;
+      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
+      num_items--;
+      return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ());
+    }
+    inline bool prev (unsigned int *property_out = NULL)
+    {
+      return prev (property_out, c->lookup_props);
+    }
+
+    unsigned int idx;
+    private:
+    hb_apply_context_t *c;
+    unsigned int num_items;
+    hb_mask_t mask;
+    uint8_t syllable;
+  };
+
+  inline bool should_mark_skip_current_glyph (void) const
+  {
+    return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL);
   }
 
-  private:
-  inline void clear_property (void) const
+
+
+  inline void replace_glyph (hb_codepoint_t glyph_index,
+                            unsigned int klass = 0) const
   {
-    /* XXX if has gdef */
-    buffer->info[buffer->idx].props_cache() = 0;
+    buffer->cur().props_cache() = klass; /*XXX if has gdef? */
+    buffer->replace_glyph (glyph_index);
+  }
+  inline void replace_glyphs_be16 (unsigned int num_in,
+                                  unsigned int num_out,
+                                  const uint16_t *glyph_data_be,
+                                  unsigned int klass = 0) const
+  {
+    buffer->cur().props_cache() = klass; /* XXX if has gdef? */
+    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
   }
 };
 
 
 
+typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
 typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
 
-struct ContextFuncs
+struct ContextClosureFuncs
+{
+  intersects_func_t intersects;
+  closure_lookup_func_t closure;
+};
+struct ContextApplyFuncs
 {
   match_func_t match;
   apply_lookup_func_t apply;
 };
 
+static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+{
+  return glyphs->has (value);
+}
+static inline bool intersects_class (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  return class_def.intersects_class (glyphs, value);
+}
+static inline bool intersects_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+  return (data+coverage).intersects (glyphs);
+}
+
+static inline bool intersects_array (hb_closure_context_t *c,
+                                    unsigned int count,
+                                    const USHORT values[],
+                                    intersects_func_t intersects_func,
+                                    const void *intersects_data)
+{
+  for (unsigned int i = 0; i < count; i++)
+    if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
+      return false;
+  return true;
+}
+
 
 static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
 {
@@ -123,7 +298,7 @@ static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, co
 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;
+  return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
 }
 
 
@@ -132,27 +307,23 @@ static inline bool match_input (hb_apply_context_t *c,
                                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 *end_offset = NULL)
 {
-  unsigned int i, j;
-  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
-  if (unlikely (c->buffer->idx + count > end))
+  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+  if (skippy_iter.has_no_chance ())
     return false;
 
-  for (i = 1, j = c->buffer->idx + 1; i < count; i++, j++)
+  for (unsigned int i = 1; i < count; i++)
   {
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j + count - i == end))
-       return false;
-      j++;
-    }
+    if (!skippy_iter.next ())
+      return false;
 
-    if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data)))
+    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data)))
       return false;
   }
 
-  *context_length_out = j - c->buffer->idx;
+  if (end_offset)
+    *end_offset = skippy_iter.idx - c->buffer->idx + 1;
 
   return true;
 }
@@ -163,19 +334,16 @@ static inline bool match_backtrack (hb_apply_context_t *c,
                                    match_func_t match_func,
                                    const void *match_data)
 {
-  if (unlikely (c->buffer->backtrack_len () < count))
+  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
+  if (skippy_iter.has_no_chance ())
     return false;
 
-  for (unsigned int i = 0, j = c->buffer->backtrack_len () - 1; i < count; i++, j--)
+  for (unsigned int i = 0; i < count; i++)
   {
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j + 1 == count - i))
-       return false;
-      j--;
-    }
+    if (!skippy_iter.prev ())
+      return false;
 
-    if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data)))
+    if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data)))
       return false;
   }
 
@@ -189,35 +357,29 @@ static inline bool match_lookahead (hb_apply_context_t *c,
                                    const void *match_data,
                                    unsigned int offset)
 {
-  unsigned int i, j;
-  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
-  if (unlikely (c->buffer->idx + offset + count > end))
+  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
+  if (skippy_iter.has_no_chance ())
     return false;
 
-  for (i = 0, j = c->buffer->idx + offset; i < count; i++, j++)
+  for (unsigned int i = 0; i < count; i++)
   {
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
-    {
-      if (unlikely (j + count - i == end))
-       return false;
-      j++;
-    }
+    if (!skippy_iter.next ())
+      return false;
 
-    if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data)))
+    if (likely (!match_func (c->buffer->info[skippy_iter.idx].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);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT       sequenceIndex;          /* Index into current glyph
@@ -229,7 +391,14 @@ struct LookupRecord
 };
 
 
-HB_BEGIN_DECLS
+static inline void closure_lookup (hb_closure_context_t *c,
+                                  unsigned int lookupCount,
+                                  const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
+                                  closure_lookup_func_t closure_func)
+{
+  for (unsigned int i = 0; i < lookupCount; i++)
+    closure_func (c, lookupRecord->lookupListIndex);
+}
 
 static inline bool apply_lookup (hb_apply_context_t *c,
                                 unsigned int count, /* Including the first glyph */
@@ -237,7 +406,7 @@ static inline bool apply_lookup (hb_apply_context_t *c,
                                 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
                                 apply_lookup_func_t apply_func)
 {
-  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
+  unsigned int end = c->buffer->len;
   if (unlikely (count == 0 || c->buffer->idx + count > end))
     return false;
 
@@ -250,12 +419,14 @@ static inline bool apply_lookup (hb_apply_context_t *c,
    */
   for (unsigned int i = 0; i < count; /* NOP */)
   {
-    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL))
+    if (unlikely (c->buffer->idx == end))
+      return true;
+    while (c->should_mark_skip_current_glyph ())
     {
-      if (unlikely (c->buffer->idx == end))
-       return true;
       /* No lookup applied for this index */
       c->buffer->next_glyph ();
+      if (unlikely (c->buffer->idx == end))
+       return true;
     }
 
     if (lookupCount && i == lookupRecord->sequenceIndex)
@@ -287,30 +458,49 @@ static inline bool apply_lookup (hb_apply_context_t *c,
   return true;
 }
 
-HB_END_DECLS
 
 
 /* Contextual lookups */
 
-struct ContextLookupContext
+struct ContextClosureLookupContext
 {
-  ContextFuncs funcs;
+  ContextClosureFuncs funcs;
+  const void *intersects_data;
+};
+
+struct ContextApplyLookupContext
+{
+  ContextApplyFuncs 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)
+static inline void context_closure_lookup (hb_closure_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[],
+                                          ContextClosureLookupContext &lookup_context)
+{
+  if (intersects_array (c,
+                       inputCount ? inputCount - 1 : 0, input,
+                       lookup_context.funcs.intersects, lookup_context.intersects_data))
+    closure_lookup (c,
+                   lookupCount, lookupRecord,
+                   lookup_context.funcs.closure);
+}
+
+
+static inline bool context_apply_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[],
+                                        ContextApplyLookupContext &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,
+                     lookup_context.funcs.match, lookup_context.match_data)
+      && apply_lookup (c,
                       inputCount,
                       lookupCount, lookupRecord,
                       lookup_context.funcs.apply);
@@ -321,14 +511,22 @@ struct Rule
   friend struct RuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+
+  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    context_closure_lookup (c,
+                           inputCount, input,
+                           lookupCount, lookupRecord,
+                           lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &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);
+    return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
@@ -337,13 +535,13 @@ struct Rule
     return inputCount.sanitize (c)
        && lookupCount.sanitize (c)
        && c->check_range (input,
-                                input[0].static_size * inputCount
-                                + lookupRecordX[0].static_size * lookupCount);
+                          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 sequence--includes the first
                                         * glyph */
   USHORT       lookupCount;            /* Number of LookupRecords */
   USHORT       input[VAR];             /* Array of match inputs--start with
@@ -356,22 +554,29 @@ struct Rule
 
 struct RuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      (this+rule[i]).closure (c, lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &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 TRACE_RETURN (true);
     }
-
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
@@ -388,25 +593,44 @@ struct ContextFormat1
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+
+    const Coverage &cov = (this+coverage);
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      NULL
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (cov.intersects_coverage (c->glyphs, i)) {
+       const RuleSet &rule_set = this+ruleSet[i];
+       rule_set.closure (c, lookup_context);
+      }
+  }
+
   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->idx].codepoint);
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED))
-      return false;
+      return TRACE_RETURN (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       NULL
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_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);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -427,28 +651,47 @@ struct ContextFormat2
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
+
+    const ClassDef &class_def = this+classDef;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      NULL
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (class_def.intersects_class (c->glyphs, i)) {
+       const RuleSet &rule_set = this+ruleSet[i];
+       rule_set.closure (c, lookup_context);
+      }
+  }
+
   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->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &class_def = this+classDef;
-    index = class_def (c->buffer->info[c->buffer->idx].codepoint);
+    index = class_def (c->buffer->cur().codepoint);
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       &class_def
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_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);
+    return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -472,33 +715,47 @@ struct ContextFormat3
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    if (!(this+coverage[0]).intersects (c->glyphs))
+      return;
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_coverage, closure_func},
+      this
+    };
+    context_closure_lookup (c,
+                           glyphCount, (const USHORT *) (coverage + 1),
+                           lookupCount, lookupRecord,
+                           lookup_context);
+  }
+
   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->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       this
     };
-    return context_lookup (c,
-                          glyphCount, (const USHORT *) (coverage + 1),
-                          lookupCount, lookupRecord,
-                          lookup_context);
+    return TRACE_RETURN (context_apply_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;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
-    if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
+    if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!coverage[i].sanitize (c, this)) return false;
+      if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
-    return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
+    return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
   private:
@@ -518,25 +775,37 @@ struct ContextFormat3
 struct Context
 {
   protected:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c, closure_func); break;
+    case 2: u.format2.closure (c, closure_func); break;
+    case 3: u.format3.closure (c, closure_func); break;
+    default:                                     break;
+    }
+  }
+
   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;
+    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
+    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
+    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_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;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -552,42 +821,67 @@ struct Context
 
 /* Chaining Contextual lookups */
 
-struct ChainContextLookupContext
+struct ChainContextClosureLookupContext
 {
-  ContextFuncs funcs;
+  ContextClosureFuncs funcs;
+  const void *intersects_data[3];
+};
+
+struct ChainContextApplyLookupContext
+{
+  ContextApplyFuncs 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)
+static inline void chain_context_closure_lookup (hb_closure_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[],
+                                                ChainContextClosureLookupContext &lookup_context)
 {
-  /* First guess */
-  if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
-               c->buffer->idx + inputCount + lookaheadCount > c->buffer->len ||
-               inputCount + lookaheadCount > c->context_length))
-    return false;
+  if (intersects_array (c,
+                       backtrackCount, backtrack,
+                       lookup_context.funcs.intersects, lookup_context.intersects_data[0])
+   && intersects_array (c,
+                       inputCount ? inputCount - 1 : 0, input,
+                       lookup_context.funcs.intersects, lookup_context.intersects_data[1])
+  && intersects_array (c,
+                      lookaheadCount, lookahead,
+                      lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
+    closure_lookup (c,
+                   lookupCount, lookupRecord,
+                   lookup_context.funcs.closure);
+}
 
-  hb_apply_context_t new_context = *c;
+static inline bool chain_context_apply_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[],
+                                              ChainContextApplyLookupContext &lookup_context)
+{
+  unsigned int lookahead_offset;
   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)
+                     &lookahead_offset)
       && match_lookahead (c,
                          lookaheadCount, lookahead,
                          lookup_context.funcs.match, lookup_context.match_data[2],
-                         new_context.context_length)
-      && apply_lookup (&new_context,
+                         lookahead_offset)
+      && apply_lookup (c,
                       inputCount,
                       lookupCount, lookupRecord,
                       lookup_context.funcs.apply);
@@ -598,30 +892,44 @@ struct ChainRule
   friend struct ChainRuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+
+  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    chain_context_closure_lookup (c,
+                                 backtrack.len, backtrack.array,
+                                 input.len, input.array,
+                                 lookahead.len, lookahead.array,
+                                 lookup.len, lookup.array,
+                                 lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return chain_context_lookup (c,
-                                backtrack.len, backtrack.array,
-                                input.len, input.array,
-                                lookahead.len, lookahead.array,
-                                lookup.len, lookup.array,
-                                lookup_context);
+    return TRACE_RETURN (chain_context_apply_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;
+    if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    if (!input.sanitize (c)) return false;
+    if (!input.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
-    if (!lookahead.sanitize (c)) return false;
+    if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
@@ -644,22 +952,28 @@ struct ChainRule
 
 struct ChainRuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      (this+rule[i]).closure (c, lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &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 TRACE_RETURN (true);
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
@@ -675,25 +989,42 @@ struct ChainContextFormat1
   friend struct ChainContext;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    const Coverage &cov = (this+coverage);
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      {NULL, NULL, NULL}
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (cov.intersects_coverage (c->glyphs, i)) {
+       const ChainRuleSet &rule_set = this+ruleSet[i];
+       rule_set.closure (c, lookup_context);
+      }
+  }
+
   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->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       {NULL, NULL, NULL}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_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);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -713,35 +1044,58 @@ struct ChainContextFormat2
   friend struct ChainContext;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
+
+    const ClassDef &backtrack_class_def = this+backtrackClassDef;
+    const ClassDef &input_class_def = this+inputClassDef;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (input_class_def.intersects_class (c->glyphs, i)) {
+       const ChainRuleSet &rule_set = this+ruleSet[i];
+       rule_set.closure (c, lookup_context);
+      }
+  }
+
   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->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_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->idx].codepoint);
+    index = input_class_def (c->buffer->cur().codepoint);
     const ChainRuleSet &rule_set = this+ruleSet[index];
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_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);
+    return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
+                        inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
+                        ruleSet.sanitize (c, this));
   }
 
   private:
@@ -774,38 +1128,58 @@ struct ChainContextFormat3
 
   private:
 
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    if (!(this+input[0]).intersects (c->glyphs))
+      return;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_coverage, closure_func},
+      {this, this, this}
+    };
+    chain_context_closure_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 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->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+input[0]) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext 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);
+    return TRACE_RETURN (chain_context_apply_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;
+    if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
-    if (!input.sanitize (c, this)) return false;
+    if (!input.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
-    if (!lookahead.sanitize (c, this)) return false;
+    if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
@@ -832,25 +1206,37 @@ struct ChainContextFormat3
 struct ChainContext
 {
   protected:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c, closure_func); break;
+    case 2: u.format2.closure (c, closure_func); break;
+    case 3: u.format3.closure (c, closure_func); break;
+    default:                                     break;
+    }
+  }
+
   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;
+    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
+    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
+    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_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;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -874,7 +1260,7 @@ struct ExtensionFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -907,10 +1293,10 @@ struct Extension
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -964,10 +1350,10 @@ struct GSUBGPOS
 
   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);
+    return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
+                        scriptList.sanitize (c, this) &&
+                        featureList.sanitize (c, this) &&
+                        lookupList.sanitize (c, this));
   }
 
   protected:
@@ -984,6 +1370,5 @@ struct GSUBGPOS
 };
 
 
-HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
index ef24e8d..f860e7b 100644 (file)
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
+#include "hb-ot-shape-complex-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
  * GDEF
  */
 
-/* buffer var allocations */
-#define props_cache() var1.u16[1] /* glyph_props cache */
-
 /* XXX cleanup */
 typedef enum {
   HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED        = 0x0001,
@@ -71,13 +68,6 @@ _hb_ot_layout_skip_mark (hb_face_t    *face,
                         unsigned int *property_out);
 
 
-/*
- * head
- */
-
-HB_INTERNAL unsigned int
-_hb_ot_layout_get_upem (hb_face_t    *face);
-
 
 /*
  * hb_ot_layout_t
@@ -88,12 +78,10 @@ struct hb_ot_layout_t
   hb_blob_t *gdef_blob;
   hb_blob_t *gsub_blob;
   hb_blob_t *gpos_blob;
-  hb_blob_t *head_blob;
 
   const struct GDEF *gdef;
   const struct GSUB *gsub;
   const struct GPOS *gpos;
-  const struct head *head;
 };
 
 
@@ -104,6 +92,5 @@ HB_INTERNAL void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout);
 
 
-HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_PRIVATE_HH */
index e7a1f98..0621f86 100644 (file)
  * 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 "hb-ot-head-private.hh"
-#include "hb-ot-maxp-private.hh"
+#include "hb-ot-layout-gdef-table.hh"
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-maxp-table.hh"
 
 
 #include <stdlib.h>
 #include <string.h>
 
-HB_BEGIN_DECLS
 
 
 hb_ot_layout_t *
@@ -58,9 +54,6 @@ _hb_ot_layout_create (hb_face_t *face)
   layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
   layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
 
-  layout->head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
-  layout->head = Sanitizer<head>::lock_instance (layout->head_blob);
-
   return layout;
 }
 
@@ -70,7 +63,6 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout)
   hb_blob_destroy (layout->gdef_blob);
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
-  hb_blob_destroy (layout->head_blob);
 
   free (layout);
 }
@@ -90,11 +82,6 @@ _get_gpos (hb_face_t *face)
 {
   return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS);
 }
-static inline const head&
-_get_head (hb_face_t *face)
-{
-  return likely (face->ot_layout && face->ot_layout->head) ? *face->ot_layout->head : Null(head);
-}
 
 
 /*
@@ -245,19 +232,19 @@ hb_ot_layout_table_find_script (hb_face_t    *face,
   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   if (g.find_script_index (script_tag, script_index))
-    return TRUE;
+    return true;
 
   /* try finding 'DFLT' */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
-    return FALSE;
+    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;
+    return false;
 
   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
-  return FALSE;
+  return false;
 }
 
 hb_bool_t
@@ -275,7 +262,7 @@ hb_ot_layout_table_choose_script (hb_face_t      *face,
     if (g.find_script_index (*script_tags, script_index)) {
       if (chosen_script)
         *chosen_script = *script_tags;
-      return TRUE;
+      return true;
     }
     script_tags++;
   }
@@ -284,20 +271,29 @@ hb_ot_layout_table_choose_script (hb_face_t      *face,
   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
     if (chosen_script)
       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
-    return FALSE;
+    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)) {
     if (chosen_script)
       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
-    return FALSE;
+    return false;
+  }
+
+  /* try with 'latn'; some old fonts put their features there even though
+     they're really trying to support Thai, for example :( */
+#define HB_OT_TAG_LATIN_SCRIPT         HB_TAG ('l', 'a', 't', 'n')
+  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
+    if (chosen_script)
+      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
+    return false;
   }
 
   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
   if (chosen_script)
     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
-  return FALSE;
+  return false;
 }
 
 unsigned int
@@ -337,14 +333,14 @@ hb_ot_layout_script_find_language (hb_face_t    *face,
   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;
+    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;
+    return false;
 
   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
-  return FALSE;
+  return false;
 }
 
 hb_bool_t
@@ -419,12 +415,12 @@ hb_ot_layout_language_find_feature (hb_face_t    *face,
 
     if (feature_tag == g.get_feature_tag (f_index)) {
       if (feature_index) *feature_index = f_index;
-      return TRUE;
+      return true;
     }
   }
 
   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
-  return FALSE;
+  return false;
 }
 
 unsigned int
@@ -464,7 +460,8 @@ hb_ot_layout_substitute_lookup (hb_face_t    *face,
                                unsigned int  lookup_index,
                                hb_mask_t     mask)
 {
-  return _get_gsub (face).substitute_lookup (face, buffer, lookup_index, mask);
+  hb_apply_context_t c (NULL, face, buffer, mask);
+  return _get_gsub (face).substitute_lookup (&c, lookup_index);
 }
 
 void
@@ -473,6 +470,14 @@ hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
   GSUB::substitute_finish (buffer);
 }
 
+void
+hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
+                                       hb_set_t     *glyphs,
+                                       unsigned int  lookup_index)
+{
+  hb_closure_context_t c (face, glyphs);
+  _get_gsub (face).closure_lookup (&c, lookup_index);
+}
 
 /*
  * GPOS
@@ -496,7 +501,8 @@ hb_ot_layout_position_lookup   (hb_font_t    *font,
                                unsigned int  lookup_index,
                                hb_mask_t     mask)
 {
-  return _get_gpos (font->face).position_lookup (font, buffer, lookup_index, mask);
+  hb_apply_context_t c (font, font->face, buffer, mask);
+  return _get_gpos (font->face).position_lookup (&c, lookup_index);
 }
 
 void
@@ -506,15 +512,3 @@ hb_ot_layout_position_finish (hb_buffer_t  *buffer)
 }
 
 
-/*
- * head
- */
-
-unsigned int
-_hb_ot_layout_get_upem (hb_face_t *face)
-{
-  return _get_head (face).get_upem ();
-}
-
-
-HB_END_DECLS
index 447e35d..b8b5baf 100644 (file)
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
 #ifndef HB_OT_LAYOUT_H
 #define HB_OT_LAYOUT_H
 
-#include "hb-common.h"
-#include "hb-buffer.h"
-#include "hb-font.h"
+#include "hb.h"
 
 #include "hb-ot-tag.h"
 
@@ -180,6 +182,12 @@ hb_ot_layout_substitute_lookup (hb_face_t    *face,
 void
 hb_ot_layout_substitute_finish (hb_buffer_t  *buffer);
 
+
+void
+hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
+                                       hb_set_t     *glyphs,
+                                       unsigned int  lookup_index);
+
 /*
  * GPOS
  */
index 5e6aca3..3811206 100644 (file)
@@ -33,7 +33,6 @@
 
 #include "hb-ot-layout.h"
 
-HB_BEGIN_DECLS
 
 
 static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
@@ -44,6 +43,8 @@ struct hb_ot_map_t
 
   public:
 
+  hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); }
+
   typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data);
   typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data);
 
@@ -68,6 +69,10 @@ struct hb_ot_map_t
   inline void position (hb_font_t *font, hb_buffer_t *buffer) const
   { apply (1, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_position_lookup, font, buffer); }
 
+  HB_INTERNAL void substitute_closure (hb_face_t *face,
+                                      hb_set_t *glyphs) const;
+
+
   inline void finish (void) {
     features.finish ();
     lookups[0].finish ();
@@ -137,6 +142,8 @@ struct hb_ot_map_builder_t
 {
   public:
 
+  hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); }
+
   HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global);
 
   inline void add_bool_feature (hb_tag_t tag, bool global = true)
@@ -184,6 +191,5 @@ struct hb_ot_map_builder_t
 };
 
 
-HB_END_DECLS
 
 #endif /* HB_OT_MAP_PRIVATE_HH */
index ad1290f..bebf3ed 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "hb-ot-shape-private.hh"
 
-HB_BEGIN_DECLS
 
 
 void
@@ -96,6 +95,21 @@ void hb_ot_map_t::apply (unsigned int table_index,
     apply_lookup_func (face_or_font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
 }
 
+void hb_ot_map_t::substitute_closure (hb_face_t *face,
+                                     hb_set_t *glyphs) const
+{
+  unsigned int table_index = 0;
+  unsigned int i = 0;
+
+  for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) {
+    const pause_map_t *pause = &pauses[table_index][pause_index];
+    for (; i < pause->num_lookups; i++)
+      hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
+  }
+
+  for (; i < lookups[table_index].len; i++)
+    hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
+}
 
 void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func, void *user_data)
 {
@@ -273,4 +287,3 @@ hb_ot_map_builder_t::compile (hb_face_t *face,
 }
 
 
-HB_END_DECLS
similarity index 84%
rename from src/hb-ot-maxp-private.hh
rename to src/hb-ot-maxp-table.hh
index fa2cb26..e270490 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_MAXP_PRIVATE_HH
-#define HB_OT_MAXP_PRIVATE_HH
+#ifndef HB_OT_MAXP_TABLE_HH
+#define HB_OT_MAXP_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
- * maxp
+ * maxp -- The Maximum Profile Table
  */
 
 #define HB_OT_TAG_maxp HB_TAG('m','a','x','p')
@@ -48,9 +47,8 @@ struct maxp
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) &&
-          likely (version.major == 1 ||
-                  (version.major == 0 && version.minor == 0x5000));
+    return TRACE_RETURN (c->check_struct (this) &&
+                        likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000)));
   }
 
   /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
@@ -63,6 +61,5 @@ struct maxp
 };
 
 
-HB_END_DECLS
 
-#endif /* HB_OT_MAXP_PRIVATE_HH */
+#endif /* HB_OT_MAXP_TABLE_HH */
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
new file mode 100644 (file)
index 0000000..9077c8c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2011,2012  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_NAME_TABLE_HH
+#define HB_OT_NAME_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+
+/*
+ * name -- The Naming Table
+ */
+
+#define HB_OT_TAG_name HB_TAG('n','a','m','e')
+
+
+struct NameRecord
+{
+  static int cmp (const NameRecord *a, const NameRecord *b)
+  {
+    int ret;
+    ret = b->platformID.cmp (a->platformID);
+    if (ret) return ret;
+    ret = b->encodingID.cmp (a->encodingID);
+    if (ret) return ret;
+    ret = b->languageID.cmp (a->languageID);
+    if (ret) return ret;
+    ret = b->nameID.cmp (a->nameID);
+    if (ret) return ret;
+    return 0;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+    TRACE_SANITIZE ();
+    /* We can check from base all the way up to the end of string... */
+    return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
+  }
+
+  USHORT       platformID;     /* Platform ID. */
+  USHORT       encodingID;     /* Platform-specific encoding ID. */
+  USHORT       languageID;     /* Language ID. */
+  USHORT       nameID;         /* Name ID. */
+  USHORT       length;         /* String length (in bytes). */
+  USHORT       offset;         /* String offset from start of storage area (in bytes). */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct name
+{
+  static const hb_tag_t Tag    = HB_OT_TAG_name;
+
+  inline unsigned int get_name (unsigned int platform_id,
+                               unsigned int encoding_id,
+                               unsigned int language_id,
+                               unsigned int name_id,
+                               void *buffer,
+                               unsigned int buffer_length) const
+  {
+    NameRecord key;
+    key.platformID.set (platform_id);
+    key.encodingID.set (encoding_id);
+    key.languageID.set (language_id);
+    key.nameID.set (name_id);
+    NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), (hb_compare_func_t) NameRecord::cmp);
+
+    if (!match)
+      return 0;
+
+    unsigned int length = MIN (buffer_length, (unsigned int) match->length);
+    memcpy (buffer, (char *) this + stringOffset + match->offset, length);
+    return length;
+  }
+
+  inline bool sanitize_records (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    char *string_pool = (char *) this + stringOffset;
+    unsigned int _count = count;
+    for (unsigned int i = 0; i < _count; i++)
+      if (!nameRecord[i].sanitize (c, string_pool)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE ();
+    return TRACE_RETURN (c->check_struct (this) &&
+                        likely (format == 0 || format == 1) &&
+                        c->check_array (nameRecord, nameRecord[0].static_size, count) &&
+                        sanitize_records (c));
+  }
+
+  /* We only implement format 0 for now. */
+  private:
+  USHORT       format;                 /* Format selector (=0/1). */
+  USHORT       count;                  /* Number of name records. */
+  Offset       stringOffset;           /* Offset to start of string storage (from start of table). */
+  NameRecord   nameRecord[VAR];        /* The name records where count is the number of records. */
+  public:
+  DEFINE_SIZE_ARRAY (6, nameRecord);
+};
+
+
+
+#endif /* HB_OT_NAME_TABLE_HH */
index 2b66dcc..df85086 100644 (file)
@@ -1,47 +1,20 @@
-/*
- * Copyright © 2011  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_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
-
-#include "hb-private.hh"
-
-HB_BEGIN_DECLS
-
 /* == Start of generated table == */
 /*
  * The following table is generated by running:
  *
- *   ./gen-arabic-table.py ArabicShaping.txt
+ *   ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt
  *
  * on files with these headers:
  *
  * # ArabicShaping-6.1.0.txt
  * # Date: 2011-04-15, 23:16:00 GMT [KW]
+ * UnicodeData.txt does not have a header.
  */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+
+
 static const uint8_t joining_table[] =
 {
 
@@ -753,8 +726,217 @@ static const uint8_t joining_table[] =
 #define JOINING_TABLE_FIRST    0x0600
 #define JOINING_TABLE_LAST     0x08AC
 
-/* == End of generated table == */
 
-HB_END_DECLS
+static const uint16_t shaping_table[][4] =
+{
+  {0x0621, 0x0621, 0x0621, 0xFE80}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */
+  {0x0622, 0x0622, 0xFE82, 0xFE81}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */
+  {0x0623, 0x0623, 0xFE84, 0xFE83}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */
+  {0x0624, 0x0624, 0xFE86, 0xFE85}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */
+  {0x0625, 0x0625, 0xFE88, 0xFE87}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */
+  {0xFE8B, 0xFE8C, 0xFE8A, 0xFE89}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */
+  {0x0627, 0x0627, 0xFE8E, 0xFE8D}, /* U+0627 ARABIC LETTER ALEF */
+  {0xFE91, 0xFE92, 0xFE90, 0xFE8F}, /* U+0628 ARABIC LETTER BEH */
+  {0x0629, 0x0629, 0xFE94, 0xFE93}, /* U+0629 ARABIC LETTER TEH MARBUTA */
+  {0xFE97, 0xFE98, 0xFE96, 0xFE95}, /* U+062A ARABIC LETTER TEH */
+  {0xFE9B, 0xFE9C, 0xFE9A, 0xFE99}, /* U+062B ARABIC LETTER THEH */
+  {0xFE9F, 0xFEA0, 0xFE9E, 0xFE9D}, /* U+062C ARABIC LETTER JEEM */
+  {0xFEA3, 0xFEA4, 0xFEA2, 0xFEA1}, /* U+062D ARABIC LETTER HAH */
+  {0xFEA7, 0xFEA8, 0xFEA6, 0xFEA5}, /* U+062E ARABIC LETTER KHAH */
+  {0x062F, 0x062F, 0xFEAA, 0xFEA9}, /* U+062F ARABIC LETTER DAL */
+  {0x0630, 0x0630, 0xFEAC, 0xFEAB}, /* U+0630 ARABIC LETTER THAL */
+  {0x0631, 0x0631, 0xFEAE, 0xFEAD}, /* U+0631 ARABIC LETTER REH */
+  {0x0632, 0x0632, 0xFEB0, 0xFEAF}, /* U+0632 ARABIC LETTER ZAIN */
+  {0xFEB3, 0xFEB4, 0xFEB2, 0xFEB1}, /* U+0633 ARABIC LETTER SEEN */
+  {0xFEB7, 0xFEB8, 0xFEB6, 0xFEB5}, /* U+0634 ARABIC LETTER SHEEN */
+  {0xFEBB, 0xFEBC, 0xFEBA, 0xFEB9}, /* U+0635 ARABIC LETTER SAD */
+  {0xFEBF, 0xFEC0, 0xFEBE, 0xFEBD}, /* U+0636 ARABIC LETTER DAD */
+  {0xFEC3, 0xFEC4, 0xFEC2, 0xFEC1}, /* U+0637 ARABIC LETTER TAH */
+  {0xFEC7, 0xFEC8, 0xFEC6, 0xFEC5}, /* U+0638 ARABIC LETTER ZAH */
+  {0xFECB, 0xFECC, 0xFECA, 0xFEC9}, /* U+0639 ARABIC LETTER AIN */
+  {0xFECF, 0xFED0, 0xFECE, 0xFECD}, /* U+063A ARABIC LETTER GHAIN */
+  {0x063B, 0x063B, 0x063B, 0x063B}, /* U+063B  */
+  {0x063C, 0x063C, 0x063C, 0x063C}, /* U+063C  */
+  {0x063D, 0x063D, 0x063D, 0x063D}, /* U+063D  */
+  {0x063E, 0x063E, 0x063E, 0x063E}, /* U+063E  */
+  {0x063F, 0x063F, 0x063F, 0x063F}, /* U+063F  */
+  {0x0640, 0x0640, 0x0640, 0x0640}, /* U+0640  */
+  {0xFED3, 0xFED4, 0xFED2, 0xFED1}, /* U+0641 ARABIC LETTER FEH */
+  {0xFED7, 0xFED8, 0xFED6, 0xFED5}, /* U+0642 ARABIC LETTER QAF */
+  {0xFEDB, 0xFEDC, 0xFEDA, 0xFED9}, /* U+0643 ARABIC LETTER KAF */
+  {0xFEDF, 0xFEE0, 0xFEDE, 0xFEDD}, /* U+0644 ARABIC LETTER LAM */
+  {0xFEE3, 0xFEE4, 0xFEE2, 0xFEE1}, /* U+0645 ARABIC LETTER MEEM */
+  {0xFEE7, 0xFEE8, 0xFEE6, 0xFEE5}, /* U+0646 ARABIC LETTER NOON */
+  {0xFEEB, 0xFEEC, 0xFEEA, 0xFEE9}, /* U+0647 ARABIC LETTER HEH */
+  {0x0648, 0x0648, 0xFEEE, 0xFEED}, /* U+0648 ARABIC LETTER WAW */
+  {0xFBE8, 0xFBE9, 0xFEF0, 0xFEEF}, /* U+0649 ARABIC LETTER */
+  {0xFEF3, 0xFEF4, 0xFEF2, 0xFEF1}, /* U+064A ARABIC LETTER YEH */
+  {0x064B, 0x064B, 0x064B, 0x064B}, /* U+064B  */
+  {0x064C, 0x064C, 0x064C, 0x064C}, /* U+064C  */
+  {0x064D, 0x064D, 0x064D, 0x064D}, /* U+064D  */
+  {0x064E, 0x064E, 0x064E, 0x064E}, /* U+064E  */
+  {0x064F, 0x064F, 0x064F, 0x064F}, /* U+064F  */
+  {0x0650, 0x0650, 0x0650, 0x0650}, /* U+0650  */
+  {0x0651, 0x0651, 0x0651, 0x0651}, /* U+0651  */
+  {0x0652, 0x0652, 0x0652, 0x0652}, /* U+0652  */
+  {0x0653, 0x0653, 0x0653, 0x0653}, /* U+0653  */
+  {0x0654, 0x0654, 0x0654, 0x0654}, /* U+0654  */
+  {0x0655, 0x0655, 0x0655, 0x0655}, /* U+0655  */
+  {0x0656, 0x0656, 0x0656, 0x0656}, /* U+0656  */
+  {0x0657, 0x0657, 0x0657, 0x0657}, /* U+0657  */
+  {0x0658, 0x0658, 0x0658, 0x0658}, /* U+0658  */
+  {0x0659, 0x0659, 0x0659, 0x0659}, /* U+0659  */
+  {0x065A, 0x065A, 0x065A, 0x065A}, /* U+065A  */
+  {0x065B, 0x065B, 0x065B, 0x065B}, /* U+065B  */
+  {0x065C, 0x065C, 0x065C, 0x065C}, /* U+065C  */
+  {0x065D, 0x065D, 0x065D, 0x065D}, /* U+065D  */
+  {0x065E, 0x065E, 0x065E, 0x065E}, /* U+065E  */
+  {0x065F, 0x065F, 0x065F, 0x065F}, /* U+065F  */
+  {0x0660, 0x0660, 0x0660, 0x0660}, /* U+0660  */
+  {0x0661, 0x0661, 0x0661, 0x0661}, /* U+0661  */
+  {0x0662, 0x0662, 0x0662, 0x0662}, /* U+0662  */
+  {0x0663, 0x0663, 0x0663, 0x0663}, /* U+0663  */
+  {0x0664, 0x0664, 0x0664, 0x0664}, /* U+0664  */
+  {0x0665, 0x0665, 0x0665, 0x0665}, /* U+0665  */
+  {0x0666, 0x0666, 0x0666, 0x0666}, /* U+0666  */
+  {0x0667, 0x0667, 0x0667, 0x0667}, /* U+0667  */
+  {0x0668, 0x0668, 0x0668, 0x0668}, /* U+0668  */
+  {0x0669, 0x0669, 0x0669, 0x0669}, /* U+0669  */
+  {0x066A, 0x066A, 0x066A, 0x066A}, /* U+066A  */
+  {0x066B, 0x066B, 0x066B, 0x066B}, /* U+066B  */
+  {0x066C, 0x066C, 0x066C, 0x066C}, /* U+066C  */
+  {0x066D, 0x066D, 0x066D, 0x066D}, /* U+066D  */
+  {0x066E, 0x066E, 0x066E, 0x066E}, /* U+066E  */
+  {0x066F, 0x066F, 0x066F, 0x066F}, /* U+066F  */
+  {0x0670, 0x0670, 0x0670, 0x0670}, /* U+0670  */
+  {0x0671, 0x0671, 0xFB51, 0xFB50}, /* U+0671 ARABIC LETTER ALEF WASLA */
+  {0x0672, 0x0672, 0x0672, 0x0672}, /* U+0672  */
+  {0x0673, 0x0673, 0x0673, 0x0673}, /* U+0673  */
+  {0x0674, 0x0674, 0x0674, 0x0674}, /* U+0674  */
+  {0x0675, 0x0675, 0x0675, 0x0675}, /* U+0675  */
+  {0x0676, 0x0676, 0x0676, 0x0676}, /* U+0676  */
+  {0x0677, 0x0677, 0x0677, 0xFBDD}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */
+  {0x0678, 0x0678, 0x0678, 0x0678}, /* U+0678  */
+  {0xFB68, 0xFB69, 0xFB67, 0xFB66}, /* U+0679 ARABIC LETTER TTEH */
+  {0xFB60, 0xFB61, 0xFB5F, 0xFB5E}, /* U+067A ARABIC LETTER TTEHEH */
+  {0xFB54, 0xFB55, 0xFB53, 0xFB52}, /* U+067B ARABIC LETTER BEEH */
+  {0x067C, 0x067C, 0x067C, 0x067C}, /* U+067C  */
+  {0x067D, 0x067D, 0x067D, 0x067D}, /* U+067D  */
+  {0xFB58, 0xFB59, 0xFB57, 0xFB56}, /* U+067E ARABIC LETTER PEH */
+  {0xFB64, 0xFB65, 0xFB63, 0xFB62}, /* U+067F ARABIC LETTER TEHEH */
+  {0xFB5C, 0xFB5D, 0xFB5B, 0xFB5A}, /* U+0680 ARABIC LETTER BEHEH */
+  {0x0681, 0x0681, 0x0681, 0x0681}, /* U+0681  */
+  {0x0682, 0x0682, 0x0682, 0x0682}, /* U+0682  */
+  {0xFB78, 0xFB79, 0xFB77, 0xFB76}, /* U+0683 ARABIC LETTER NYEH */
+  {0xFB74, 0xFB75, 0xFB73, 0xFB72}, /* U+0684 ARABIC LETTER DYEH */
+  {0x0685, 0x0685, 0x0685, 0x0685}, /* U+0685  */
+  {0xFB7C, 0xFB7D, 0xFB7B, 0xFB7A}, /* U+0686 ARABIC LETTER TCHEH */
+  {0xFB80, 0xFB81, 0xFB7F, 0xFB7E}, /* U+0687 ARABIC LETTER TCHEHEH */
+  {0x0688, 0x0688, 0xFB89, 0xFB88}, /* U+0688 ARABIC LETTER DDAL */
+  {0x0689, 0x0689, 0x0689, 0x0689}, /* U+0689  */
+  {0x068A, 0x068A, 0x068A, 0x068A}, /* U+068A  */
+  {0x068B, 0x068B, 0x068B, 0x068B}, /* U+068B  */
+  {0x068C, 0x068C, 0xFB85, 0xFB84}, /* U+068C ARABIC LETTER DAHAL */
+  {0x068D, 0x068D, 0xFB83, 0xFB82}, /* U+068D ARABIC LETTER DDAHAL */
+  {0x068E, 0x068E, 0xFB87, 0xFB86}, /* U+068E ARABIC LETTER DUL */
+  {0x068F, 0x068F, 0x068F, 0x068F}, /* U+068F  */
+  {0x0690, 0x0690, 0x0690, 0x0690}, /* U+0690  */
+  {0x0691, 0x0691, 0xFB8D, 0xFB8C}, /* U+0691 ARABIC LETTER RREH */
+  {0x0692, 0x0692, 0x0692, 0x0692}, /* U+0692  */
+  {0x0693, 0x0693, 0x0693, 0x0693}, /* U+0693  */
+  {0x0694, 0x0694, 0x0694, 0x0694}, /* U+0694  */
+  {0x0695, 0x0695, 0x0695, 0x0695}, /* U+0695  */
+  {0x0696, 0x0696, 0x0696, 0x0696}, /* U+0696  */
+  {0x0697, 0x0697, 0x0697, 0x0697}, /* U+0697  */
+  {0x0698, 0x0698, 0xFB8B, 0xFB8A}, /* U+0698 ARABIC LETTER JEH */
+  {0x0699, 0x0699, 0x0699, 0x0699}, /* U+0699  */
+  {0x069A, 0x069A, 0x069A, 0x069A}, /* U+069A  */
+  {0x069B, 0x069B, 0x069B, 0x069B}, /* U+069B  */
+  {0x069C, 0x069C, 0x069C, 0x069C}, /* U+069C  */
+  {0x069D, 0x069D, 0x069D, 0x069D}, /* U+069D  */
+  {0x069E, 0x069E, 0x069E, 0x069E}, /* U+069E  */
+  {0x069F, 0x069F, 0x069F, 0x069F}, /* U+069F  */
+  {0x06A0, 0x06A0, 0x06A0, 0x06A0}, /* U+06A0  */
+  {0x06A1, 0x06A1, 0x06A1, 0x06A1}, /* U+06A1  */
+  {0x06A2, 0x06A2, 0x06A2, 0x06A2}, /* U+06A2  */
+  {0x06A3, 0x06A3, 0x06A3, 0x06A3}, /* U+06A3  */
+  {0xFB6C, 0xFB6D, 0xFB6B, 0xFB6A}, /* U+06A4 ARABIC LETTER VEH */
+  {0x06A5, 0x06A5, 0x06A5, 0x06A5}, /* U+06A5  */
+  {0xFB70, 0xFB71, 0xFB6F, 0xFB6E}, /* U+06A6 ARABIC LETTER PEHEH */
+  {0x06A7, 0x06A7, 0x06A7, 0x06A7}, /* U+06A7  */
+  {0x06A8, 0x06A8, 0x06A8, 0x06A8}, /* U+06A8  */
+  {0xFB90, 0xFB91, 0xFB8F, 0xFB8E}, /* U+06A9 ARABIC LETTER KEHEH */
+  {0x06AA, 0x06AA, 0x06AA, 0x06AA}, /* U+06AA  */
+  {0x06AB, 0x06AB, 0x06AB, 0x06AB}, /* U+06AB  */
+  {0x06AC, 0x06AC, 0x06AC, 0x06AC}, /* U+06AC  */
+  {0xFBD5, 0xFBD6, 0xFBD4, 0xFBD3}, /* U+06AD ARABIC LETTER NG */
+  {0x06AE, 0x06AE, 0x06AE, 0x06AE}, /* U+06AE  */
+  {0xFB94, 0xFB95, 0xFB93, 0xFB92}, /* U+06AF ARABIC LETTER GAF */
+  {0x06B0, 0x06B0, 0x06B0, 0x06B0}, /* U+06B0  */
+  {0xFB9C, 0xFB9D, 0xFB9B, 0xFB9A}, /* U+06B1 ARABIC LETTER NGOEH */
+  {0x06B2, 0x06B2, 0x06B2, 0x06B2}, /* U+06B2  */
+  {0xFB98, 0xFB99, 0xFB97, 0xFB96}, /* U+06B3 ARABIC LETTER GUEH */
+  {0x06B4, 0x06B4, 0x06B4, 0x06B4}, /* U+06B4  */
+  {0x06B5, 0x06B5, 0x06B5, 0x06B5}, /* U+06B5  */
+  {0x06B6, 0x06B6, 0x06B6, 0x06B6}, /* U+06B6  */
+  {0x06B7, 0x06B7, 0x06B7, 0x06B7}, /* U+06B7  */
+  {0x06B8, 0x06B8, 0x06B8, 0x06B8}, /* U+06B8  */
+  {0x06B9, 0x06B9, 0x06B9, 0x06B9}, /* U+06B9  */
+  {0x06BA, 0x06BA, 0xFB9F, 0xFB9E}, /* U+06BA ARABIC LETTER NOON GHUNNA */
+  {0xFBA2, 0xFBA3, 0xFBA1, 0xFBA0}, /* U+06BB ARABIC LETTER RNOON */
+  {0x06BC, 0x06BC, 0x06BC, 0x06BC}, /* U+06BC  */
+  {0x06BD, 0x06BD, 0x06BD, 0x06BD}, /* U+06BD  */
+  {0xFBAC, 0xFBAD, 0xFBAB, 0xFBAA}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */
+  {0x06BF, 0x06BF, 0x06BF, 0x06BF}, /* U+06BF  */
+  {0x06C0, 0x06C0, 0xFBA5, 0xFBA4}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */
+  {0xFBA8, 0xFBA9, 0xFBA7, 0xFBA6}, /* U+06C1 ARABIC LETTER HEH GOAL */
+  {0x06C2, 0x06C2, 0x06C2, 0x06C2}, /* U+06C2  */
+  {0x06C3, 0x06C3, 0x06C3, 0x06C3}, /* U+06C3  */
+  {0x06C4, 0x06C4, 0x06C4, 0x06C4}, /* U+06C4  */
+  {0x06C5, 0x06C5, 0xFBE1, 0xFBE0}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */
+  {0x06C6, 0x06C6, 0xFBDA, 0xFBD9}, /* U+06C6 ARABIC LETTER OE */
+  {0x06C7, 0x06C7, 0xFBD8, 0xFBD7}, /* U+06C7 ARABIC LETTER U */
+  {0x06C8, 0x06C8, 0xFBDC, 0xFBDB}, /* U+06C8 ARABIC LETTER YU */
+  {0x06C9, 0x06C9, 0xFBE3, 0xFBE2}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */
+  {0x06CA, 0x06CA, 0x06CA, 0x06CA}, /* U+06CA  */
+  {0x06CB, 0x06CB, 0xFBDF, 0xFBDE}, /* U+06CB ARABIC LETTER VE */
+  {0xFBFE, 0xFBFF, 0xFBFD, 0xFBFC}, /* U+06CC ARABIC LETTER FARSI YEH */
+  {0x06CD, 0x06CD, 0x06CD, 0x06CD}, /* U+06CD  */
+  {0x06CE, 0x06CE, 0x06CE, 0x06CE}, /* U+06CE  */
+  {0x06CF, 0x06CF, 0x06CF, 0x06CF}, /* U+06CF  */
+  {0xFBE6, 0xFBE7, 0xFBE5, 0xFBE4}, /* U+06D0 ARABIC LETTER E */
+  {0x06D1, 0x06D1, 0x06D1, 0x06D1}, /* U+06D1  */
+  {0x06D2, 0x06D2, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */
+  {0x06D3, 0x06D3, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */
+};
+
+#define SHAPING_TABLE_FIRST    0x0621
+#define SHAPING_TABLE_LAST     0x06D3
+
+
+static const struct {
+ uint16_t first;
+ struct {
+   uint16_t second;
+   uint16_t ligature;
+ } ligatures[4];
+} ligature_table[] =
+{
+  { 0xFEDF, {
+    { 0xFE88, 0xFEF9 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */
+    { 0xFE82, 0xFEF5 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */
+    { 0xFE8E, 0xFEFB }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */
+    { 0xFE84, 0xFEF7 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */
+  }},
+  { 0xFEE0, {
+    { 0xFE88, 0xFEFA }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */
+    { 0xFE82, 0xFEF6 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */
+    { 0xFE8E, 0xFEFC }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */
+    { 0xFE84, 0xFEF8 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */
+  }},
+};
+
 
 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */
+
+/* == End of generated table == */
index 02df533..54460f0 100644 (file)
  */
 
 #include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_temporary_u16() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */
 
 
 /*
@@ -59,8 +59,6 @@ enum {
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
-  /* TODO Macroize the magic bit operations */
-
   if (likely (hb_in_range<hb_codepoint_t> (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) {
     unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST];
     if (likely (j_type != JOINING_TYPE_X))
@@ -83,7 +81,23 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ
         JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
+static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape)
+{
+  if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4)
+    return shaping_table[u - SHAPING_TABLE_FIRST][shape];
+  return u;
+}
 
+static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second)
+{
+  if (unlikely (!second)) return 0;
+  for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
+    if (ligature_table[i].first == first)
+      for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
+       if (ligature_table[i].ligatures[j].second == second)
+         return ligature_table[i].ligatures[j].ligature;
+  return 0;
+}
 
 static const hb_tag_t arabic_syriac_features[] =
 {
@@ -151,7 +165,8 @@ static const struct arabic_state_table_entry {
 
 
 void
-_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
+                                             const hb_segment_properties_t *props)
 {
   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
@@ -164,6 +179,7 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb
    */
 
   map->add_bool_feature (HB_TAG('c','c','m','p'));
+  map->add_bool_feature (HB_TAG('l','o','c','l'));
 
   map->add_gsub_pause (NULL, NULL);
 
@@ -183,14 +199,51 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
-bool
-_hb_ot_shape_complex_prefer_decomposed_arabic (void)
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_arabic (void)
 {
-  return FALSE;
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
+}
+
+
+static void
+arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  hb_codepoint_t glyph;
+
+  /* Shape to presentation forms */
+  for (unsigned int i = 0; i < count; i++) {
+    hb_codepoint_t u = buffer->info[i].codepoint;
+    hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
+    if (shaped != u && hb_font_get_glyph (font, shaped, 0, &glyph))
+      buffer->info[i].codepoint = shaped;
+  }
+
+  /* Mandatory ligatures */
+  buffer->clear_output ();
+  for (buffer->idx = 0; buffer->idx + 1 < count;) {
+    hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint,
+                                           buffer->cur(+1).codepoint);
+    if (likely (!ligature) || !(hb_font_get_glyph (font, ligature, 0, &glyph))) {
+      buffer->next_glyph ();
+      continue;
+    }
+
+    buffer->replace_glyphs (2, 1, &ligature);
+
+    /* Technically speaking we can skip marks and stuff, like the GSUB path does.
+     * But who cares, we're in fallback! */
+  }
+  for (; buffer->idx < count;)
+      buffer->next_glyph ();
+  buffer->swap_buffers ();
 }
 
 void
-_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
+_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map,
+                                        hb_buffer_t *buffer,
+                                        hb_font_t *font)
 {
   unsigned int count = buffer->len;
   unsigned int prev = 0, state = 0;
@@ -199,7 +252,7 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
 
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category());
+    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
 
     if (unlikely (this_type == JOINING_TYPE_T)) {
       buffer->info[i].arabic_shaping_action() = NONE;
@@ -218,15 +271,29 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
   }
 
   hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0};
+  hb_mask_t total_masks = 0;
   unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
-  for (unsigned int i = 0; i < num_masks; i++)
+  for (unsigned int i = 0; i < num_masks; i++) {
     mask_array[i] = map->get_1_mask (arabic_syriac_features[i]);
+    total_masks |= mask_array[i];
+  }
 
-  for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
+  if (total_masks) {
+    /* Has OpenType tables */
+    for (unsigned int i = 0; i < count; i++)
+      buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
+  } else if (buffer->props.script == HB_SCRIPT_ARABIC) {
+    /* Fallback Arabic shaping to Presentation Forms */
+    /* Pitfalls:
+     * - This path fires if user force-set init/medi/fina/isol off,
+     * - If font does not declare script 'arab', well, what to do?
+     *   Most probably it's safe to assume that init/medi/fina/isol
+     *   still mean Arabic shaping, although they do not have to.
+     */
+    arabic_fallback_shape (font, buffer);
+  }
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
 
-HB_END_DECLS
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
new file mode 100644 (file)
index 0000000..db1396b
--- /dev/null
@@ -0,0 +1,293 @@
+
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+/*
+ * Copyright © 2011,2012  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_INDIC_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
+
+#include "hb-private.hh"
+
+HB_BEGIN_DECLS
+
+
+#line 38 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+static const unsigned char _indic_syllable_machine_trans_keys[] = {
+       5u, 5u, 1u, 2u, 1u, 2u, 5u, 5u, 1u, 5u, 1u, 2u, 5u, 5u, 1u, 13u, 
+       4u, 11u, 4u, 11u, 5u, 11u, 1u, 10u, 1u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, 
+       5u, 10u, 8u, 10u, 5u, 10u, 6u, 10u, 9u, 10u, 4u, 11u, 1u, 13u, 4u, 10u, 
+       4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, 
+       4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 0
+};
+
+static const char _indic_syllable_machine_key_spans[] = {
+       1, 2, 2, 1, 5, 2, 1, 13, 
+       8, 8, 7, 10, 10, 1, 1, 7, 
+       6, 3, 6, 5, 2, 8, 13, 7, 
+       7, 6, 7, 6, 3, 1, 1, 7, 
+       7, 6, 7, 6, 3, 1, 1
+};
+
+static const unsigned char _indic_syllable_machine_index_offsets[] = {
+       0, 2, 5, 8, 10, 16, 19, 21, 
+       35, 44, 53, 61, 72, 83, 85, 87, 
+       95, 102, 106, 113, 119, 122, 131, 145, 
+       153, 161, 168, 176, 183, 187, 189, 191, 
+       199, 207, 214, 222, 229, 233, 235
+};
+
+static const char _indic_syllable_machine_indicies[] = {
+       1, 0, 2, 2, 0, 4, 4, 3, 
+       5, 3, 4, 4, 3, 3, 5, 3, 
+       7, 7, 6, 8, 6, 2, 10, 11, 
+       9, 9, 9, 9, 9, 9, 9, 9, 
+       12, 12, 9, 14, 15, 16, 16, 17, 
+       18, 19, 20, 13, 21, 15, 16, 16, 
+       17, 18, 19, 20, 13, 15, 16, 16, 
+       17, 18, 19, 20, 13, 2, 2, 13, 
+       13, 13, 22, 22, 13, 18, 19, 13, 
+       2, 2, 13, 13, 13, 13, 13, 13, 
+       18, 19, 13, 19, 13, 23, 13, 24, 
+       25, 13, 13, 17, 18, 19, 13, 25, 
+       13, 13, 17, 18, 19, 13, 17, 18, 
+       19, 13, 26, 13, 13, 17, 18, 19, 
+       13, 27, 27, 13, 18, 19, 13, 18, 
+       19, 13, 14, 28, 16, 16, 17, 18, 
+       19, 20, 13, 2, 2, 11, 13, 13, 
+       22, 22, 13, 18, 19, 13, 12, 12, 
+       13, 30, 5, 31, 32, 33, 34, 35, 
+       29, 4, 5, 31, 32, 33, 34, 35, 
+       29, 5, 31, 32, 33, 34, 35, 29, 
+       36, 37, 29, 29, 33, 34, 35, 29, 
+       37, 29, 29, 33, 34, 35, 29, 33, 
+       34, 35, 29, 35, 29, 38, 29, 40, 
+       8, 41, 41, 42, 43, 44, 39, 7, 
+       8, 41, 41, 42, 43, 44, 39, 8, 
+       41, 41, 42, 43, 44, 39, 45, 46, 
+       39, 39, 42, 43, 44, 39, 46, 39, 
+       39, 42, 43, 44, 39, 42, 43, 44, 
+       39, 44, 39, 47, 39, 0
+};
+
+static const char _indic_syllable_machine_trans_targs[] = {
+       7, 1, 8, 7, 25, 2, 7, 33, 
+       5, 7, 21, 23, 31, 7, 9, 11, 
+       0, 15, 13, 14, 18, 10, 12, 7, 
+       16, 17, 19, 20, 22, 7, 24, 3, 
+       4, 26, 29, 30, 27, 28, 7, 7, 
+       32, 6, 34, 37, 38, 35, 36, 7
+};
+
+static const char _indic_syllable_machine_trans_actions[] = {
+       1, 0, 2, 3, 2, 0, 4, 2, 
+       0, 7, 2, 2, 2, 8, 2, 0, 
+       0, 0, 0, 0, 0, 2, 0, 9, 
+       0, 0, 0, 0, 0, 10, 2, 0, 
+       0, 0, 0, 0, 0, 0, 11, 12, 
+       2, 0, 0, 0, 0, 0, 0, 13
+};
+
+static const char _indic_syllable_machine_to_state_actions[] = {
+       0, 0, 0, 0, 0, 0, 0, 5, 
+       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
+};
+
+static const char _indic_syllable_machine_from_state_actions[] = {
+       0, 0, 0, 0, 0, 0, 0, 6, 
+       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
+};
+
+static const unsigned char _indic_syllable_machine_eof_trans[] = {
+       1, 1, 4, 4, 4, 7, 7, 0, 
+       14, 14, 14, 14, 14, 14, 14, 14, 
+       14, 14, 14, 14, 14, 14, 14, 30, 
+       30, 30, 30, 30, 30, 30, 30, 40, 
+       40, 40, 40, 40, 40, 40, 40
+};
+
+static const int indic_syllable_machine_start = 7;
+static const int indic_syllable_machine_first_final = 7;
+static const int indic_syllable_machine_error = -1;
+
+static const int indic_syllable_machine_en_main = 7;
+
+
+#line 38 "../../src/hb-ot-shape-complex-indic-machine.rl"
+
+
+
+#line 79 "../../src/hb-ot-shape-complex-indic-machine.rl"
+
+
+#define process_syllable(func) \
+  HB_STMT_START { \
+    /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = syllable_serial; \
+    PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (!syllable_serial)) syllable_serial++; \
+  } HB_STMT_END
+
+static void
+find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array)
+{
+  unsigned int p, pe, eof, ts, te, act;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 170 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+       {
+       cs = indic_syllable_machine_start;
+       ts = 0;
+       te = 0;
+       act = 0;
+       }
+
+#line 101 "../../src/hb-ot-shape-complex-indic-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  uint8_t syllable_serial = 1;
+  
+#line 187 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+       {
+       int _slen;
+       int _trans;
+       const unsigned char *_keys;
+       const char *_inds;
+       if ( p == pe )
+               goto _test_eof;
+_resume:
+       switch ( _indic_syllable_machine_from_state_actions[cs] ) {
+       case 6:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {ts = p;}
+       break;
+#line 201 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+       }
+
+       _keys = _indic_syllable_machine_trans_keys + (cs<<1);
+       _inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
+
+       _slen = _indic_syllable_machine_key_spans[cs];
+       _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
+               ( info[p].indic_category()) <= _keys[1] ?
+               ( info[p].indic_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+       cs = _indic_syllable_machine_trans_targs[_trans];
+
+       if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
+               goto _again;
+
+       switch ( _indic_syllable_machine_trans_actions[_trans] ) {
+       case 2:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p+1;}
+       break;
+       case 9:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p+1;{ process_syllable (consonant_syllable); }}
+       break;
+       case 11:
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p+1;{ process_syllable (vowel_syllable); }}
+       break;
+       case 13:
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p+1;{ process_syllable (standalone_cluster); }}
+       break;
+       case 7:
+#line 75 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p+1;{ process_syllable (non_indic); }}
+       break;
+       case 8:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p;p--;{ process_syllable (consonant_syllable); }}
+       break;
+       case 10:
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p;p--;{ process_syllable (vowel_syllable); }}
+       break;
+       case 12:
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {te = p;p--;{ process_syllable (standalone_cluster); }}
+       break;
+       case 1:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {{p = ((te))-1;}{ process_syllable (consonant_syllable); }}
+       break;
+       case 3:
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {{p = ((te))-1;}{ process_syllable (vowel_syllable); }}
+       break;
+       case 4:
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {{p = ((te))-1;}{ process_syllable (standalone_cluster); }}
+       break;
+#line 263 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+       }
+
+_again:
+       switch ( _indic_syllable_machine_to_state_actions[cs] ) {
+       case 5:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+       {ts = 0;}
+       break;
+#line 272 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+       }
+
+       if ( ++p != pe )
+               goto _resume;
+       _test_eof: {}
+       if ( p == eof )
+       {
+       if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
+               _trans = _indic_syllable_machine_eof_trans[cs] - 1;
+               goto _eof_trans;
+       }
+       }
+
+       }
+
+#line 110 "../../src/hb-ot-shape-complex-indic-machine.rl"
+
+}
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
index 53dc20d..93ca29a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -53,62 +53,58 @@ SM   = 9;
 VD   = 10;
 A    = 11;
 NBSP = 12;
+DOTTEDCIRCLE = 13;
 
 c = C | Ra;
+n = N N?;
 z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
+place_holder = NBSP | DOTTEDCIRCLE;
 
-action found_consonant_syllable { found_consonant_syllable (map, buffer, mask_array, last, p); }
-action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, last, p); }
-action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); }
-action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); }
 
-action next_syllable { set_cluster (buffer, p, last); last = p; }
+consonant_syllable =   (c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail;
+vowel_syllable =       (Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail;
+standalone_cluster =   (Ra H)? place_holder n? (z? H c)* matra_group* syllable_tail;
+other =                        any;
 
-consonant_syllable =   (c.N? (z.H|H.z?))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
-vowel_syllable =       (Ra H)? V N? (z.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
-standalone_cluster =   (Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster);
-non_indic = X %(found_non_indic);
+main := |*
+       consonant_syllable      => { process_syllable (consonant_syllable); };
+       vowel_syllable          => { process_syllable (vowel_syllable); };
+       standalone_cluster      => { process_syllable (standalone_cluster); };
+       other                   => { process_syllable (non_indic); };
+*|;
 
-syllable =
-         consonant_syllable
-       | vowel_syllable
-       | standalone_cluster
-       | non_indic
-       ;
-
-main := (syllable %(next_syllable))**;
 
 }%%
 
-
-static void
-set_cluster (hb_buffer_t *buffer,
-            unsigned int start, unsigned int end)
-{
-  unsigned int cluster = buffer->info[start].cluster;
-
-  for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN (cluster, buffer->info[i].cluster);
-  for (unsigned int i = start; i < end; i++)
-    buffer->info[i].cluster = cluster;
-}
+#define process_syllable(func) \
+  HB_STMT_START { \
+    /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = syllable_serial; \
+    PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (!syllable_serial)) syllable_serial++; \
+  } HB_STMT_END
 
 static void
 find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array)
 {
-  unsigned int p, pe, eof;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
+  hb_glyph_info_t *info = buffer->info;
   %%{
     write init;
-    getkey buffer->info[p].indic_category();
+    getkey info[p].indic_category();
   }%%
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
+  uint8_t syllable_serial = 1;
   %%{
     write exec;
   }%%
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
new file mode 100644 (file)
index 0000000..64af0da
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright © 2012  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_INDIC_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+/* buffer var allocations */
+#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
+#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
+
+#define INDIC_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the OpenType spec:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum indic_category_t {
+  OT_X = 0,
+  OT_C,
+  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
+  OT_V,
+  OT_N,
+  OT_H,
+  OT_ZWNJ,
+  OT_ZWJ,
+  OT_M,
+  OT_SM,
+  OT_VD,
+  OT_A,
+  OT_NBSP,
+  OT_DOTTEDCIRCLE /* Not in the spec, but special in Uniscribe. /Very very/ special! */
+};
+
+/* Visual positions in a syllable from left to right. */
+enum indic_position_t {
+  POS_RA_TO_BECOME_REPH,
+  POS_PRE_M,
+  POS_PRE_C,
+  POS_BASE_C,
+  POS_ABOVE_C,
+  POS_BELOW_C,
+  POS_ABOVE_M,
+  POS_BELOW_M,
+  POS_POST_C,
+  POS_POST_M,
+  POS_SMVD
+};
+
+/* Categories used in IndicSyllabicCategory.txt from UCD. */
+enum indic_syllabic_category_t {
+  INDIC_SYLLABIC_CATEGORY_OTHER                        = OT_X,
+
+  INDIC_SYLLABIC_CATEGORY_AVAGRAHA             = OT_X,
+  INDIC_SYLLABIC_CATEGORY_BINDU                        = OT_SM,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT            = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD       = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL      = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER        = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL     = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER        = OT_NBSP,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED  = OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA      = OT_C,
+  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER     = OT_X,
+  INDIC_SYLLABIC_CATEGORY_NUKTA                        = OT_N,
+  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER     = OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_LETTER          = OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_MARK            = OT_X,
+  INDIC_SYLLABIC_CATEGORY_VIRAMA               = OT_H,
+  INDIC_SYLLABIC_CATEGORY_VISARGA              = OT_SM,
+  INDIC_SYLLABIC_CATEGORY_VOWEL                        = OT_V,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT      = OT_M,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT    = OT_V
+};
+
+/* Categories used in IndicSMatraCategory.txt from UCD */
+enum indic_matra_category_t {
+  INDIC_MATRA_CATEGORY_NOT_APPLICABLE          = POS_BASE_C,
+
+  INDIC_MATRA_CATEGORY_LEFT                    = POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP                     = POS_ABOVE_M,
+  INDIC_MATRA_CATEGORY_BOTTOM                  = POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_RIGHT                   = POS_POST_M,
+
+  /* We don't really care much about these since we decompose them
+   * in the generic pre-shaping layer.  They will only be used if
+   * the font does not cover the decomposition.  In which case, we
+   * define these as aliases to the place we want the split-matra
+   * glyph to show up.  Quite arbitrary.
+   *
+   * TODO: There are some split matras without Unicode decompositions.
+   * We have to figure out what to do with them.
+   */
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT                = POS_POST_M,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT          = POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM          = POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT        = POS_POST_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT            = POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT  = POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT           = POS_POST_M,
+
+  INDIC_MATRA_CATEGORY_INVISIBLE               = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_OVERSTRUCK              = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT       = INDIC_MATRA_CATEGORY_NOT_APPLICABLE
+};
+
+/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
+ * because gcc fails to optimize the latter and fills the table in at runtime. */
+#define INDIC_COMBINE_CATEGORIES(S,M) \
+  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
+   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
+   ((M << 4) | S))
+
+
+#include "hb-ot-shape-complex-indic-table.hh"
+
+/* XXX
+ * This is a hack for now.  We should:
+ * 1. Move this data into the main Indic table,
+ * and/or
+ * 2. Probe font lookups to determine consonant positions.
+ */
+static const struct consonant_position_t {
+  hb_codepoint_t u;
+  indic_position_t position;
+} consonant_positions[] = {
+  {0x0930, POS_BELOW_C},
+  {0x09AC, POS_BELOW_C},
+  {0x09AF, POS_POST_C},
+  {0x09B0, POS_BELOW_C},
+  {0x09F0, POS_BELOW_C},
+  {0x0A2F, POS_POST_C},
+  {0x0A30, POS_BELOW_C},
+  {0x0A35, POS_BELOW_C},
+  {0x0A39, POS_BELOW_C},
+  {0x0AB0, POS_BELOW_C},
+  {0x0B24, POS_BELOW_C},
+  {0x0B28, POS_BELOW_C},
+  {0x0B2C, POS_BELOW_C},
+  {0x0B2D, POS_BELOW_C},
+  {0x0B2E, POS_BELOW_C},
+  {0x0B2F, POS_POST_C},
+  {0x0B30, POS_BELOW_C},
+  {0x0B32, POS_BELOW_C},
+  {0x0B33, POS_BELOW_C},
+  {0x0B5F, POS_POST_C},
+  {0x0B71, POS_BELOW_C},
+  {0x0C15, POS_BELOW_C},
+  {0x0C16, POS_BELOW_C},
+  {0x0C17, POS_BELOW_C},
+  {0x0C18, POS_BELOW_C},
+  {0x0C19, POS_BELOW_C},
+  {0x0C1A, POS_BELOW_C},
+  {0x0C1B, POS_BELOW_C},
+  {0x0C1C, POS_BELOW_C},
+  {0x0C1D, POS_BELOW_C},
+  {0x0C1E, POS_BELOW_C},
+  {0x0C1F, POS_BELOW_C},
+  {0x0C20, POS_BELOW_C},
+  {0x0C21, POS_BELOW_C},
+  {0x0C22, POS_BELOW_C},
+  {0x0C23, POS_BELOW_C},
+  {0x0C24, POS_BELOW_C},
+  {0x0C25, POS_BELOW_C},
+  {0x0C26, POS_BELOW_C},
+  {0x0C27, POS_BELOW_C},
+  {0x0C28, POS_BELOW_C},
+  {0x0C2A, POS_BELOW_C},
+  {0x0C2B, POS_BELOW_C},
+  {0x0C2C, POS_BELOW_C},
+  {0x0C2D, POS_BELOW_C},
+  {0x0C2E, POS_BELOW_C},
+  {0x0C2F, POS_BELOW_C},
+  {0x0C30, POS_BELOW_C},
+  {0x0C32, POS_BELOW_C},
+  {0x0C33, POS_BELOW_C},
+  {0x0C35, POS_BELOW_C},
+  {0x0C36, POS_BELOW_C},
+  {0x0C37, POS_BELOW_C},
+  {0x0C38, POS_BELOW_C},
+  {0x0C39, POS_BELOW_C},
+  {0x0C95, POS_BELOW_C},
+  {0x0C96, POS_BELOW_C},
+  {0x0C97, POS_BELOW_C},
+  {0x0C98, POS_BELOW_C},
+  {0x0C99, POS_BELOW_C},
+  {0x0C9A, POS_BELOW_C},
+  {0x0C9B, POS_BELOW_C},
+  {0x0C9C, POS_BELOW_C},
+  {0x0C9D, POS_BELOW_C},
+  {0x0C9E, POS_BELOW_C},
+  {0x0C9F, POS_BELOW_C},
+  {0x0CA0, POS_BELOW_C},
+  {0x0CA1, POS_BELOW_C},
+  {0x0CA2, POS_BELOW_C},
+  {0x0CA3, POS_BELOW_C},
+  {0x0CA4, POS_BELOW_C},
+  {0x0CA5, POS_BELOW_C},
+  {0x0CA6, POS_BELOW_C},
+  {0x0CA7, POS_BELOW_C},
+  {0x0CA8, POS_BELOW_C},
+  {0x0CAA, POS_BELOW_C},
+  {0x0CAB, POS_BELOW_C},
+  {0x0CAC, POS_BELOW_C},
+  {0x0CAD, POS_BELOW_C},
+  {0x0CAE, POS_BELOW_C},
+  {0x0CAF, POS_BELOW_C},
+  {0x0CB0, POS_BELOW_C},
+  {0x0CB2, POS_BELOW_C},
+  {0x0CB3, POS_BELOW_C},
+  {0x0CB5, POS_BELOW_C},
+  {0x0CB6, POS_BELOW_C},
+  {0x0CB7, POS_BELOW_C},
+  {0x0CB8, POS_BELOW_C},
+  {0x0CB9, POS_BELOW_C},
+  {0x0CDE, POS_BELOW_C},
+  {0x0D2F, POS_POST_C},
+  {0x0D30, POS_POST_C},
+  {0x0D32, POS_BELOW_C},
+  {0x0D35, POS_POST_C},
+};
+
+/* XXX
+ * This is a hack for now.  We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
+ */
+static const hb_codepoint_t ra_chars[] = {
+  0x0930, /* Devanagari */
+  0x09B0, /* Bengali */
+  0x09F0, /* Bengali */
+  0x0A30, /* Gurmukhi */       /* No Reph */
+  0x0AB0, /* Gujarati */
+  0x0B30, /* Oriya */
+  0x0BB0, /* Tamil */          /* No Reph */
+  0x0C30, /* Telugu */         /* No Reph */
+  0x0CB0, /* Kannada */
+  0x0D30, /* Malayalam */      /* No Reph */
+};
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
index 2ee27b8..5b4b344 100644 (file)
@@ -1,36 +1,3 @@
-/*
- * Copyright © 2011  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_INDIC_TABLE_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
-
-#include "hb-private.hh"
-
-HB_BEGIN_DECLS
-
 /* == Start of generated table == */
 /*
  * The following table is generated by running:
@@ -39,57 +6,60 @@ HB_BEGIN_DECLS
  *
  * on files with these headers:
  *
- * # IndicSyllabicCategory-6.0.0.txt
- * # Date: 2010-05-25, 11:45:00 PDT [KW]
- * # IndicMatraCategory-6.0.0.txt
- * # Date: 2010-07-14, 15:03:00 PDT [KW]
- * # Blocks-6.0.0.txt
- * # Date: 2010-06-04, 11:12:00 PDT [KW]
+ * # IndicSyllabicCategory-6.1.0.txt
+ * # Date: 2011-08-31, 23:54:00 GMT [KW]
+ * # IndicMatraCategory-6.1.0.txt
+ * # Date: 2011-08-31, 23:50:00 GMT [KW]
+ * # Blocks-6.1.0.txt
+ * # Date: 2011-06-14, 18:26:00 GMT [KW, LI]
  */
 
+#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
 
-#define ISC_A  INDIC_SYLLABIC_CATEGORY_AVAGRAHA                /*   9 chars; Avagraha */
-#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU                   /*  31 chars; Bindu */
-#define ISC_C  INDIC_SYLLABIC_CATEGORY_CONSONANT               /* 116 chars; Consonant */
+
+#define ISC_A  INDIC_SYLLABIC_CATEGORY_AVAGRAHA                /*  11 chars; Avagraha */
+#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU                   /*  34 chars; Bindu */
+#define ISC_C  INDIC_SYLLABIC_CATEGORY_CONSONANT               /* 123 chars; Consonant */
 #define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD          /*   2 chars; Consonant_Dead */
-#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL         /*  16 chars; Consonant_Final */
+#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL         /*  17 chars; Consonant_Final */
 #define ISC_CHL        INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER   /*   1 chars; Consonant_Head_Letter */
 #define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL        /*  12 chars; Consonant_Medial */
 #define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER   /*   4 chars; Consonant_Placeholder */
 #define ISC_CR INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA         /*   5 chars; Consonant_Repha */
-#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED     /*   9 chars; Consonant_Subjoined */
+#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED     /*  10 chars; Consonant_Subjoined */
 #define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER        /*   1 chars; Modifying_Letter */
-#define ISC_N  INDIC_SYLLABIC_CATEGORY_NUKTA                   /*  11 chars; Nukta */
+#define ISC_N  INDIC_SYLLABIC_CATEGORY_NUKTA                   /*  12 chars; Nukta */
 #define ISC_x  INDIC_SYLLABIC_CATEGORY_OTHER                   /*   1 chars; Other */
 #define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER        /*   1 chars; Register_Shifter */
 #define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER             /*   3 chars; Tone_Letter */
 #define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK               /*  16 chars; Tone_Mark */
-#define ISC_V  INDIC_SYLLABIC_CATEGORY_VIRAMA                  /*  29 chars; Virama */
-#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA                 /*  19 chars; Visarga */
+#define ISC_V  INDIC_SYLLABIC_CATEGORY_VIRAMA                  /*  34 chars; Virama */
+#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA                 /*  25 chars; Visarga */
 #define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL                   /*   5 chars; Vowel */
-#define ISC_M  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT         /* 161 chars; Vowel_Dependent */
-#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT       /*  53 chars; Vowel_Independent */
+#define ISC_M  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT         /* 165 chars; Vowel_Dependent */
+#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT       /*  59 chars; Vowel_Independent */
 
-#define IMC_B  INDIC_MATRA_CATEGORY_BOTTOM                     /*  60 chars; Bottom */
+#define IMC_B  INDIC_MATRA_CATEGORY_BOTTOM                     /*  65 chars; Bottom */
 #define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT           /*   2 chars; Bottom_And_Right */
-#define IMC_I  INDIC_MATRA_CATEGORY_INVISIBLE                  /*   4 chars; Invisible */
-#define IMC_L  INDIC_MATRA_CATEGORY_LEFT                       /*  25 chars; Left */
+#define IMC_I  INDIC_MATRA_CATEGORY_INVISIBLE                  /*   6 chars; Invisible */
+#define IMC_L  INDIC_MATRA_CATEGORY_LEFT                       /*  30 chars; Left */
 #define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT             /*   8 chars; Left_And_Right */
 #define IMC_x  INDIC_MATRA_CATEGORY_NOT_APPLICABLE             /*   1 chars; Not_Applicable */
 #define IMC_O  INDIC_MATRA_CATEGORY_OVERSTRUCK                 /*   2 chars; Overstruck */
-#define IMC_R  INDIC_MATRA_CATEGORY_RIGHT                      /*  70 chars; Right */
-#define IMC_T  INDIC_MATRA_CATEGORY_TOP                        /*  74 chars; Top */
-#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM             /*   5 chars; Top_And_Bottom */
+#define IMC_R  INDIC_MATRA_CATEGORY_RIGHT                      /*  75 chars; Right */
+#define IMC_T  INDIC_MATRA_CATEGORY_TOP                        /*  83 chars; Top */
+#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM             /*   6 chars; Top_And_Bottom */
 #define IMC_TBR        INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT   /*   1 chars; Top_And_Bottom_And_Right */
 #define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT               /*   4 chars; Top_And_Left */
 #define IMC_TLR        INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT     /*   2 chars; Top_And_Left_And_Right */
-#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT              /*   7 chars; Top_And_Right */
+#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT              /*   8 chars; Top_And_Right */
 #define IMC_VOL        INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT          /*   5 chars; Visual_Order_Left */
 
 #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)
 
 
-static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
+static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = {
 
 
 #define indic_offset_0x0900 0
@@ -535,9 +505,9 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
   /* 1B90 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* 1B98 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* 1BA0 */  _(C,x), _(CS,x), _(CS,x), _(CS,x),  _(M,T),  _(M,B),  _(M,L),  _(M,R),
-  /* 1BA8 */  _(M,T),  _(M,T),  _(V,R),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),
+  /* 1BA8 */  _(M,T),  _(M,T),  _(V,R),  _(V,x), _(CS,x), _(CS,x),  _(C,x),  _(C,x),
   /* 1BB0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 1BB8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1BB8 */  _(x,x),  _(x,x),  _(A,x),  _(C,x),  _(C,x),  _(C,x), _(CF,x), _(CF,x),
 
   /* Batak  (1BC0..1BFF) */
 
@@ -563,7 +533,19 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
   /* 1C40 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
   /* 1C48 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),
 
-#define indic_offset_0xa800 2976
+#define indic_offset_0x1cd0 2976
+
+
+  /* Vedic Extensions  (1CD0..1CFF) */
+
+  /* 1CD0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CD8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CE0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CE8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CF0 */  _(x,x),  _(x,x), _(Vs,x), _(Vs,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CF8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+
+#define indic_offset_0xa800 3024
 
 
   /* Syloti Nagri  (A800..A82F) */
@@ -697,21 +679,28 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
   /* AAD0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
   /* AAD8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
-#define indic_offset_0xabc0 3712
+  /* Meetei Mayek Extensions  (AAE0..AAFF) */
+
+  /* AAE0 */ _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* AAE8 */  _(C,x),  _(C,x),  _(C,x),  _(M,L),  _(M,B),  _(M,T),  _(M,L),  _(M,R),
+  /* AAF0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Vs,x),  _(V,I),  _(x,x),
+  /* AAF8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+
+#define indic_offset_0xabc0 3792
 
 
   /* Meetei Mayek  (ABC0..ABFF) */
 
   /* ABC0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* ABC8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* ABD0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* ABC8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x), _(VI,x), _(VI,x),
+  /* ABD0 */  _(C,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* ABD8 */  _(C,x),  _(C,x),  _(C,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x),
   /* ABE0 */ _(CF,x), _(CF,x), _(CF,x),  _(M,R),  _(M,R),  _(M,T),  _(M,R),  _(M,R),
   /* ABE8 */  _(M,B),  _(M,R),  _(M,R),  _(x,x), _(TM,x),  _(V,B),  _(x,x),  _(x,x),
   /* ABF0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
   /* ABF8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
-#define indic_offset_0x10a00 3776
+#define indic_offset_0x10a00 3856
 
 
   /* Kharoshthi  (10A00..10A5F) */
@@ -729,7 +718,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
   /* 10A50 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
   /* 10A58 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
-#define indic_offset_0x11000 3872
+#define indic_offset_0x11000 3952
 
 
   /* Brahmi  (11000..1107F) */
@@ -764,9 +753,59 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = {
   /* 110C0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
   /* 110C8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
-#define indic_offset_total 4080
+#define indic_offset_0x11100 4160
+
+
+  /* Chakma  (11100..1114F) */
+
+  /* 11100 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),
+  /* 11108 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11110 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11118 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11120 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(M,T),
+  /* 11128 */  _(M,T),  _(M,T),  _(M,B),  _(M,B),  _(M,L),  _(M,T), _(M,TB), _(M,TB),
+  /* 11130 */  _(M,T),  _(M,B),  _(M,B),  _(V,I),  _(V,T),  _(x,x),  _(x,x),  _(x,x),
+  /* 11138 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 11140 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 11148 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+
+#define indic_offset_0x11180 4240
+
+
+  /* Sharada  (11180..111DF) */
+
+  /* 11180 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+  /* 11188 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+  /* 11190 */ _(VI,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11198 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 111A0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 111A8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 111B0 */  _(C,x),  _(C,x),  _(C,x),  _(M,R),  _(M,L),  _(M,R),  _(M,B),  _(M,B),
+  /* 111B8 */  _(M,B),  _(M,B),  _(M,B),  _(M,B),  _(M,T),  _(M,T),  _(M,T), _(M,TR),
+  /* 111C0 */  _(V,R),  _(A,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 111C8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 111D0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 111D8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
-};
+#define indic_offset_0x11680 4336
+
+
+  /* Takri  (11680..116CF) */
+
+  /* 11680 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+  /* 11688 */ _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11690 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 11698 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 116A0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
+  /* 116A8 */  _(C,x),  _(C,x),  _(C,x), _(Bi,x), _(Vs,x),  _(M,T),  _(M,L),  _(M,R),
+  /* 116B0 */  _(M,B),  _(M,B),  _(M,T),  _(M,T),  _(M,T),  _(M,T),  _(V,T),  _(N,x),
+  /* 116B8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 116C0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 116C8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+
+#define indic_offset_total 4416
+
+}; /* Table occupancy: 60% */
 
 static INDIC_TABLE_ELEMENT_TYPE
 get_indic_categories (hb_codepoint_t u)
@@ -775,10 +814,14 @@ get_indic_categories (hb_codepoint_t u)
   if (0x1700 <= u && u <= 0x1800) return indic_table[u - 0x1700 + indic_offset_0x1700];
   if (0x1900 <= u && u <= 0x1AB0) return indic_table[u - 0x1900 + indic_offset_0x1900];
   if (0x1B00 <= u && u <= 0x1C50) return indic_table[u - 0x1B00 + indic_offset_0x1b00];
-  if (0xA800 <= u && u <= 0xAAE0) return indic_table[u - 0xA800 + indic_offset_0xa800];
+  if (0x1CD0 <= u && u <= 0x1D00) return indic_table[u - 0x1CD0 + indic_offset_0x1cd0];
+  if (0xA800 <= u && u <= 0xAB00) return indic_table[u - 0xA800 + indic_offset_0xa800];
   if (0xABC0 <= u && u <= 0xAC00) return indic_table[u - 0xABC0 + indic_offset_0xabc0];
   if (0x10A00 <= u && u <= 0x10A60) return indic_table[u - 0x10A00 + indic_offset_0x10a00];
   if (0x11000 <= u && u <= 0x110D0) return indic_table[u - 0x11000 + indic_offset_0x11000];
+  if (0x11100 <= u && u <= 0x11150) return indic_table[u - 0x11100 + indic_offset_0x11100];
+  if (0x11180 <= u && u <= 0x111E0) return indic_table[u - 0x11180 + indic_offset_0x11180];
+  if (0x11680 <= u && u <= 0x116D0) return indic_table[u - 0x11680 + indic_offset_0x11680];
   if (unlikely (u == 0x00A0)) return _(CP,x);
   if (unlikely (u == 0x25CC)) return _(CP,x);
   return _(x,x);
@@ -824,9 +867,6 @@ get_indic_categories (hb_codepoint_t u)
 #undef IMC_TR
 #undef IMC_VOL
 
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */
 
 /* == End of generated table == */
-
-HB_END_DECLS
-
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */
index fceb076..f168fe1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-private.hh"
 
-HB_BEGIN_DECLS
-
-
-/* buffer var allocations */
-#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
-#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
-
-#define INDIC_TABLE_ELEMENT_TYPE uint8_t
-
-/* Cateories used in the OpenType spec:
- * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum indic_category_t {
-  OT_X = 0,
-  OT_C,
-  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
-  OT_V,
-  OT_N,
-  OT_H,
-  OT_ZWNJ,
-  OT_ZWJ,
-  OT_M,
-  OT_SM,
-  OT_VD,
-  OT_A,
-  OT_NBSP
+struct indic_options_t
+{
+  int initialized : 1;
+  int uniscribe_bug_compatible : 1;
 };
 
-/* Visual positions in a syllable from left to right. */
-enum indic_position_t {
-  POS_PRE,
-  POS_BASE,
-  POS_ABOVE,
-  POS_BELOW,
-  POS_POST,
+union indic_options_union_t {
+  int i;
+  indic_options_t opts;
 };
+ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t));
 
-/* Categories used in IndicSyllabicCategory.txt from UCD */
-/* The assignments are guesswork */
-enum indic_syllabic_category_t {
-  INDIC_SYLLABIC_CATEGORY_OTHER                        = OT_X,
-
-  INDIC_SYLLABIC_CATEGORY_AVAGRAHA             = OT_X,
-  INDIC_SYLLABIC_CATEGORY_BINDU                        = OT_SM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT            = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD       = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL      = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER        = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL     = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER        = OT_NBSP,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED  = OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA      = OT_C,
-  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER     = OT_X,
-  INDIC_SYLLABIC_CATEGORY_NUKTA                        = OT_N,
-  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER     = OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_LETTER          = OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_MARK            = OT_X,
-  INDIC_SYLLABIC_CATEGORY_VIRAMA               = OT_H,
-  INDIC_SYLLABIC_CATEGORY_VISARGA              = OT_SM,
-  INDIC_SYLLABIC_CATEGORY_VOWEL                        = OT_V,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT      = OT_M,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT    = OT_V
-};
+static indic_options_union_t
+indic_options_init (void)
+{
+  indic_options_union_t u;
+  u.i = 0;
+  u.opts.initialized = 1;
 
-/* Categories used in IndicSMatraCategory.txt from UCD */
-enum indic_matra_category_t {
-  INDIC_MATRA_CATEGORY_NOT_APPLICABLE          = POS_BASE,
-
-  INDIC_MATRA_CATEGORY_LEFT                    = POS_PRE,
-  INDIC_MATRA_CATEGORY_TOP                     = POS_ABOVE,
-  INDIC_MATRA_CATEGORY_BOTTOM                  = POS_BELOW,
-  INDIC_MATRA_CATEGORY_RIGHT                   = POS_POST,
-
-  /* We don't really care much about these since we decompose them
-   * in the generic pre-shaping layer.  They will only be used if
-   * the font does not cover the decomposition.  In which case, we
-   * define these as aliases to the place we want the split-matra
-   * glyph to show up.  Quite arbitrary. */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT                = INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT          = INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM          = INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT        = INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT            = INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT  = INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT           = INDIC_MATRA_CATEGORY_RIGHT,
-
-  INDIC_MATRA_CATEGORY_INVISIBLE               = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_OVERSTRUCK              = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT       = INDIC_MATRA_CATEGORY_NOT_APPLICABLE
-};
+  char *c = getenv ("HB_OT_INDIC_OPTIONS");
+  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+
+  return u;
+}
 
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
-#define INDIC_COMBINE_CATEGORIES(S,M) \
-  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
-   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
-   ((M << 4) | S))
+inline indic_options_t
+indic_options (void)
+{
+  static indic_options_union_t options;
 
-#include "hb-ot-shape-complex-indic-table.hh"
+  if (unlikely (!options.i)) {
+    /* This is idempotent and threadsafe. */
+    options = indic_options_init ();
+  }
 
-/* XXX
- * This is a hack for now.  We should:
- * 1. Move this data into the main Indic table,
- * and/or
- * 2. Probe font lookups to determine consonant positions.
- */
-static const struct consonant_position_t {
-  hb_codepoint_t u;
-  indic_position_t position;
-} consonant_positions[] = {
-  {0x0930, POS_BELOW},
-  {0x09AC, POS_BELOW},
-  {0x09AF, POS_POST},
-  {0x09B0, POS_BELOW},
-  {0x09F0, POS_BELOW},
-  {0x0A2F, POS_POST},
-  {0x0A30, POS_BELOW},
-  {0x0A35, POS_BELOW},
-  {0x0A39, POS_BELOW},
-  {0x0AB0, POS_BELOW},
-  {0x0B24, POS_BELOW},
-  {0x0B28, POS_BELOW},
-  {0x0B2C, POS_BELOW},
-  {0x0B2D, POS_BELOW},
-  {0x0B2E, POS_BELOW},
-  {0x0B2F, POS_POST},
-  {0x0B30, POS_BELOW},
-  {0x0B32, POS_BELOW},
-  {0x0B33, POS_BELOW},
-  {0x0B5F, POS_POST},
-  {0x0B71, POS_BELOW},
-  {0x0C15, POS_BELOW},
-  {0x0C16, POS_BELOW},
-  {0x0C17, POS_BELOW},
-  {0x0C18, POS_BELOW},
-  {0x0C19, POS_BELOW},
-  {0x0C1A, POS_BELOW},
-  {0x0C1B, POS_BELOW},
-  {0x0C1C, POS_BELOW},
-  {0x0C1D, POS_BELOW},
-  {0x0C1E, POS_BELOW},
-  {0x0C1F, POS_BELOW},
-  {0x0C20, POS_BELOW},
-  {0x0C21, POS_BELOW},
-  {0x0C22, POS_BELOW},
-  {0x0C23, POS_BELOW},
-  {0x0C24, POS_BELOW},
-  {0x0C25, POS_BELOW},
-  {0x0C26, POS_BELOW},
-  {0x0C27, POS_BELOW},
-  {0x0C28, POS_BELOW},
-  {0x0C2A, POS_BELOW},
-  {0x0C2B, POS_BELOW},
-  {0x0C2C, POS_BELOW},
-  {0x0C2D, POS_BELOW},
-  {0x0C2E, POS_BELOW},
-  {0x0C2F, POS_BELOW},
-  {0x0C30, POS_BELOW},
-  {0x0C32, POS_BELOW},
-  {0x0C33, POS_BELOW},
-  {0x0C35, POS_BELOW},
-  {0x0C36, POS_BELOW},
-  {0x0C37, POS_BELOW},
-  {0x0C38, POS_BELOW},
-  {0x0C39, POS_BELOW},
-  {0x0C95, POS_BELOW},
-  {0x0C96, POS_BELOW},
-  {0x0C97, POS_BELOW},
-  {0x0C98, POS_BELOW},
-  {0x0C99, POS_BELOW},
-  {0x0C9A, POS_BELOW},
-  {0x0C9B, POS_BELOW},
-  {0x0C9C, POS_BELOW},
-  {0x0C9D, POS_BELOW},
-  {0x0C9E, POS_BELOW},
-  {0x0C9F, POS_BELOW},
-  {0x0CA0, POS_BELOW},
-  {0x0CA1, POS_BELOW},
-  {0x0CA2, POS_BELOW},
-  {0x0CA3, POS_BELOW},
-  {0x0CA4, POS_BELOW},
-  {0x0CA5, POS_BELOW},
-  {0x0CA6, POS_BELOW},
-  {0x0CA7, POS_BELOW},
-  {0x0CA8, POS_BELOW},
-  {0x0CAA, POS_BELOW},
-  {0x0CAB, POS_BELOW},
-  {0x0CAC, POS_BELOW},
-  {0x0CAD, POS_BELOW},
-  {0x0CAE, POS_BELOW},
-  {0x0CAF, POS_BELOW},
-  {0x0CB0, POS_BELOW},
-  {0x0CB2, POS_BELOW},
-  {0x0CB3, POS_BELOW},
-  {0x0CB5, POS_BELOW},
-  {0x0CB6, POS_BELOW},
-  {0x0CB7, POS_BELOW},
-  {0x0CB8, POS_BELOW},
-  {0x0CB9, POS_BELOW},
-  {0x0CDE, POS_BELOW},
-  {0x0D2F, POS_POST},
-  {0x0D30, POS_POST},
-  {0x0D32, POS_BELOW},
-  {0x0D35, POS_POST},
-};
+  return options.opts;
+}
 
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930, /* Devanagari */
-  0x09B0, /* Bengali */
-  0x09F0, /* Bengali */
-//0x09F1, /* Bengali */
-//0x0A30, /* Gurmukhi */
-  0x0AB0, /* Gujarati */
-  0x0B30, /* Oriya */
-//0x0BB0, /* Tamil */
-//0x0C30, /* Telugu */
-  0x0CB0, /* Kannada */
-//0x0D30, /* Malayalam */
-};
 
 static int
 compare_codepoint (const void *pa, const void *pb)
@@ -270,7 +85,7 @@ consonant_position (hb_codepoint_t u)
                                             sizeof (consonant_positions[0]),
                                             compare_codepoint);
 
-  return record ? record->position : POS_BASE;
+  return record ? record->position : POS_BASE_C;
 }
 
 static bool
@@ -291,24 +106,32 @@ is_joiner (const hb_glyph_info_t &info)
 static bool
 is_consonant (const hb_glyph_info_t &info)
 {
-  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra)));
+  /* Note:
+   *
+   * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+   * cannot happen in a consonant syllable.  The plus side however is, we can call the
+   * consonant syllable logic from the vowel syllable function and get it all right! */
+  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)));
 }
 
-static const struct {
+struct feature_list_t {
   hb_tag_t tag;
   hb_bool_t is_global;
-} indic_basic_features[] =
+};
+
+static const feature_list_t
+indic_basic_features[] =
 {
   {HB_TAG('n','u','k','t'), true},
   {HB_TAG('a','k','h','n'), false},
   {HB_TAG('r','p','h','f'), false},
-  {HB_TAG('r','k','r','f'), false},
+  {HB_TAG('r','k','r','f'), true},
   {HB_TAG('p','r','e','f'), false},
   {HB_TAG('b','l','w','f'), false},
   {HB_TAG('h','a','l','f'), false},
-  {HB_TAG('v','a','t','u'), true},
   {HB_TAG('p','s','t','f'), false},
   {HB_TAG('c','j','c','t'), false},
+  {HB_TAG('v','a','t','u'), true},
 };
 
 /* Same order as the indic_basic_features array */
@@ -316,26 +139,33 @@ enum {
   _NUKT,
   AKHN,
   RPHF,
-  RKRF,
+  _RKRF,
   PREF,
   BLWF,
   HALF,
-  _VATU,
   PSTF,
   CJCT,
+  VATU
 };
 
-static const hb_tag_t indic_other_features[] =
+static const feature_list_t
+indic_other_features[] =
 {
-  HB_TAG('p','r','e','s'),
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('p','s','t','s'),
-  HB_TAG('h','a','l','n'),
-
-  HB_TAG('d','i','s','t'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
+  {HB_TAG('i','n','i','t'), false},
+  {HB_TAG('p','r','e','s'), true},
+  {HB_TAG('a','b','v','s'), true},
+  {HB_TAG('b','l','w','s'), true},
+  {HB_TAG('p','s','t','s'), true},
+  {HB_TAG('h','a','l','n'), true},
+
+  {HB_TAG('d','i','s','t'), true},
+  {HB_TAG('a','b','v','m'), true},
+  {HB_TAG('b','l','w','m'), true},
+};
+
+/* Same order as the indic_other_features array */
+enum {
+  INIT
 };
 
 
@@ -351,7 +181,8 @@ final_reordering (const hb_ot_map_t *map,
                  void *user_data HB_UNUSED);
 
 void
-_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map,
+                                            const hb_segment_properties_t *props HB_UNUSED)
 {
   map->add_bool_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
@@ -360,26 +191,32 @@ _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_
 
   map->add_gsub_pause (initial_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) {
     map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global);
+    map->add_gsub_pause (NULL, NULL);
+  }
 
   map->add_gsub_pause (final_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++)
-    map->add_bool_feature (indic_other_features[i], true);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) {
+    map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global);
+    map->add_gsub_pause (NULL, NULL);
+  }
 }
 
 
-bool
-_hb_ot_shape_complex_prefer_decomposed_indic (void)
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_indic (void)
 {
   /* We want split matras decomposed by the common shaping logic. */
-  return TRUE;
+  return HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
 }
 
 
 void
-_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer)
+_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED,
+                                       hb_buffer_t *buffer,
+                                       hb_font_t *font HB_UNUSED)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
@@ -390,19 +227,36 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer)
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int type = get_indic_categories (buffer->info[i].codepoint);
-
-    buffer->info[i].indic_category() = type & 0x0F;
-    buffer->info[i].indic_position() = type >> 4;
-
-    if (buffer->info[i].indic_category() == OT_C) {
-      buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
-      if (is_ra (buffer->info[i].codepoint))
-       buffer->info[i].indic_category() = OT_Ra;
-    } else if (buffer->info[i].codepoint == 0x200C)
-      buffer->info[i].indic_category() = OT_ZWNJ;
-    else if (buffer->info[i].codepoint == 0x200D)
-      buffer->info[i].indic_category() = OT_ZWJ;
+    hb_glyph_info_t &info = buffer->info[i];
+    unsigned int type = get_indic_categories (info.codepoint);
+
+    info.indic_category() = type & 0x0F;
+    info.indic_position() = type >> 4;
+
+    /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
+     * treats U+0951..U+0952 all as OT_VD.
+     * TESTS:
+     * U+092E,U+0947,U+0952
+     * U+092E,U+0952,U+0947
+     * U+092E,U+0947,U+0951
+     * U+092E,U+0951,U+0947
+     * */
+    if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954)))
+      info.indic_category() = OT_VD;
+
+    if (info.indic_category() == OT_C) {
+      info.indic_position() = consonant_position (info.codepoint);
+      if (is_ra (info.codepoint))
+       info.indic_category() = OT_Ra;
+    } else if (info.indic_category() == OT_SM ||
+              info.indic_category() == OT_VD) {
+      info.indic_position() = POS_SMVD;
+    } else if (unlikely (info.codepoint == 0x200C))
+      info.indic_category() = OT_ZWNJ;
+    else if (unlikely (info.codepoint == 0x200D))
+      info.indic_category() = OT_ZWJ;
+    else if (unlikely (info.codepoint == 0x25CC))
+      info.indic_category() = OT_DOTTEDCIRCLE;
   }
 }
 
@@ -415,15 +269,15 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
+/* Rules from:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
+
 static void
-found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-                         unsigned int start, unsigned int end)
+initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+                                      unsigned int start, unsigned int end)
 {
-  unsigned int i;
   hb_glyph_info_t *info = buffer->info;
 
-  /* Comments from:
-   * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
 
   /* 1. Find base consonant:
    *
@@ -440,52 +294,65 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
    */
 
   unsigned int base = end;
+  bool has_reph = false;
 
-  /* -> starting from the end of the syllable, move backwards */
-  i = end;
-  unsigned int limit = start;
-  if (info[start].indic_category() == OT_Ra && start + 2 <= end) {
-    limit += 2;
-    base = start;
-  };
-  do {
-    i--;
-    /* -> until a consonant is found */
-    if (is_consonant (info[i]))
+  {
+    /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+     *    and has more than one consonant, Ra is excluded from candidates for
+     *    base consonants. */
+    unsigned int limit = start;
+    if (mask_array[RPHF] &&
+       start + 3 <= end &&
+       info[start].indic_category() == OT_Ra &&
+       info[start + 1].indic_category() == OT_H &&
+       !is_joiner (info[start + 2]))
     {
-      /* -> that does not have a below-base or post-base form
-       * (post-base forms have to follow below-base forms), */
-      if (info[i].indic_position() != POS_BELOW &&
-         info[i].indic_position() != POS_POST)
+      limit += 2;
+      base = start;
+      has_reph = true;
+    };
+
+    /* -> starting from the end of the syllable, move backwards */
+    unsigned int i = end;
+    do {
+      i--;
+      /* -> until a consonant is found */
+      if (is_consonant (info[i]))
       {
-        base = i;
-       break;
-      }
-
-      /* -> or that is not a pre-base reordering Ra,
-       *
-       * TODO
-       */
+       /* -> that does not have a below-base or post-base form
+        * (post-base forms have to follow below-base forms), */
+       if (info[i].indic_position() != POS_BELOW_C &&
+           info[i].indic_position() != POS_POST_C)
+       {
+         base = i;
+         break;
+       }
 
-      /* ->  o If the syllable starts with Ra + Halant (in a script that has Reph)
-       *       and has more than one consonant, Ra is excluded from candidates for
-       *       base consonants.
-       *
-       * IMPLEMENTATION NOTES:
-       *
-       * We do this by adjusting limit accordingly before entering the loop.
-       */
+       /* -> or that is not a pre-base reordering Ra,
+        *
+        * TODO
+        */
 
-      /* -> or arrive at the first consonant. The consonant stopped at will
-       * be the base. */
-      base = i;
+       /* -> or arrive at the first consonant. The consonant stopped at will
+        * be the base. */
+       base = i;
+      }
+      else
+       if (is_joiner (info[i]))
+         break;
+    } while (i > limit);
+    if (base < start)
+      base = start; /* Just in case... */
+
+
+    /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+     *    and has more than one consonant, Ra is excluded from candidates for
+     *    base consonants. */
+    if (has_reph && base == start) {
+      /* Have no other consonant, so Reph is not formed and Ra becomes base. */
+      has_reph = false;
     }
-    else
-      if (is_joiner (info[i]))
-        break;
-  } while (i > limit);
-  if (base < start)
-    base = start; /* Just in case... */
+  }
 
 
   /* 2. Decompose and reorder Matras:
@@ -522,30 +389,23 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
 
   /* Reorder characters */
 
-  for (i = start; i < base; i++)
-    info[i].indic_position() = POS_PRE;
-  info[base].indic_position() = POS_BASE;
-
+  for (unsigned int i = start; i < base; i++)
+    info[i].indic_position() = POS_PRE_C;
+  info[base].indic_position() = POS_BASE_C;
 
   /* Handle beginning Ra */
-  if (start + 3 <= end &&
-      info[start].indic_category() == OT_Ra &&
-      info[start + 1].indic_category() == OT_H &&
-      !is_joiner (info[start + 2]))
-   {
-    info[start].indic_position() = POS_POST;
-    info[start].mask = mask_array[RPHF];
-   }
+  if (has_reph)
+    info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant. */
   if ((map->get_chosen_script (0) & 0x000000FF) != '2') {
     /* We should only do this for Indic scripts which have a version two I guess. */
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H) {
         unsigned int j;
         for (j = end - 1; j > i; j--)
-         if ((FLAG (info[j].indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra))))
+         if (is_consonant (info[j]))
            break;
        if (j > i) {
          /* Move Halant to after last consonant. */
@@ -558,38 +418,68 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
   }
 
   /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */
-  for (i = start + 1; i < end; i++)
-    if ((FLAG (info[i].indic_category()) &
-        (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
-      info[i].indic_position() = info[i - 1].indic_position();
+  if (!indic_options ().uniscribe_bug_compatible)
+  {
+    /* Please update the Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
+       info[i].indic_position() = info[i - 1].indic_position();
+  } else {
+    /*
+     * Uniscribe doesn't move the Halant with Left Matra.
+     * TEST: U+092B,U+093F,U+094DE
+     */
+    /* Please update the non-Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
+       info[i].indic_position() = info[i - 1].indic_position();
+       if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M)
+         for (unsigned int j = i; j > start; j--)
+           if (info[j - 1].indic_position() != POS_PRE_M) {
+             info[i].indic_position() = info[j - 1].indic_position();
+             break;
+           }
+      }
+  }
 
   /* We do bubble-sort, skip malicious clusters attempts */
-  if (end - start > 20)
-    return;
-
-  /* Sit tight, rock 'n roll! */
-  hb_bubble_sort (info + start, end - start, compare_indic_order);
+  if (end - start < 64)
+  {
+    /* Sit tight, rock 'n roll! */
+    hb_bubble_sort (info + start, end - start, compare_indic_order);
+    /* Find base again */
+    base = end;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].indic_position() == POS_BASE_C) {
+        base = i;
+       break;
+      }
+  }
 
   /* Setup masks now */
 
   {
     hb_mask_t mask;
 
+    /* Reph */
+    for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
+      info[i].mask |= mask_array[RPHF];
+
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
-    for (i = start; i < base; i++)
+    for (unsigned int i = start; i < base; i++)
       info[i].mask  |= mask;
     /* Base */
     mask = mask_array[AKHN] | mask_array[CJCT];
     info[base].mask |= mask;
     /* Post-base */
     mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT];
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
   /* Apply ZWJ/ZWNJ effects */
-  for (i = start + 1; i < end; i++)
+  for (unsigned int i = start + 1; i < end; i++)
     if (is_joiner (info[i])) {
       bool non_joiner = info[i].indic_category() == OT_ZWNJ;
       unsigned int j = i;
@@ -597,14 +487,9 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
       do {
        j--;
 
-       /* Reading the Unicode and OpenType specs, I think the following line
-        * is correct, but this is not what the test suite expects currently.
-        * The test suite has been drinking, not me...  But disable while
-        * investigating.
-        */
-       //info[j].mask &= !mask_array[CJCT];
+       info[j].mask &= ~mask_array[CJCT];
        if (non_joiner)
-         info[j].mask &= !mask_array[HALF];
+         info[j].mask &= ~mask_array[HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
@@ -612,28 +497,41 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
 
 
 static void
-found_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-                     unsigned int start, unsigned int end)
+initial_reordering_vowel_syllable (const hb_ot_map_t *map,
+                                  hb_buffer_t *buffer,
+                                  hb_mask_t *mask_array,
+                                  unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Not clear to me how this should work.  Do the matras move to before the
-   * independent vowel?  No idea.
-   */
+  /* We made the vowels look like consonants.  So let's call the consonant logic! */
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
-found_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-                         unsigned int start, unsigned int end)
+initial_reordering_standalone_cluster (const hb_ot_map_t *map,
+                                      hb_buffer_t *buffer,
+                                      hb_mask_t *mask_array,
+                                      unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Easiest thing to do here is to convert the NBSP to consonant and
-   * call found_consonant_syllable.
-   */
+  /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain.
+   * Only if not in compatibility mode that is... */
+
+  if (indic_options ().uniscribe_bug_compatible)
+  {
+    /* For dotted-circle, this is what Uniscribe does:
+     * If dotted-circle is the last glyph, it just does nothing.
+     * Ie. It doesn't form Reph. */
+    if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
+      return;
+  }
+
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
-found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-                unsigned int start, unsigned int end)
+initial_reordering_non_indic (const hb_ot_map_t *map HB_UNUSED,
+                             hb_buffer_t *buffer HB_UNUSED,
+                             hb_mask_t *mask_array HB_UNUSED,
+                             unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
 {
   /* Nothing to do right now.  If we ever switch to using the output
    * buffer in the reordering process, we'd need to next_glyph() here. */
@@ -642,22 +540,8 @@ found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_ar
 #include "hb-ot-shape-complex-indic-machine.hh"
 
 static void
-remove_joiners (hb_buffer_t *buffer)
-{
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count;)
-    if (unlikely (is_joiner (buffer->info[buffer->idx])))
-      buffer->skip_glyph ();
-    else
-      buffer->next_glyph ();
-
-  buffer->swap_buffers ();
-}
-
-static void
 initial_reordering (const hb_ot_map_t *map,
-                   hb_face_t *face,
+                   hb_face_t *face HB_UNUSED,
                    hb_buffer_t *buffer,
                    void *user_data HB_UNUSED)
 {
@@ -667,24 +551,40 @@ initial_reordering (const hb_ot_map_t *map,
     mask_array[i] = map->get_1_mask (indic_basic_features[i].tag);
 
   find_syllables (map, buffer, mask_array);
-
-  remove_joiners (buffer);
 }
 
 static void
-final_reordering (const hb_ot_map_t *map,
-                 hb_face_t *face,
-                 hb_buffer_t *buffer,
-                 void *user_data HB_UNUSED)
+final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
+                          unsigned int start, unsigned int end)
 {
+  hb_glyph_info_t *info = buffer->info;
+
   /* 4. Final reordering:
    *
    * After the localized forms and basic shaping forms GSUB features have been
    * applied (see below), the shaping engine performs some final glyph
    * reordering before applying all the remaining font features to the entire
    * cluster.
-   *
-   *   o Reorder matras:
+   */
+
+  /* Find base again */
+  unsigned int base = end;
+  for (unsigned int i = start; i < end; i++)
+    if (info[i].indic_position() == POS_BASE_C) {
+      base = i;
+      break;
+    }
+
+  if (base == start) {
+    /* There's no Reph, and no left Matra to reposition.  Just merge the cluster
+     * and go home. */
+    buffer->merge_clusters (start, end);
+    return;
+  }
+
+  unsigned int start_of_last_cluster = base;
+
+  /*   o Reorder matras:
    *
    *     If a pre-base matra character had been reordered before applying basic
    *     features, the glyph can be moved closer to the main consonant based on
@@ -692,49 +592,197 @@ final_reordering (const hb_ot_map_t *map,
    *     defined as “after last standalone halant glyph, after initial matra
    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
    *     halant, position is moved after it.
-   *
-   *   o Reorder reph:
+   */
+
+  {
+    unsigned int new_matra_pos = base - 1;
+    while (new_matra_pos > start &&
+          !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
+      new_matra_pos--;
+    /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
+     * not belong to the Matra itself! */
+    if (info[new_matra_pos].indic_category() == OT_H &&
+       info[new_matra_pos].indic_position() != POS_PRE_M) {
+      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+      if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
+       new_matra_pos++;
+
+      /* Now go see if there's actually any matras... */
+      for (unsigned int i = new_matra_pos; i > start; i--)
+       if (info[i - 1].indic_position () == POS_PRE_M)
+       {
+         unsigned int old_matra_pos = i - 1;
+         hb_glyph_info_t matra = info[old_matra_pos];
+         memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
+         info[new_matra_pos] = matra;
+         start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
+         new_matra_pos--;
+       }
+    }
+  }
+
+
+  /*   o Reorder reph:
    *
    *     Reph’s original position is always at the beginning of the syllable,
    *     (i.e. it is not reordered at the character reordering stage). However,
    *     it will be reordered according to the basic-forms shaping results.
    *     Possible positions for reph, depending on the script, are; after main,
    *     before post-base consonant forms, and after post-base consonant forms.
-   *
-   *       1. If reph should be positioned after post-base consonant forms,
-   *          proceed to step 5.
-   *
-   *       2. If the reph repositioning class is not after post-base: target
-   *          position is after the first explicit halant glyph between the
-   *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
-   *          are following this halant, position is moved after it. If such
-   *          position is found, this is the target position. Otherwise,
-   *          proceed to the next step.
-   *
-   *          Note: in old-implementation fonts, where classifications were
-   *          fixed in shaping engine, there was no case where reph position
-   *          will be found on this step.
-   *
-   *       3. If reph should be repositioned after the main consonant: from the
-   *          first consonant not ligated with main, or find the first
-   *          consonant that is not a potential pre-base reordering Ra.
-   *
-   *
-   *       4. If reph should be positioned before post-base consonant, find
-   *          first post-base classified consonant not ligated with main. If no
-   *          consonant is found, the target position should be before the
-   *          first matra, syllable modifier sign or vedic sign.
-   *
-   *       5. If no consonant is found in steps 3 or 4, move reph to a position
-   *          immediately before the first post-base matra, syllable modifier
-   *          sign or vedic sign that has a reordering class after the intended
-   *          reph position. For example, if the reordering position for reph
-   *          is post-main, it will skip above-base matras that also have a
-   *          post-main position.
-   *
-   *       6. Otherwise, reorder reph to the end of the syllable.
-   *
-   *   o Reorder pre-base reordering consonants:
+   */
+
+  /* If there's anything after the Ra that has the REPH pos, it ought to be halant.
+   * Which means that the font has failed to ligate the Reph.  In which case, we
+   * shouldn't move. */
+  if (start + 1 < end &&
+      info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
+      info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH)
+  {
+      unsigned int new_reph_pos;
+
+     enum reph_position_t {
+       REPH_AFTER_MAIN,
+       REPH_BEFORE_SUBSCRIPT,
+       REPH_AFTER_SUBSCRIPT,
+       REPH_BEFORE_POSTSCRIPT,
+       REPH_AFTER_POSTSCRIPT
+     } reph_pos;
+
+     /* XXX Figure out old behavior too */
+     switch ((hb_tag_t) buffer->props.script)
+     {
+       case HB_SCRIPT_MALAYALAM:
+       case HB_SCRIPT_ORIYA:
+        reph_pos = REPH_AFTER_MAIN;
+        break;
+
+       case HB_SCRIPT_GURMUKHI:
+        reph_pos = REPH_BEFORE_SUBSCRIPT;
+        break;
+
+       case HB_SCRIPT_BENGALI:
+        reph_pos = REPH_AFTER_SUBSCRIPT;
+        break;
+
+       default:
+       case HB_SCRIPT_DEVANAGARI:
+       case HB_SCRIPT_GUJARATI:
+        reph_pos = REPH_BEFORE_POSTSCRIPT;
+        break;
+
+       case HB_SCRIPT_KANNADA:
+       case HB_SCRIPT_TAMIL:
+       case HB_SCRIPT_TELUGU:
+        reph_pos = REPH_AFTER_POSTSCRIPT;
+        break;
+     }
+
+    /*       1. If reph should be positioned after post-base consonant forms,
+     *          proceed to step 5.
+     */
+    if (reph_pos == REPH_AFTER_POSTSCRIPT)
+    {
+      goto reph_step_5;
+    }
+
+    /*       2. If the reph repositioning class is not after post-base: target
+     *          position is after the first explicit halant glyph between the
+     *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
+     *          are following this halant, position is moved after it. If such
+     *          position is found, this is the target position. Otherwise,
+     *          proceed to the next step.
+     *
+     *          Note: in old-implementation fonts, where classifications were
+     *          fixed in shaping engine, there was no case where reph position
+     *          will be found on this step.
+     */
+    {
+      new_reph_pos = start + 1;
+      while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H)
+       new_reph_pos++;
+
+      if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) {
+       /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
+       if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
+         new_reph_pos++;
+       goto reph_move;
+      }
+    }
+
+    /*       3. If reph should be repositioned after the main consonant: find the
+     *          first consonant not ligated with main, or find the first
+     *          consonant that is not a potential pre-base reordering Ra.
+     */
+    if (reph_pos == REPH_AFTER_MAIN)
+    {
+      /* XXX */
+    }
+
+    /*       4. If reph should be positioned before post-base consonant, find
+     *          first post-base classified consonant not ligated with main. If no
+     *          consonant is found, the target position should be before the
+     *          first matra, syllable modifier sign or vedic sign.
+     */
+    /* This is our take on what step 4 is trying to say (and failing, BADLY). */
+    if (reph_pos == REPH_AFTER_SUBSCRIPT)
+    {
+      new_reph_pos = base;
+      while (new_reph_pos < end &&
+            !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD))))
+       new_reph_pos++;
+      if (new_reph_pos < end)
+        goto reph_move;
+    }
+
+    /*       5. If no consonant is found in steps 3 or 4, move reph to a position
+     *          immediately before the first post-base matra, syllable modifier
+     *          sign or vedic sign that has a reordering class after the intended
+     *          reph position. For example, if the reordering position for reph
+     *          is post-main, it will skip above-base matras that also have a
+     *          post-main position.
+     */
+    reph_step_5:
+    {
+      /* XXX */
+    }
+
+    /*       6. Otherwise, reorder reph to the end of the syllable.
+     */
+    {
+      new_reph_pos = end - 1;
+      while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
+       new_reph_pos--;
+
+      /*
+       * If the Reph is to be ending up after a Matra,Halant sequence,
+       * position it before that Halant so it can interact with the Matra.
+       * However, if it's a plain Consonant,Halant we shouldn't do that.
+       * Uniscribe doesn't do this.
+       * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
+       */
+      if (!indic_options ().uniscribe_bug_compatible &&
+         unlikely (info[new_reph_pos].indic_category() == OT_H)) {
+       for (unsigned int i = base + 1; i < new_reph_pos; i++)
+         if (info[i].indic_category() == OT_M) {
+           /* Ok, got it. */
+           new_reph_pos--;
+         }
+      }
+      goto reph_move;
+    }
+
+    reph_move:
+    {
+      /* Move */
+      hb_glyph_info_t reph = info[start];
+      memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
+      info[new_reph_pos] = reph;
+      start_of_last_cluster = start; /* Yay, one big cluster! */
+    }
+  }
+
+
+  /*   o Reorder pre-base reordering consonants:
    *
    *     If a pre-base reordering consonant is found, reorder it according to
    *     the following rules:
@@ -754,10 +802,72 @@ final_reordering (const hb_ot_map_t *map,
 
 
 
+  /* Apply 'init' to the Left Matra if it's a word start. */
+  if (info[start].indic_position () == POS_PRE_M &&
+      (!start ||
+       !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) &
+        (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+         FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))))
+    info[start].mask |= mask_array[INIT];
+
+
+
+  /* Finish off the clusters and go home! */
+
+  if (!indic_options ().uniscribe_bug_compatible)
+  {
+    /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
+     * This means, half forms are submerged into the main consonants cluster.
+     * This is unnecessary, and makes cursor positioning harder, but that's what
+     * Uniscribe does. */
+    unsigned int cluster_start = start;
+    for (unsigned int i = start + 1; i < start_of_last_cluster; i++)
+      if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) {
+        i++;
+       buffer->merge_clusters (cluster_start, i);
+       cluster_start = i;
+      }
+    start_of_last_cluster = cluster_start;
+  }
+
+  buffer->merge_clusters (start_of_last_cluster, end);
+}
+
+
+static void
+final_reordering (const hb_ot_map_t *map,
+                 hb_face_t *face HB_UNUSED,
+                 hb_buffer_t *buffer,
+                 void *user_data HB_UNUSED)
+{
+  unsigned int count = buffer->len;
+  if (!count) return;
+
+  hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0};
+  unsigned int num_masks = ARRAY_LENGTH (indic_other_features);
+  for (unsigned int i = 0; i < num_masks; i++)
+    mask_array[i] = map->get_1_mask (indic_other_features[i].tag);
+
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int last = 0;
+  unsigned int last_syllable = info[0].syllable();
+  for (unsigned int i = 1; i < count; i++)
+    if (last_syllable != info[i].syllable()) {
+      final_reordering_syllable (buffer, mask_array, last, i);
+      last = i;
+      last_syllable = info[last].syllable();
+    }
+  final_reordering_syllable (buffer, mask_array, last, count);
+
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
 
 
 
-HB_END_DECLS
index b2de3ff..d93d4c6 100644 (file)
@@ -26,9 +26,8 @@
 
 #include "hb-ot-shape-complex-private.hh"
 
-HB_BEGIN_DECLS
 
-/* TODO Add kana, hangul, and other small sahpers here */
+/* TODO Add kana, and other small shapers here */
 
 /* When adding trivial shapers, eg. kana, hangul, etc, we can either
  * add a full shaper enum value for them, or switch on the script in
@@ -38,20 +37,157 @@ HB_BEGIN_DECLS
  */
 
 void
-_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSED,
+                                              const hb_segment_properties_t *props HB_UNUSED)
 {
 }
 
-bool
-_hb_ot_shape_complex_prefer_decomposed_default (void)
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_default (void)
 {
-  return FALSE;
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
 }
 
 void
-_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map, hb_buffer_t *buffer)
+_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED,
+                                         hb_buffer_t *buffer HB_UNUSED,
+                                         hb_font_t *font HB_UNUSED)
 {
 }
 
 
-HB_END_DECLS
+
+/* Hangul shaper */
+
+static const hb_tag_t hangul_features[] =
+{
+  HB_TAG('l','j','m','o'),
+  HB_TAG('v','j','m','o'),
+  HB_TAG('t','j','m','o'),
+};
+
+void
+_hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map,
+                                             const hb_segment_properties_t *props HB_UNUSED)
+{
+  for (unsigned int i = 0; i < ARRAY_LENGTH (hangul_features); i++)
+    map->add_bool_feature (hangul_features[i]);
+}
+
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_hangul (void)
+{
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
+}
+
+void
+_hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map HB_UNUSED,
+                                        hb_buffer_t *buffer HB_UNUSED,
+                                        hb_font_t *font HB_UNUSED)
+{
+}
+
+
+
+/* Thai / Lao shaper */
+
+void
+_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED,
+                                           const hb_segment_properties_t *props HB_UNUSED)
+{
+}
+
+hb_ot_shape_normalization_mode_t
+_hb_ot_shape_complex_normalization_preference_thai (void)
+{
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
+}
+
+void
+_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED,
+                                      hb_buffer_t *buffer,
+                                      hb_font_t *font HB_UNUSED)
+{
+  /* The following is NOT specified in the MS OT Thai spec, however, it seems
+   * to be what Uniscribe and other engines implement.  According to Eric Muller:
+   *
+   * When you have a sara am, decompose it in nikhahit + sara a, *and* mode the
+   * nihka hit backwards over any *tone* mark (0E48-0E4B).
+   *
+   * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
+   *
+   * This reordering is legit only when the nikhahit comes from a sara am, not
+   * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
+   * not what a u↪ser wanted, but the rendering is nevertheless nikhahit above
+   * chattawa.
+   *
+   * Same for Lao.
+   */
+
+  /*
+   * Here are the characters of significance:
+   *
+   *                   Thai    Lao
+   * SARA AM:          U+0E33  U+0EB3
+   * SARA AA:          U+0E32  U+0EB2
+   * Nikhahit:         U+0E4D  U+0ECD
+   *
+   * Tone marks:
+   * Thai:     <0E48..0E4B> CCC=107
+   * Lao:      <0EC8..0ECB> CCC=122
+   *
+   * Note how the Lao versions are the same as Thai + 0x80.
+   */
+
+  /* We only get one script at a time, so a script-agnostic implementation
+   * is adequate here. */
+#define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33)
+#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D)
+#define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
+#define IS_TONE_MARK(x) (((x) & ~0x0083) == 0x0E48)
+
+  buffer->clear_output ();
+  unsigned int count = buffer->len;
+  for (buffer->idx = 0; buffer->idx < count;)
+  {
+    hb_codepoint_t u = buffer->cur().codepoint;
+    if (likely (!IS_SARA_AM (u))) {
+      buffer->next_glyph ();
+      continue;
+    }
+
+    /* Is SARA AM. Decompose and reorder. */
+    hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)),
+                                   hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))};
+    buffer->replace_glyphs (1, 2, decomposed);
+    if (unlikely (buffer->in_error))
+      return;
+
+    /* Ok, let's see... */
+    unsigned int end = buffer->out_len;
+    unsigned int start = end - 2;
+    while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint))
+      start--;
+
+    /* Move Nikhahit (end-2) to the beginning */
+    hb_glyph_info_t t = buffer->out_info[end - 2];
+    memmove (buffer->out_info + start + 1,
+            buffer->out_info + start,
+            sizeof (buffer->out_info[0]) * (end - start - 2));
+    buffer->out_info[start] = t;
+
+    /* XXX Make this easier! */
+    /* Make cluster */
+    for (; start > 0 && buffer->out_info[start - 1].cluster == buffer->out_info[start].cluster; start--)
+      ;
+    for (; buffer->idx < count;)
+      if (buffer->cur().cluster == buffer->prev().cluster)
+        buffer->next_glyph ();
+      else
+        break;
+    end = buffer->out_len;
+
+    buffer->merge_out_clusters (start, end);
+  }
+  buffer->swap_buffers ();
+}
index 1aca595..e0d93a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010,2011  Google, Inc.
+ * Copyright © 2010,2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 #include "hb-private.hh"
 
 #include "hb-ot-map-private.hh"
+#include "hb-ot-shape-normalize-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /* buffer var allocations, used during the entire shaping process */
-#define general_category() var1.u8[0] /* unicode general_category (hb_unicode_general_category_t) */
-#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */
+#define unicode_props0()       var1.u8[0]
+#define unicode_props1()       var1.u8[1]
+
+/* buffer var allocations, used during the GSUB/GPOS processing */
+#define props_cache()          var1.u16[1] /* GSUB/GPOS glyph_props cache */
+#define syllable()             var2.u8[0] /* GSUB/GPOS shaping boundaries */
+#define lig_props()            var2.u8[1] /* GSUB/GPOS ligature tracking */
 
 /* buffer var allocations, used by complex shapers */
-#define complex_var_persistent_u8_0()  var2.u8[0]
-#define complex_var_persistent_u8_1()  var2.u8[1]
-#define complex_var_persistent_u16()   var2.u16[0]
-#define complex_var_temporary_u8_0()   var2.u8[2]
-#define complex_var_temporary_u8_1()   var2.u8[3]
-#define complex_var_temporary_u16()    var2.u16[1]
+#define complex_var_persistent_u8_0()  var2.u8[2]
+#define complex_var_persistent_u8_1()  var2.u8[3]
+#define complex_var_temporary_u8()     var2.u8[0]
 
 
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   /* ^--- Add new shapers here */
 
 enum hb_ot_complex_shaper_t {
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) hb_ot_complex_shaper_##name,
   HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
+  /* Just here to avoid enum trailing comma: */
+  hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default
 #undef HB_COMPLEX_SHAPER_IMPLEMENT
 };
 
 static inline hb_ot_complex_shaper_t
 hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
 {
-  switch ((int) props->script)
+  switch ((hb_tag_t) props->script)
   {
     default:
       return hb_ot_complex_shaper_default;
 
+
+    /* Unicode-1.1 additions */
     case HB_SCRIPT_ARABIC:
-    case HB_SCRIPT_MANDAIC:
     case HB_SCRIPT_MONGOLIAN:
-    case HB_SCRIPT_NKO:
     case HB_SCRIPT_SYRIAC:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_NKO:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_MANDAIC:
+
       return hb_ot_complex_shaper_arabic;
 
-    /* TODO: These are all the scripts that the ucd/IndicSyllabicCategory.txt covers.
-     * Quite possibly many of these need no shaping, and some other are encoded visually.
-     * Needs to be refined.
+
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_HANGUL:
+
+      return hb_ot_complex_shaper_hangul;
+
+
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_THAI:
+    case HB_SCRIPT_LAO:
+
+      return hb_ot_complex_shaper_thai;
+
+
+
+    /* ^--- Add new shapers here */
+
+
+#if 0
+    /* Note:
+     *
+     * These disabled scripts are listed in ucd/IndicSyllabicCategory.txt, but according
+     * to Martin Hosken and Jonathan Kew do not require complex shaping.
+     *
+     * TODO We should automate figuring out which scripts do not need complex shaping
+     *
+     * TODO We currently keep data for these scripts in our indic table.  Need to fix the
+     * generator to not do that.
      */
-    case HB_SCRIPT_BALINESE:
+
+
+    /* Simple? */
+
+    /* Unicode-3.2 additions */
+    case HB_SCRIPT_BUHID:
+    case HB_SCRIPT_HANUNOO:
+
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_SAURASHTRA:
+
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_MEETEI_MAYEK:
+
+    /* Unicode-6.0 additions */
     case HB_SCRIPT_BATAK:
-    case HB_SCRIPT_BENGALI:
     case HB_SCRIPT_BRAHMI:
-    case HB_SCRIPT_BUGINESE:
-    case HB_SCRIPT_BUHID:
-    case HB_SCRIPT_CHAM:
+
+
+    /* Simple */
+
+    /* Unicode-1.1 additions */
+    /* These have their own shaper now. */
+    case HB_SCRIPT_LAO:
+    case HB_SCRIPT_THAI:
+
+    /* Unicode-2.0 additions */
+    case HB_SCRIPT_TIBETAN:
+
+    /* Unicode-3.2 additions */
+    case HB_SCRIPT_TAGALOG:
+    case HB_SCRIPT_TAGBANWA:
+
+    /* Unicode-4.0 additions */
+    case HB_SCRIPT_LIMBU:
+    case HB_SCRIPT_TAI_LE:
+
+    /* Unicode-4.1 additions */
+    case HB_SCRIPT_SYLOTI_NAGRI:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_PHAGS_PA:
+
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_KAYAH_LI:
+
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_TAI_VIET:
+
+
+    /* May need Indic treatment in the future? */
+
+    /* Unicode-3.0 additions */
+    case HB_SCRIPT_MYANMAR:
+
+
+#endif
+
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_BENGALI:
     case HB_SCRIPT_DEVANAGARI:
     case HB_SCRIPT_GUJARATI:
     case HB_SCRIPT_GURMUKHI:
-    case HB_SCRIPT_HANUNOO:
-    case HB_SCRIPT_JAVANESE:
-    case HB_SCRIPT_KAITHI:
     case HB_SCRIPT_KANNADA:
-    case HB_SCRIPT_KAYAH_LI:
-    case HB_SCRIPT_KHAROSHTHI:
-    case HB_SCRIPT_KHMER:
-    case HB_SCRIPT_LAO:
-    case HB_SCRIPT_LEPCHA:
-    case HB_SCRIPT_LIMBU:
     case HB_SCRIPT_MALAYALAM:
-    case HB_SCRIPT_MEETEI_MAYEK:
-    case HB_SCRIPT_MYANMAR:
-    case HB_SCRIPT_NEW_TAI_LUE:
     case HB_SCRIPT_ORIYA:
-    case HB_SCRIPT_PHAGS_PA:
-    case HB_SCRIPT_REJANG:
-    case HB_SCRIPT_SAURASHTRA:
+    case HB_SCRIPT_TAMIL:
+    case HB_SCRIPT_TELUGU:
+
+    /* Unicode-3.0 additions */
+    case HB_SCRIPT_KHMER:
     case HB_SCRIPT_SINHALA:
+
+    /* Unicode-4.1 additions */
+    case HB_SCRIPT_BUGINESE:
+    case HB_SCRIPT_KHAROSHTHI:
+    case HB_SCRIPT_NEW_TAI_LUE:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_BALINESE:
+
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_CHAM:
+    case HB_SCRIPT_LEPCHA:
+    case HB_SCRIPT_REJANG:
     case HB_SCRIPT_SUNDANESE:
-    case HB_SCRIPT_SYLOTI_NAGRI:
-    case HB_SCRIPT_TAGALOG:
-    case HB_SCRIPT_TAGBANWA:
-    case HB_SCRIPT_TAI_LE:
+
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_JAVANESE:
+    case HB_SCRIPT_KAITHI:
     case HB_SCRIPT_TAI_THAM:
-    case HB_SCRIPT_TAI_VIET:
-    case HB_SCRIPT_TAMIL:
-    case HB_SCRIPT_TELUGU:
-    case HB_SCRIPT_THAI:
-    case HB_SCRIPT_TIBETAN:
+
+    /* Unicode-6.1 additions */
+    case HB_SCRIPT_CHAKMA:
+    case HB_SCRIPT_SHARADA:
+    case HB_SCRIPT_TAKRI:
+
       return hb_ot_complex_shaper_indic;
 
     /* ^--- Add new shapers here */
@@ -156,26 +251,26 @@ hb_ot_shape_complex_collect_features (hb_ot_complex_shaper_t shaper,
 
 
 /*
- * prefer_decomposed()
+ * normalization_preference()
  *
  * Called during shape_execute().
  *
- * Shapers should return TRUE if it prefers decomposed (NFD) input rather than precomposed (NFC).
+ * Shapers should return true if it prefers decomposed (NFD) input rather than precomposed (NFC).
  */
 
-typedef bool hb_ot_shape_complex_prefer_decomposed_func_t (void);
+typedef hb_ot_shape_normalization_mode_t hb_ot_shape_complex_normalization_preference_func_t (void);
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-  HB_INTERNAL hb_ot_shape_complex_prefer_decomposed_func_t _hb_ot_shape_complex_prefer_decomposed_##name;
+  HB_INTERNAL hb_ot_shape_complex_normalization_preference_func_t _hb_ot_shape_complex_normalization_preference_##name;
   HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
 #undef HB_COMPLEX_SHAPER_IMPLEMENT
 
-static inline bool
-hb_ot_shape_complex_prefer_decomposed (hb_ot_complex_shaper_t shaper)
+static inline hb_ot_shape_normalization_mode_t
+hb_ot_shape_complex_normalization_preference (hb_ot_complex_shaper_t shaper)
 {
   switch (shaper) {
     default:
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:  return _hb_ot_shape_complex_prefer_decomposed_##name ();
+    case hb_ot_complex_shaper_##name:  return _hb_ot_shape_complex_normalization_preference_##name ();
     HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
 #undef HB_COMPLEX_SHAPER_IMPLEMENT
   }
@@ -189,7 +284,7 @@ hb_ot_shape_complex_prefer_decomposed (hb_ot_complex_shaper_t shaper)
  * Shapers should use map to get feature masks and set on buffer.
  */
 
-typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer);
+typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font);
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
   HB_INTERNAL hb_ot_shape_complex_setup_masks_func_t _hb_ot_shape_complex_setup_masks_##name;
   HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
@@ -198,18 +293,18 @@ typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer
 static inline void
 hb_ot_shape_complex_setup_masks (hb_ot_complex_shaper_t shaper,
                                 hb_ot_map_t *map,
-                                hb_buffer_t *buffer)
+                                hb_buffer_t *buffer,
+                                hb_font_t *font)
 {
   switch (shaper) {
     default:
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:  _hb_ot_shape_complex_setup_masks_##name (map, buffer); return;
+    case hb_ot_complex_shaper_##name:  _hb_ot_shape_complex_setup_masks_##name (map, buffer, font); return;
     HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
 #undef HB_COMPLEX_SHAPER_IMPLEMENT
   }
 }
 
 
-HB_END_DECLS
 
 #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize-private.hh
new file mode 100644 (file)
index 0000000..bb81f00
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012  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_NORMALIZE_PRIVATE_HH
+#define HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-font.h"
+#include "hb-buffer.h"
+
+
+enum hb_ot_shape_normalization_mode_t {
+  HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL /* including base-to-base composition */
+};
+
+HB_INTERNAL void _hb_ot_shape_normalize (hb_font_t *font,
+                                        hb_buffer_t *buffer,
+                                        hb_ot_shape_normalization_mode_t mode);
+
+#endif /* HB_OT_SHAPE_NORMALIZE_PRIVATE_HH */
index e2fd91c..562ba88 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb-ot-shape-normalize-private.hh"
 #include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-complex-private.hh"
 
-HB_BEGIN_DECLS
 
 /*
  * HIGHLEVEL DESIGN:
@@ -35,11 +34,13 @@ HB_BEGIN_DECLS
  * This file exports one main function: _hb_ot_shape_normalize().
  *
  * This function closely reflects the Unicode Normalization Algorithm,
- * yet it's different.  The shaper an either prefer decomposed (NFD) or
- * composed (NFC).
+ * yet it's different.
+ *
+ * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC).
+ * The logic however tries to use whatever the font can support.
  *
  * In general what happens is that: each grapheme is decomposed in a chain
- * of 1:2 decompositions, marks reordered, and then recomposed if desires,
+ * of 1:2 decompositions, marks reordered, and then recomposed if desired,
  * so far it's like Unicode Normalization.  However, the decomposition and
  * recomposition only happens if the font supports the resulting characters.
  *
@@ -53,9 +54,13 @@ HB_BEGIN_DECLS
  *     though their NFC may be different.
  *
  *   - When a font has a precomposed character for a sequence but the 'ccmp'
- *     feature in the font is not adequate, form use the precomposed character
+ *     feature in the font is not adequate, use the precomposed character
  *     which typically has better mark positioning.
  *
+ *   - When a font does not support a combining mark, but supports it precomposed
+ *     with previous base, use that.  This needs the itemizer to have this
+ *     knowledge too.  We need to provide assistance to the itemizer.
+ *
  *   - When a font does not support a character but supports its decomposition,
  *     well, use the decomposition.
  *
@@ -64,104 +69,104 @@ HB_BEGIN_DECLS
  */
 
 static void
-output_glyph (hb_ot_shape_context_t *c,
-             hb_codepoint_t glyph)
+output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph)
 {
-  hb_buffer_t *buffer = c->buffer;
-
   buffer->output_glyph (glyph);
-  hb_glyph_info_set_unicode_props (&buffer->out_info[buffer->out_len - 1], buffer->unicode);
+  _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode);
 }
 
 static bool
-decompose (hb_ot_shape_context_t *c,
+decompose (hb_font_t *font, hb_buffer_t *buffer,
           bool shortest,
           hb_codepoint_t ab)
 {
   hb_codepoint_t a, b, glyph;
 
-  if (!hb_unicode_decompose (c->buffer->unicode, ab, &a, &b) ||
-      (b && !hb_font_get_glyph (c->font, b, 0, &glyph)))
-    return FALSE;
+  if (!hb_unicode_decompose (buffer->unicode, ab, &a, &b) ||
+      (b && !hb_font_get_glyph (font, b, 0, &glyph)))
+    return false;
 
-  bool has_a = hb_font_get_glyph (c->font, a, 0, &glyph);
+  bool has_a = hb_font_get_glyph (font, a, 0, &glyph);
   if (shortest && has_a) {
     /* Output a and b */
-    output_glyph (c, a);
+    output_glyph (buffer, a);
     if (b)
-      output_glyph (c, b);
-    return TRUE;
+      output_glyph (buffer, b);
+    return true;
   }
 
-  if (decompose (c, shortest, a)) {
+  if (decompose (font, buffer, shortest, a)) {
     if (b)
-      output_glyph (c, b);
-    return TRUE;
+      output_glyph (buffer, b);
+    return true;
   }
 
   if (has_a) {
-    output_glyph (c, a);
+    output_glyph (buffer, a);
     if (b)
-      output_glyph (c, b);
-    return TRUE;
+      output_glyph (buffer, b);
+    return true;
   }
 
-  return FALSE;
+  return false;
 }
 
 static void
-decompose_current_glyph (hb_ot_shape_context_t *c,
+decompose_current_glyph (hb_font_t *font, hb_buffer_t *buffer,
                         bool shortest)
 {
-  if (decompose (c, shortest, c->buffer->info[c->buffer->idx].codepoint))
-    c->buffer->skip_glyph ();
+  if (decompose (font, buffer, shortest, buffer->cur().codepoint))
+    buffer->skip_glyph ();
   else
-    c->buffer->next_glyph ();
+    buffer->next_glyph ();
 }
 
 static void
-decompose_single_char_cluster (hb_ot_shape_context_t *c,
+decompose_single_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
                               bool will_recompose)
 {
   hb_codepoint_t glyph;
 
   /* If recomposing and font supports this, we're good to go */
-  if (will_recompose && hb_font_get_glyph (c->font, c->buffer->info[c->buffer->idx].codepoint, 0, &glyph)) {
-    c->buffer->next_glyph ();
+  if (will_recompose && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph)) {
+    buffer->next_glyph ();
     return;
   }
 
-  decompose_current_glyph (c, will_recompose);
+  decompose_current_glyph (font, buffer, will_recompose);
 }
 
 static void
-decompose_multi_char_cluster (hb_ot_shape_context_t *c,
+decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
                              unsigned int end)
 {
   /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
-  for (unsigned int i = c->buffer->idx; i < end; i++)
-    if (unlikely (is_variation_selector (c->buffer->info[i].codepoint)))
+  for (unsigned int i = buffer->idx; i < end; i++)
+    if (unlikely (_hb_unicode_is_variation_selector (buffer->info[i].codepoint))) {
+      while (buffer->idx < end)
+       buffer->next_glyph ();
       return;
+    }
 
-  while (c->buffer->idx < end)
-    decompose_current_glyph (c, FALSE);
+  while (buffer->idx < end)
+    decompose_current_glyph (font, buffer, false);
 }
 
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
-  unsigned int a = pa->combining_class();
-  unsigned int b = pb->combining_class();
+  unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
+  unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
 void
-_hb_ot_shape_normalize (hb_ot_shape_context_t *c)
+_hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
+                       hb_ot_shape_normalization_mode_t mode)
 {
-  hb_buffer_t *buffer = c->buffer;
-  bool recompose = !hb_ot_shape_complex_prefer_decomposed (c->plan->shaper);
-  bool has_multichar_clusters = FALSE;
+  bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
+  bool has_multichar_clusters = false;
   unsigned int count;
 
   /* We do a fairly straightforward yet custom normalization process in three
@@ -179,28 +184,20 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
-      if (buffer->info[buffer->idx].cluster != buffer->info[end].cluster)
+      if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
     if (buffer->idx + 1 == end)
-      decompose_single_char_cluster (c, recompose);
+      decompose_single_char_cluster (font, buffer, recompose);
     else {
-      decompose_multi_char_cluster (c, end);
-      has_multichar_clusters = TRUE;
+      decompose_multi_char_cluster (font, buffer, end);
+      has_multichar_clusters = true;
     }
   }
   buffer->swap_buffers ();
 
 
-  /* Technically speaking, two characters with ccc=0 may combine.  But all
-   * those cases are in languages that the indic module handles (which expects
-   * decomposed), or in Hangul jamo, which again, we want decomposed anyway.
-   * So we don't bother combining across cluster boundaries.
-   *
-   * TODO: Am I right about Hangul?  If I am, we should add a Hangul module
-   * that requests decomposed. */
-
-  if (!has_multichar_clusters)
+  if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !has_multichar_clusters)
     return; /* Done! */
 
 
@@ -209,12 +206,12 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c)
   count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    if (buffer->info[i].combining_class() == 0)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
       continue;
 
     unsigned int end;
     for (end = i + 1; end < count; end++)
-      if (buffer->info[end].combining_class() == 0)
+      if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
         break;
 
     /* We are going to do a bubble-sort.  Only do this if the
@@ -245,34 +242,38 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c)
   buffer->next_glyph ();
   while (buffer->idx < count)
   {
-    if (buffer->info[buffer->idx].combining_class() == 0) {
-      starter = buffer->out_len;
-      buffer->next_glyph ();
-      continue;
-    }
-
     hb_codepoint_t composed, glyph;
-    if ((buffer->out_info[buffer->out_len - 1].combining_class() >=
-        buffer->info[buffer->idx].combining_class()) ||
-       !hb_unicode_compose (c->buffer->unicode,
-                            buffer->out_info[starter].codepoint,
-                            buffer->info[buffer->idx].codepoint,
-                            &composed) ||
-       !hb_font_get_glyph (c->font, composed, 0, &glyph))
+    if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to
+        * compose a CCC=0 character with it's preceding starter. */
+       (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
+        _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
+       /* If there's anything between the starter and this char, they should have CCC
+        * smaller than this character's. */
+       (starter == buffer->out_len - 1 ||
+        _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
+       /* And compose. */
+       hb_unicode_compose (buffer->unicode,
+                           buffer->out_info[starter].codepoint,
+                           buffer->cur().codepoint,
+                           &composed) &&
+       /* And the font has glyph for the composite. */
+       hb_font_get_glyph (font, composed, 0, &glyph))
     {
-      /* Blocked, or doesn't compose. */
-      buffer->next_glyph ();
+      /* Composes. Modify starter and carry on. */
+      buffer->out_info[starter].codepoint = composed;
+      /* XXX update cluster */
+      _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
+
+      buffer->skip_glyph ();
       continue;
     }
 
-    /* Composes. Modify starter and carry on. */
-    buffer->out_info[starter].codepoint = composed;
-    hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
+    /* Blocked, or doesn't compose. */
+    buffer->next_glyph ();
 
-    buffer->skip_glyph ();
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0)
+      starter = buffer->out_len - 1;
   }
   buffer->swap_buffers ();
 
 }
-
-HB_END_DECLS
index 17f8273..df0c705 100644 (file)
 
 #include "hb-private.hh"
 
-#include "hb-ot-shape.h"
-
 #include "hb-ot-map-private.hh"
 #include "hb-ot-shape-complex-private.hh"
 
-HB_BEGIN_DECLS
-
-
-enum hb_ot_complex_shaper_t;
 
 struct hb_ot_shape_plan_t
 {
-  friend struct hb_ot_shape_planner_t;
-
   hb_ot_map_t map;
   hb_ot_complex_shaper_t shaper;
 
@@ -53,63 +45,39 @@ struct hb_ot_shape_plan_t
   NO_COPY (hb_ot_shape_plan_t);
 };
 
-struct hb_ot_shape_planner_t
-{
-  hb_ot_map_builder_t map;
-  hb_ot_complex_shaper_t shaper;
 
-  hb_ot_shape_planner_t (void) : map () {}
-  ~hb_ot_shape_planner_t (void) { map.finish (); }
 
-  inline void compile (hb_face_t *face,
-                      const hb_segment_properties_t *props,
-                      struct hb_ot_shape_plan_t &plan)
-  {
-    plan.shaper = shaper;
-    map.compile (face, props, plan.map);
-  }
-
-  private:
-  NO_COPY (hb_ot_shape_planner_t);
-};
+HB_INTERNAL hb_bool_t
+_hb_ot_shape (hb_font_t          *font,
+             hb_buffer_t        *buffer,
+             const hb_feature_t *features,
+             unsigned int        num_features);
 
 
-struct hb_ot_shape_context_t
+inline void
+_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
 {
-  /* 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;
-};
-
+  info->unicode_props0() = ((unsigned int) hb_unicode_general_category (unicode, info->codepoint)) |
+                          (_hb_unicode_is_zero_width (info->codepoint) ? 0x80 : 0);
+  info->unicode_props1() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
+}
 
-static inline hb_bool_t
-is_variation_selector (hb_codepoint_t unicode)
+inline hb_unicode_general_category_t
+_hb_glyph_info_get_general_category (const hb_glyph_info_t *info)
 {
-  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 */
+  return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F);
 }
 
-static inline void
-hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
+inline unsigned int
+_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
 {
-  info->general_category() = hb_unicode_general_category (unicode, info->codepoint);
-  info->combining_class() = hb_unicode_combining_class (unicode, info->codepoint);
+  return info->unicode_props1();
 }
 
-HB_INTERNAL void _hb_set_unicode_props (hb_buffer_t *buffer);
-
-HB_INTERNAL void _hb_ot_shape_normalize (hb_ot_shape_context_t *c);
-
-HB_END_DECLS
+inline hb_bool_t
+_hb_glyph_info_is_zero_width (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x80);
+}
 
 #endif /* HB_OT_SHAPE_PRIVATE_HH */
index e40cf1e..19cf680 100644 (file)
  */
 
 #include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-normalize-private.hh"
 
 #include "hb-font-private.hh"
+#include "hb-set-private.hh"
 
-HB_BEGIN_DECLS
 
 
 hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
+  HB_TAG('l','i','g','a'),
   HB_TAG('l','o','c','l'),
   HB_TAG('m','a','r','k'),
   HB_TAG('m','k','m','k'),
   HB_TAG('r','l','i','g'),
 };
 
+
 hb_tag_t horizontal_features[] = {
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
-  HB_TAG('l','i','g','a'),
 };
 
 /* Note:
@@ -63,6 +64,28 @@ hb_tag_t vertical_features[] = {
   HB_TAG('v','r','t','2'),
 };
 
+
+
+struct hb_ot_shape_planner_t
+{
+  hb_ot_map_builder_t map;
+  hb_ot_complex_shaper_t shaper;
+
+  hb_ot_shape_planner_t (void) : map () {}
+  ~hb_ot_shape_planner_t (void) { map.finish (); }
+
+  inline void compile (hb_face_t *face,
+                      const hb_segment_properties_t *props,
+                      struct hb_ot_shape_plan_t &plan)
+  {
+    plan.shaper = shaper;
+    map.compile (face, props, plan.map);
+  }
+
+  private:
+  NO_COPY (hb_ot_shape_planner_t);
+};
+
 static void
 hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
                              const hb_segment_properties_t  *props,
@@ -109,13 +132,28 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
 }
 
 
+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_position_complex;
+};
+
 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->plan->shaper, &c->plan->map, c->buffer);
+  hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer, c->font);
 
   for (unsigned int i = 0; i < c->num_user_features; i++)
   {
@@ -133,12 +171,12 @@ hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
 
 /* Prepare */
 
-void
-_hb_set_unicode_props (hb_buffer_t *buffer)
+static void
+hb_set_unicode_props (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
-  for (unsigned int i = 1; i < count; i++)
-    hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
+  for (unsigned int i = 0; i < count; i++)
+    _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
 }
 
 static void
@@ -146,11 +184,11 @@ hb_form_clusters (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 1; i < count; i++)
-    if (FLAG (buffer->info[i].general_category()) &
+    if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
        (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
         FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
         FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
-      buffer->info[i].cluster = buffer->info[i - 1].cluster;
+      buffer->info[i].cluster = buffer->info[i - 1].cluster; /* XXX do the min() here */
 }
 
 static void
@@ -206,17 +244,16 @@ hb_map_glyphs (hb_font_t    *font,
 
   unsigned int count = buffer->len - 1;
   for (buffer->idx = 0; buffer->idx < count;) {
-    if (unlikely (is_variation_selector (buffer->info[buffer->idx + 1].codepoint))) {
-      hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, buffer->info[buffer->idx + 1].codepoint, &glyph);
-      buffer->replace_glyph (glyph);
-      buffer->skip_glyph ();
+    if (unlikely (_hb_unicode_is_variation_selector (buffer->cur(+1).codepoint))) {
+      hb_font_get_glyph (font, buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph);
+      buffer->replace_glyphs (2, 1, &glyph);
     } else {
-      hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
+      hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph);
       buffer->replace_glyph (glyph);
     }
   }
   if (likely (buffer->idx < buffer->len)) {
-    hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
+    hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph);
     buffer->replace_glyph (glyph);
   }
   buffer->swap_buffers ();
@@ -235,21 +272,15 @@ hb_substitute_default (hb_ot_shape_context_t *c)
 static void
 hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
-  if (hb_ot_layout_has_substitution (c->face))
+  if (hb_ot_layout_has_substitution (c->face)) {
     c->plan->map.substitute (c->face, c->buffer);
+  }
 
   hb_ot_layout_substitute_finish (c->buffer);
 
-  c->applied_substitute_complex = TRUE;
   return;
 }
 
-static void
-hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
-{
-  /* TODO Arabic */
-}
-
 
 /* Position */
 
@@ -295,11 +326,12 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
                                                   &c->buffer->pos[i].x_offset,
                                                   &c->buffer->pos[i].y_offset);
     }
+
+    c->applied_position_complex = true;
   }
 
   hb_ot_layout_position_finish (c->buffer);
 
-  c->applied_position_complex = TRUE;
   return;
 }
 
@@ -341,6 +373,23 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
   hb_truetype_kern (c);
 }
 
+static void
+hb_hide_zerowidth (hb_ot_shape_context_t *c)
+{
+  /* TODO Save the space character in the font? */
+  hb_codepoint_t space;
+  if (!hb_font_get_glyph (c->font, ' ', 0, &space))
+    return; /* No point! */
+
+  unsigned int count = c->buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if (unlikely (_hb_glyph_info_is_zero_width (&c->buffer->info[i]))) {
+      c->buffer->info[i].codepoint = space;
+      c->buffer->pos[i].x_advance = 0;
+      c->buffer->pos[i].y_advance = 0;
+    }
+}
+
 
 /* Do it! */
 
@@ -352,16 +401,16 @@ 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_BUFFER_ALLOCATE_VAR (c->buffer, general_category);
-  HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1);
 
-  _hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */
+  hb_set_unicode_props (c->buffer);
 
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
-  _hb_ot_shape_normalize (c);
+  _hb_ot_shape_normalize (c->font, c->buffer, hb_ot_shape_complex_normalization_preference (c->plan->shaper));
 
   hb_ot_shape_setup_masks (c);
 
@@ -370,9 +419,6 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
     hb_substitute_default (c);
 
     hb_ot_substitute_complex (c);
-
-    if (!c->applied_substitute_complex)
-      hb_substitute_complex_fallback (c);
   }
 
   /* POSITION */
@@ -392,8 +438,10 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
       hb_position_complex_fallback_visual (c);
   }
 
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class);
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category);
+  hb_hide_zerowidth (c);
+
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1);
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0);
 
   c->buffer->props.direction = c->target_direction;
 
@@ -409,6 +457,8 @@ hb_ot_shape_plan_internal (hb_ot_shape_plan_t       *plan,
 {
   hb_ot_shape_planner_t planner;
 
+  assert (HB_DIRECTION_IS_VALID (props->direction));
+
   planner.shaper = hb_ot_shape_complex_categorize (props);
 
   hb_ot_shape_collect_features (&planner, props, user_features, num_user_features);
@@ -427,17 +477,52 @@ hb_ot_shape_execute (hb_ot_shape_plan_t *plan,
   hb_ot_shape_execute_internal (&c);
 }
 
-void
-hb_ot_shape (hb_font_t          *font,
-            hb_buffer_t        *buffer,
-            const hb_feature_t *features,
-            unsigned int        num_features)
+hb_bool_t
+_hb_ot_shape (hb_font_t          *font,
+             hb_buffer_t        *buffer,
+             const hb_feature_t *features,
+             unsigned int        num_features)
 {
   hb_ot_shape_plan_t plan;
 
+  buffer->guess_properties ();
+
   hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features);
   hb_ot_shape_execute (&plan, font, buffer, features, num_features);
+
+  return true;
 }
 
 
-HB_END_DECLS
+void
+hb_ot_shape_glyphs_closure (hb_font_t          *font,
+                           hb_buffer_t        *buffer,
+                           const hb_feature_t *features,
+                           unsigned int        num_features,
+                           hb_set_t           *glyphs)
+{
+  hb_ot_shape_plan_t plan;
+
+  buffer->guess_properties ();
+
+  hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features);
+
+  /* TODO: normalization? have shapers do closure()? */
+  /* TODO: Deal with mirrored chars? */
+  hb_map_glyphs (font, buffer);
+
+  /* Seed it.  It's user's responsibility to have cleard glyphs
+   * if that's what they desire. */
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    hb_set_add (glyphs, buffer->info[i].codepoint);
+
+  /* And find transitive closure. */
+  hb_set_t copy;
+  copy.init ();
+
+  do {
+    copy.set (glyphs);
+    plan.map.substitute_closure (font->face, glyphs);
+  } while (!copy.equal (glyphs));
+}
index 0ce635a..ac60e96 100644 (file)
@@ -31,7 +31,6 @@
 
 #include <string.h>
 
-HB_BEGIN_DECLS
 
 
 /* hb_script_t */
@@ -652,6 +651,14 @@ hb_ot_tag_from_language (hb_language_t language)
     return HB_TAG('Z','H','S',' ');
   }
 
+  s = strchr (lang_str, '-');
+  if (!s)
+    s = lang_str + strlen (lang_str);
+  if (s - lang_str == 3) {
+    /* Assume it's ISO-639-3 and upper-case and use it. */
+    return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000;
+  }
+
   return HB_OT_TAG_DEFAULT_LANGUAGE;
 }
 
@@ -665,12 +672,12 @@ hb_ot_tag_to_language (hb_tag_t tag)
 
   for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
     if (ot_languages[i].tag == tag)
-      return hb_language_from_string (ot_languages[i].language);
+      return hb_language_from_string (ot_languages[i].language, -1);
 
   /* If tag starts with ZH, it's Chinese */
   if ((tag & 0xFFFF0000)  == 0x5A480000) {
     switch (tag) {
-      case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk"); /* Hong Kong */
+      case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */
       default: {
         /* Encode the tag... */
        unsigned char buf[14] = "zh-x-hbot";
@@ -681,7 +688,7 @@ hb_ot_tag_to_language (hb_tag_t tag)
        if (buf[12] == 0x20)
          buf[12] = '\0';
        buf[13] = '\0';
-       return hb_language_from_string ((char *) buf);
+       return hb_language_from_string ((char *) buf, -1);
       }
     }
   }
@@ -696,9 +703,8 @@ hb_ot_tag_to_language (hb_tag_t tag)
     if (buf[9] == 0x20)
       buf[9] = '\0';
     buf[10] = '\0';
-    return hb_language_from_string ((char *) buf);
+    return hb_language_from_string ((char *) buf, -1);
   }
 }
 
 
-HB_END_DECLS
index 427a069..1bf12ab 100644 (file)
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
 #ifndef HB_OT_TAG_H
 #define HB_OT_TAG_H
 
-#include "hb-common.h"
+#include "hb.h"
 
 HB_BEGIN_DECLS
 
index fd6dd58..2d750c3 100644 (file)
 
 #ifndef HB_OT_H
 #define HB_OT_H
+#define HB_OT_H_IN
 
 #include "hb.h"
 
 #include "hb-ot-layout.h"
-#include "hb-ot-shape.h"
 #include "hb-ot-tag.h"
 
 HB_BEGIN_DECLS
+
+void
+hb_ot_shape_glyphs_closure (hb_font_t          *font,
+                           hb_buffer_t        *buffer,
+                           const hb_feature_t *features,
+                           unsigned int        num_features,
+                           hb_set_t           *glyphs);
+
 HB_END_DECLS
 
+#undef HB_OT_H_IN
 #endif /* HB_OT_H */
index 23fc0af..0cb049e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 #ifndef HB_PRIVATE_HH
 #define HB_PRIVATE_HH
 
-#if HAVE_CONFIG_H
+#ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
-#include "hb-common.h"
+#include "hb.h"
+#define HB_H_IN
+#ifdef HAVE_OT
+#include "hb-ot.h"
+#define HB_OT_H_IN
+#endif
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -48,7 +53,6 @@
 #include <errno.h>
 #include <stdarg.h>
 
-HB_BEGIN_DECLS
 
 
 /* Essentials */
@@ -57,16 +61,9 @@ HB_BEGIN_DECLS
 # define NULL ((void *) 0)
 #endif
 
-#undef FALSE
-#define FALSE 0
-
-#undef TRUE
-#define TRUE 1
-
 
 /* Basics */
 
-HB_END_DECLS
 
 #undef MIN
 template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
@@ -74,7 +71,6 @@ template <typename Type> static inline Type MIN (const Type &a, const Type &b) {
 #undef MAX
 template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 
-HB_BEGIN_DECLS
 
 #undef  ARRAY_LENGTH
 #define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
@@ -82,13 +78,15 @@ HB_BEGIN_DECLS
 #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))
+#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))
 
-#define ASSERT_STATIC_EXPR(_cond) ((void) sizeof (char[(_cond) ? 1 : -1]))
+#define ASSERT_STATIC_EXPR(_cond)((void) sizeof (char[(_cond) ? 1 : -1]))
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1]))
 
+#define _PASTE1(a,b) a##b
+#define PASTE(a,b) _PASTE1(a,b)
 
 /* Lets assert int types.  Saves trouble down the road. */
 
@@ -106,6 +104,34 @@ ASSERT_STATIC (sizeof (hb_position_t) == 4);
 ASSERT_STATIC (sizeof (hb_mask_t) == 4);
 ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
 
+
+/* We like our types POD */
+
+#define _ASSERT_TYPE_POD1(_line, _type)        union _type_##_type##_on_line_##_line##_is_not_POD { _type instance; }
+#define _ASSERT_TYPE_POD0(_line, _type)        _ASSERT_TYPE_POD1 (_line, _type)
+#define ASSERT_TYPE_POD(_type)         _ASSERT_TYPE_POD0 (__LINE__, _type)
+
+#ifdef __GNUC__
+# define _ASSERT_INSTANCE_POD1(_line, _instance) \
+       HB_STMT_START { \
+               typedef __typeof__(_instance) _type_##_line; \
+               _ASSERT_TYPE_POD1 (_line, _type_##_line); \
+       } HB_STMT_END
+#else
+# define _ASSERT_INSTANCE_POD1(_line, _instance)       typedef int _assertion_on_line_##_line##_not_tested
+#endif
+# define _ASSERT_INSTANCE_POD0(_line, _instance)       _ASSERT_INSTANCE_POD1 (_line, _instance)
+# define ASSERT_INSTANCE_POD(_instance)                        _ASSERT_INSTANCE_POD0 (__LINE__, _instance)
+
+/* Check _assertion in a method environment */
+#define _ASSERT_POD1(_line) \
+       inline void _static_assertion_on_line_##_line (void) const \
+       { _ASSERT_INSTANCE_POD1 (_line, *this); /* Make sure it's POD. */ }
+# define _ASSERT_POD0(_line)   _ASSERT_POD1 (_line)
+# define ASSERT_POD()          _ASSERT_POD0 (__LINE__)
+
+
+
 /* Misc */
 
 
@@ -130,7 +156,7 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
 #else
 #define HB_PURE_FUNC
 #define HB_CONST_FUNC
-#define HB_PRINTF_FUCN(format_idx, arg_idx)
+#define HB_PRINTF_FUNC(format_idx, arg_idx)
 #endif
 #if __GNUC__ >= 4
 #define HB_UNUSED      __attribute__((unused))
@@ -139,7 +165,11 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
 #endif
 
 #ifndef HB_INTERNAL
-# define HB_INTERNAL __attribute__((__visibility__("hidden")))
+# ifndef __MINGW32__
+#  define HB_INTERNAL __attribute__((__visibility__("hidden")))
+# else
+#  define HB_INTERNAL
+# endif
 #endif
 
 
@@ -226,20 +256,22 @@ _hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size)
 typedef int (*hb_compare_func_t) (const void *, const void *);
 
 
-HB_END_DECLS
 
 
 /* arrays and maps */
 
 
+#define HB_PREALLOCED_ARRAY_INIT {0}
 template <typename Type, unsigned int StaticSize>
-struct hb_prealloced_array_t {
-
+struct hb_prealloced_array_t
+{
   unsigned int len;
   unsigned int allocated;
   Type *array;
   Type static_array[StaticSize];
 
+  void init (void) { memset (this, 0, sizeof (*this)); }
+
   inline Type& operator [] (unsigned int i) { return array[i]; }
   inline const Type& operator [] (unsigned int i) const { return array[i]; }
 
@@ -333,25 +365,31 @@ struct hb_prealloced_array_t {
   }
 };
 
-template <typename Type>
-struct hb_array_t : hb_prealloced_array_t<Type, 2> {};
-
 
+#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT}
 template <typename item_t, typename lock_t>
 struct hb_lockable_set_t
 {
-  hb_array_t <item_t> items;
+  hb_prealloced_array_t <item_t, 2> items;
+
+  inline void init (void) { items.init (); }
 
   template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l)
+  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
   {
     l.lock ();
     item_t *item = items.find (v);
     if (item) {
-      item_t old = *item;
-      *item = v;
-      l.unlock ();
-      old.finish ();
+      if (replace) {
+       item_t old = *item;
+       *item = v;
+       l.unlock ();
+       old.finish ();
+      }
+      else {
+        item = NULL;
+       l.unlock ();
+      }
     } else {
       item = items.push ();
       if (likely (item))
@@ -419,7 +457,6 @@ struct hb_lockable_set_t
 };
 
 
-HB_BEGIN_DECLS
 
 
 /* Big-endian handling */
@@ -430,6 +467,14 @@ static inline uint16_t hb_be_uint16 (const uint16_t v)
   return (uint16_t) (V[0] << 8) + V[1];
 }
 
+/* Note, of the following macros, uint16_get is the one called many many times.
+ * If there is any optimizations to be done, it's in that macro.  However, I
+ * already confirmed that on my T400 ThinkPad at least, using bswap_16(), which
+ * results in a single ror instruction, does NOT speed this up.  In fact, it
+ * resulted in a minor slowdown.  At any rate, note that v may not be correctly
+ * aligned, so I think the current implementation is optimal.
+ */
+
 #define hb_be_uint16_put(v,V)  HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
 #define hb_be_uint16_get(v)    (uint16_t) ((v[0] << 8) + v[1])
 #define hb_be_uint16_eq(a,b)   (a[0] == b[0] && a[1] == b[1])
@@ -466,7 +511,6 @@ static inline unsigned char TOLOWER (unsigned char c)
 
 /* Debug */
 
-HB_END_DECLS
 
 #ifndef HB_DEBUG
 #define HB_DEBUG 0
@@ -482,60 +526,106 @@ _hb_debug (unsigned int level,
 #define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
 #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
 
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
+_hb_debug_msg_va (const char *what,
+                 const void *obj,
+                 const char *func,
+                 bool indented,
+                 unsigned int level,
+                 int level_dir,
+                 const char *message,
+                 va_list ap)
+{
+  if (!_hb_debug (level, max_level))
+    return;
+
+  fprintf (stderr, "%-10s", what ? what : "");
+
+  if (obj)
+    fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
+  else
+    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+
+  if (indented) {
+    static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
+    fprintf (stderr, "%2d %s├%s",
+            level,
+            bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
+            level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴");
+  } else
+    fprintf (stderr, "   ├╴");
+
+  if (func) {
+    /* If there's a class name, just write that. */
+    const char *dotdot = strstr (func, "::");
+    const char *space = strchr (func, ' ');
+    if (space && dotdot && space < dotdot)
+      func = space + 1;
+    unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+    fprintf (stderr, "%.*s: ", func_len, func);
+  }
+
+  if (message)
+    vfprintf (stderr, message, ap);
+
+  fprintf (stderr, "\n");
+}
+template <> inline void
+_hb_debug_msg_va<0> (const char *what HB_UNUSED,
+                    const void *obj HB_UNUSED,
+                    const char *func HB_UNUSED,
+                    bool indented HB_UNUSED,
+                    unsigned int level HB_UNUSED,
+                    int level_dir HB_UNUSED,
+                    const char *message HB_UNUSED,
+                    va_list ap HB_UNUSED) {}
+
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
               const void *obj,
               const char *func,
               bool indented,
-              int level,
+              unsigned int level,
+              int level_dir,
               const char *message,
-              ...) HB_PRINTF_FUNC(6, 7);
-template <int max_level> inline bool /* always returns TRUE */
+              ...) HB_PRINTF_FUNC(7, 8);
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
               const void *obj,
               const char *func,
               bool indented,
-              int level,
+              unsigned int level,
+              int level_dir,
               const char *message,
               ...)
 {
   va_list ap;
   va_start (ap, message);
-
-  (void) (_hb_debug (level, max_level) &&
-         fprintf (stderr, "%s(%p): ", what, obj) &&
-         (func && fprintf (stderr, "%s: ", func), TRUE) &&
-         (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
-         vfprintf (stderr, message, ap) &&
-         fprintf (stderr, "\n"));
-
+  _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
   va_end (ap);
-
-  return TRUE;
-}
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
-                 const void *obj,
-                 const char *func,
-                 bool indented,
-                 int level,
-                 const char *message,
-                 ...) HB_PRINTF_FUNC(6, 7);
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
-                 const void *obj,
-                 const char *func,
-                 bool indented,
-                 int level,
-                 const char *message,
-                 ...)
-{
-  return TRUE;
 }
-
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, FALSE, (LEVEL), __VA_ARGS__)
-#define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__)
-#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__)
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+                 const void *obj HB_UNUSED,
+                 const char *func HB_UNUSED,
+                 bool indented HB_UNUSED,
+                 unsigned int level HB_UNUSED,
+                 int level_dir HB_UNUSED,
+                 const char *message HB_UNUSED,
+                 ...) HB_PRINTF_FUNC(7, 8);
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+                 const void *obj HB_UNUSED,
+                 const char *func HB_UNUSED,
+                 bool indented HB_UNUSED,
+                 unsigned int level HB_UNUSED,
+                 int level_dir HB_UNUSED,
+                 const char *message HB_UNUSED,
+                 ...) {}
+
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)      _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    true, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
+#define DEBUG_MSG(WHAT, OBJ, ...)                              _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    false, 0, 0, __VA_ARGS__)
+#define DEBUG_MSG_FUNC(WHAT, OBJ, ...)                         _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__)
 
 
 /*
@@ -545,29 +635,64 @@ _hb_debug_msg<0> (const char *what,
 template <int max_level>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
-                                  const char *what,
-                                  const void *obj,
+                                  const char *what_,
+                                  const void *obj_,
                                   const char *func,
-                                  const char *message) : plevel(plevel_)
+                                  const char *message,
+                                  ...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
   {
-    if (max_level) ++*plevel;
-    /* TODO support variadic args here */
-    _hb_debug_msg<max_level> (what, obj, func, TRUE, *plevel, "%s", message);
+    if (plevel) ++*plevel;
+
+    va_list ap;
+    va_start (ap, message);
+    _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
+    va_end (ap);
+  }
+  inline ~hb_auto_trace_t (void)
+  {
+    if (unlikely (!returned)) {
+      fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN.  This is a bug, please report.  Level was %d.\n", plevel ? *plevel : -1);
+      _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " ");
+      return;
+    }
+
+    if (plevel) --*plevel;
+  }
+
+  inline bool ret (bool v)
+  {
+    if (unlikely (returned)) {
+      fprintf (stderr, "OUCH, double calls to TRACE_RETURN.  This is a bug, please report.\n");
+      return v;
+    }
+
+    _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
+    if (plevel) --*plevel;
+    plevel = NULL;
+    returned = true;
+    return v;
   }
-  ~hb_auto_trace_t (void) { if (max_level) --*plevel; }
 
   private:
   unsigned int *plevel;
+  bool returned;
+  const char *what;
+  const void *obj;
 };
 template <> /* Optimize when tracing is disabled */
 struct hb_auto_trace_t<0> {
-  explicit inline hb_auto_trace_t (unsigned int *plevel_,
-                                  const char *what,
-                                  const void *obj,
-                                  const char *func,
-                                  const char *message) {}
+  explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED,
+                                  const char *what HB_UNUSED,
+                                  const void *obj HB_UNUSED,
+                                  const char *func HB_UNUSED,
+                                  const char *message HB_UNUSED,
+                                  ...) {}
+
+  template <typename T>
+  inline T ret (T v) { return v; }
 };
 
+#define TRACE_RETURN(RET) trace.ret (RET)
 
 /* Misc */
 
@@ -619,8 +744,6 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
 }
 
 
-HB_BEGIN_DECLS
 
-HB_END_DECLS
 
 #endif /* HB_PRIVATE_HH */
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
new file mode 100644 (file)
index 0000000..5cdf8a0
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2012  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_SET_PRIVATE_HH
+#define HB_SET_PRIVATE_HH
+
+#include "hb-private.hh"
+#include "hb-set.h"
+#include "hb-object-private.hh"
+
+
+/* TODO Make this faster and memmory efficient. */
+
+struct _hb_set_t
+{
+  hb_object_header_t header;
+  ASSERT_POD ();
+
+  inline void init (void) {
+    header.init ();
+    clear ();
+  }
+  inline void fini (void) {
+  }
+  inline void clear (void) {
+    memset (elts, 0, sizeof elts);
+  }
+  inline bool empty (void) const {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++)
+      if (elts[i])
+        return false;
+    return true;
+  }
+  inline void add (hb_codepoint_t g)
+  {
+    if (unlikely (g == SENTINEL)) return;
+    if (unlikely (g > MAX_G)) return;
+    elt (g) |= mask (g);
+  }
+  inline void del (hb_codepoint_t g)
+  {
+    if (unlikely (g > MAX_G)) return;
+    elt (g) &= ~mask (g);
+  }
+  inline bool has (hb_codepoint_t g) const
+  {
+    if (unlikely (g > MAX_G)) return false;
+    return !!(elt (g) & mask (g));
+  }
+  inline bool intersects (hb_codepoint_t first,
+                         hb_codepoint_t last) const
+  {
+    if (unlikely (first > MAX_G)) return false;
+    if (unlikely (last  > MAX_G)) last = MAX_G;
+    unsigned int end = last + 1;
+    for (hb_codepoint_t i = first; i < end; i++)
+      if (has (i))
+        return true;
+    return false;
+  }
+  inline bool equal (const hb_set_t *other) const
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      if (elts[i] != other->elts[i])
+        return false;
+    return true;
+  }
+  inline void set (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] = other->elts[i];
+  }
+  inline void union_ (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] |= other->elts[i];
+  }
+  inline void intersect (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] &= other->elts[i];
+  }
+  inline void subtract (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] &= ~other->elts[i];
+  }
+  inline void symmetric_difference (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] ^= other->elts[i];
+  }
+  inline bool next (hb_codepoint_t *codepoint)
+  {
+    if (unlikely (*codepoint == SENTINEL)) {
+      hb_codepoint_t i = get_min ();
+      if (i != SENTINEL) {
+        *codepoint = i;
+       return true;
+      } else
+        return false;
+    }
+    for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++)
+      if (has (i)) {
+        *codepoint = i;
+       return true;
+      }
+    return false;
+  }
+  inline hb_codepoint_t get_min (void) const
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      if (elts[i])
+       for (unsigned int j = 0; i < BITS; j++)
+         if (elts[i] & (1 << j))
+           return i * BITS + j;
+    return SENTINEL;
+  }
+  inline hb_codepoint_t get_max (void) const
+  {
+    for (unsigned int i = ELTS; i; i--)
+      if (elts[i - 1])
+       for (unsigned int j = BITS; j; j--)
+         if (elts[i - 1] & (1 << (j - 1)))
+           return (i - 1) * BITS + (j - 1);
+    return SENTINEL;
+  }
+
+  typedef uint32_t elt_t;
+  static const unsigned int MAX_G = 65536 - 1; /* XXX Fix this... */
+  static const unsigned int SHIFT = 5;
+  static const unsigned int BITS = (1 << SHIFT);
+  static const unsigned int MASK = BITS - 1;
+  static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS;
+  static  const hb_codepoint_t SENTINEL = (hb_codepoint_t) -1;
+
+  elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; }
+  elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
+  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
+
+  elt_t elts[ELTS]; /* XXX 8kb */
+
+  ASSERT_STATIC (sizeof (elt_t) * 8 == BITS);
+  ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G);
+};
+
+
+
+#endif /* HB_SET_PRIVATE_HH */
diff --git a/src/hb-set.cc b/src/hb-set.cc
new file mode 100644 (file)
index 0000000..4225e3c
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright © 2012  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-set-private.hh"
+
+
+
+/* Public API */
+
+
+hb_set_t *
+hb_set_create ()
+{
+  hb_set_t *set;
+
+  if (!(set = hb_object_create<hb_set_t> ()))
+    return hb_set_get_empty ();
+
+  set->clear ();
+
+  return set;
+}
+
+hb_set_t *
+hb_set_get_empty (void)
+{
+  static const hb_set_t _hb_set_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    {0} /* elts */
+  };
+
+  return const_cast<hb_set_t *> (&_hb_set_nil);
+}
+
+hb_set_t *
+hb_set_reference (hb_set_t *set)
+{
+  return hb_object_reference (set);
+}
+
+void
+hb_set_destroy (hb_set_t *set)
+{
+  if (!hb_object_destroy (set)) return;
+
+  set->fini ();
+
+  free (set);
+}
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t        *set,
+                        hb_user_data_key_t *key,
+                        void *              data,
+                        hb_destroy_func_t   destroy,
+                        hb_bool_t           replace)
+{
+  return hb_object_set_user_data (set, key, data, destroy, replace);
+}
+
+void *
+hb_set_get_user_data (hb_set_t        *set,
+                        hb_user_data_key_t *key)
+{
+  return hb_object_get_user_data (set, key);
+}
+
+
+hb_bool_t
+hb_set_allocation_successful (hb_set_t  *set HB_UNUSED)
+{
+  return true;
+}
+
+void
+hb_set_clear (hb_set_t *set)
+{
+  set->clear ();
+}
+
+hb_bool_t
+hb_set_empty (hb_set_t *set)
+{
+  return set->empty ();
+}
+
+hb_bool_t
+hb_set_has (hb_set_t       *set,
+           hb_codepoint_t  codepoint)
+{
+  return set->has (codepoint);
+}
+
+void
+hb_set_add (hb_set_t       *set,
+           hb_codepoint_t  codepoint)
+{
+  set->add (codepoint);
+}
+
+void
+hb_set_del (hb_set_t       *set,
+           hb_codepoint_t  codepoint)
+{
+  set->del (codepoint);
+}
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+             hb_set_t *other)
+{
+  return set->equal (other);
+}
+
+void
+hb_set_set (hb_set_t *set,
+           hb_set_t *other)
+{
+  set->set (other);
+}
+
+void
+hb_set_union (hb_set_t *set,
+             hb_set_t *other)
+{
+  set->union_ (other);
+}
+
+void
+hb_set_intersect (hb_set_t *set,
+                 hb_set_t *other)
+{
+  set->intersect (other);
+}
+
+void
+hb_set_subtract (hb_set_t *set,
+                hb_set_t *other)
+{
+  set->subtract (other);
+}
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+                            hb_set_t *other)
+{
+  set->symmetric_difference (other);
+}
+
+hb_codepoint_t
+hb_set_min (hb_set_t *set)
+{
+  return set->get_min ();
+}
+
+hb_codepoint_t
+hb_set_max (hb_set_t *set)
+{
+  return set->get_max ();
+}
+
+hb_bool_t
+hb_set_next (hb_set_t       *set,
+            hb_codepoint_t *codepoint)
+{
+  return set->next (codepoint);
+}
diff --git a/src/hb-set.h b/src/hb-set.h
new file mode 100644 (file)
index 0000000..9f849cf
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2012  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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_SET_H
+#define HB_SET_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+typedef struct _hb_set_t hb_set_t;
+
+
+hb_set_t *
+hb_set_create (void);
+
+hb_set_t *
+hb_set_get_empty (void);
+
+hb_set_t *
+hb_set_reference (hb_set_t *set);
+
+void
+hb_set_destroy (hb_set_t *set);
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t           *set,
+                     hb_user_data_key_t *key,
+                     void *              data,
+                     hb_destroy_func_t   destroy,
+                     hb_bool_t           replace);
+
+void *
+hb_set_get_user_data (hb_set_t           *set,
+                     hb_user_data_key_t *key);
+
+
+/* Returns false if allocation has failed before */
+hb_bool_t
+hb_set_allocation_successful (hb_set_t  *set);
+
+void
+hb_set_clear (hb_set_t *set);
+
+hb_bool_t
+hb_set_empty (hb_set_t *set);
+
+hb_bool_t
+hb_set_has (hb_set_t       *set,
+           hb_codepoint_t  codepoint);
+
+/* Right now limited to 16-bit integers.  Eventually will do full codepoint range, sans -1
+ * which we will use as a sentinel. */
+void
+hb_set_add (hb_set_t       *set,
+           hb_codepoint_t  codepoint);
+
+void
+hb_set_del (hb_set_t       *set,
+           hb_codepoint_t  codepoint);
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+             hb_set_t *other);
+
+void
+hb_set_set (hb_set_t *set,
+           hb_set_t *other);
+
+void
+hb_set_union (hb_set_t *set,
+             hb_set_t *other);
+
+void
+hb_set_intersect (hb_set_t *set,
+                 hb_set_t *other);
+
+void
+hb_set_subtract (hb_set_t *set,
+                hb_set_t *other);
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+                            hb_set_t *other);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_min (hb_set_t *set);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_max (hb_set_t *set);
+
+/* Pass -1 in to get started. */
+hb_bool_t
+hb_set_next (hb_set_t       *set,
+            hb_codepoint_t *codepoint);
+
+/* TODO: Add faster iteration API? */
+
+
+HB_END_DECLS
+
+#endif /* HB_SET_H */
index 1ff830a..163a5bf 100644 (file)
 
 #include "hb-buffer-private.hh"
 
-#include "hb-ot-shape.h"
-
 #ifdef HAVE_GRAPHITE
-#include "hb-graphite.h"
+#include "hb-graphite2-private.hh"
+#endif
+#ifdef HAVE_UNISCRIBE
+# include "hb-uniscribe-private.hh"
+#endif
+#ifdef HAVE_OT
+# include "hb-ot-shape-private.hh"
+#endif
+#include "hb-fallback-shape-private.hh"
+
+typedef hb_bool_t (*hb_shape_func_t) (hb_font_t          *font,
+                                     hb_buffer_t        *buffer,
+                                     const hb_feature_t *features,
+                                     unsigned int        num_features);
+
+#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape}
+static const struct hb_shaper_pair_t {
+  char name[16];
+  hb_shape_func_t func;
+} all_shapers[] = {
+  /* v--- Add new shapers in the right place here */
+#ifdef HAVE_GRAPHITE
+  HB_SHAPER_IMPLEMENT (graphite2),
 #endif
+#ifdef HAVE_UNISCRIBE
+  HB_SHAPER_IMPLEMENT (uniscribe),
+#endif
+#ifdef HAVE_OT
+  HB_SHAPER_IMPLEMENT (ot),
+#endif
+  HB_SHAPER_IMPLEMENT (fallback) /* should be last */
+};
+#undef HB_SHAPER_IMPLEMENT
+
 
-HB_BEGIN_DECLS
+/* Thread-safe, lock-free, shapers */
 
+static hb_shaper_pair_t *static_shapers;
 
-static void
-hb_shape_internal (hb_font_t          *font,
-                  hb_buffer_t        *buffer,
-                  const hb_feature_t *features,
-                  unsigned int        num_features)
+static
+void free_static_shapers (void)
 {
-  hb_ot_shape (font, buffer, features, num_features);
+  if (unlikely (static_shapers != all_shapers))
+    free (static_shapers);
 }
 
-void
-hb_shape (hb_font_t          *font,
-         hb_buffer_t        *buffer,
-         const hb_feature_t *features,
-         unsigned int        num_features)
+static const hb_shaper_pair_t *
+get_shapers (void)
 {
-  hb_segment_properties_t orig_props;
-
-  orig_props = buffer->props;
-
-  /* If script is set to INVALID, guess from buffer contents */
-  if (buffer->props.script == HB_SCRIPT_INVALID) {
-    hb_unicode_funcs_t *unicode = buffer->unicode;
-    unsigned int count = buffer->len;
-    for (unsigned int i = 0; i < count; i++) {
-      hb_script_t script = hb_unicode_script (unicode, buffer->info[i].codepoint);
-      if (likely (script != HB_SCRIPT_COMMON &&
-                 script != HB_SCRIPT_INHERITED &&
-                 script != HB_SCRIPT_UNKNOWN)) {
-        buffer->props.script = script;
-        break;
-      }
+retry:
+  hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers);
+
+  if (unlikely (!shapers))
+  {
+    char *env = getenv ("HB_SHAPER_LIST");
+    if (!env || !*env) {
+      hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair_t *) all_shapers);
+      return (const hb_shaper_pair_t *) all_shapers;
     }
-  }
 
-  /* If direction is set to INVALID, guess from script */
-  if (buffer->props.direction == HB_DIRECTION_INVALID) {
-    buffer->props.direction = hb_script_get_horizontal_direction (buffer->props.script);
-  }
+    /* Not found; allocate one. */
+    shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers));
+    if (unlikely (!shapers))
+      return (const hb_shaper_pair_t *) all_shapers;
+     memcpy (shapers, all_shapers, sizeof (all_shapers));
+
+     /* Reorder shaper list to prefer requested shapers. */
+    unsigned int i = 0;
+    char *end, *p = env;
+    for (;;) {
+      end = strchr (p, ',');
+      if (!end)
+       end = p + strlen (p);
+
+      for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++)
+       if (end - p == (int) strlen (shapers[j].name) &&
+           0 == strncmp (shapers[j].name, p, end - p))
+       {
+         /* Reorder this shaper to position i */
+        struct hb_shaper_pair_t t = shapers[j];
+        memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i));
+        shapers[i] = t;
+        i++;
+       }
+
+      if (!*end)
+       break;
+      else
+       p = end + 1;
+    }
 
-  /* If language is not set, use default language from locale */
-  if (buffer->props.language == HB_LANGUAGE_INVALID) {
-    /* TODO get_default_for_script? using $LANGUAGE */
-    buffer->props.language = hb_language_get_default ();
+    if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) {
+      free (shapers);
+      goto retry;
+    }
+
+#ifdef HAVE_ATEXIT
+    atexit (free_static_shapers); /* First person registers atexit() callback. */
+#endif
   }
 
-  hb_shape_internal (font, buffer, features, num_features);
+  return shapers;
+}
+
+
+static const char **static_shaper_list;
+
+static
+void free_static_shaper_list (void)
+{
+  free (static_shaper_list);
+}
+
+const char **
+hb_shape_list_shapers (void)
+{
+retry:
+  const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
+
+  if (unlikely (!shaper_list))
+  {
+    /* Not found; allocate one. */
+    shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof (const char *));
+    if (unlikely (!shaper_list)) {
+      static const char *nil_shaper_list[] = {NULL};
+      return nil_shaper_list;
+    }
 
-  buffer->props = orig_props;
+    const hb_shaper_pair_t *shapers = get_shapers ();
+    unsigned int i;
+    for (i = 0; i < ARRAY_LENGTH (all_shapers); i++)
+      shaper_list[i] = shapers[i].name;
+    shaper_list[i] = NULL;
+
+    if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) {
+      free (shaper_list);
+      goto retry;
+    }
+
+#ifdef HAVE_ATEXIT
+    atexit (free_static_shaper_list); /* First person registers atexit() callback. */
+#endif
+  }
+
+  return shaper_list;
 }
 
 
-HB_END_DECLS
+hb_bool_t
+hb_shape_full (hb_font_t          *font,
+              hb_buffer_t        *buffer,
+              const hb_feature_t *features,
+              unsigned int        num_features,
+              const char * const *shaper_list)
+{
+  hb_font_make_immutable (font); /* So we can safely cache stuff on it */
+
+  if (likely (!shaper_list)) {
+    const hb_shaper_pair_t *shapers = get_shapers ();
+    for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++)
+      if (likely (shapers[i].func (font, buffer, features, num_features)))
+        return true;
+  } else {
+    while (*shaper_list) {
+      for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++)
+       if (0 == strcmp (*shaper_list, all_shapers[i].name)) {
+         if (likely (all_shapers[i].func (font, buffer, features, num_features)))
+           return true;
+         break;
+       }
+      shaper_list++;
+    }
+  }
+  return false;
+}
+
+void
+hb_shape (hb_font_t           *font,
+         hb_buffer_t         *buffer,
+         const hb_feature_t  *features,
+         unsigned int         num_features)
+{
+  hb_shape_full (font, buffer, features, num_features, NULL);
+}
index 39383af..1a0d6cf 100644 (file)
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_SHAPE_H
 #define HB_SHAPE_H
 
@@ -41,11 +45,22 @@ typedef struct _hb_feature_t {
   unsigned int  end;
 } hb_feature_t;
 
+
 void
-hb_shape (hb_font_t          *font,
-         hb_buffer_t        *buffer,
-         const hb_feature_t *features,
-         unsigned int        num_features);
+hb_shape (hb_font_t           *font,
+         hb_buffer_t         *buffer,
+         const hb_feature_t  *features,
+         unsigned int         num_features);
+
+hb_bool_t
+hb_shape_full (hb_font_t          *font,
+              hb_buffer_t        *buffer,
+              const hb_feature_t *features,
+              unsigned int        num_features,
+              const char * const *shaper_list);
+
+const char **
+hb_shape_list_shapers (void);
 
 
 HB_END_DECLS
diff --git a/src/hb-tt-font.cc b/src/hb-tt-font.cc
new file mode 100644 (file)
index 0000000..0055ae9
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2011  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-font-private.hh" /* Shall be first since may include windows.h */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-hmtx-table.hh"
+
+#include "hb-blob.h"
+
+#include <string.h>
+
+
+
+#if 0
+struct hb_tt_font_t
+{
+  const struct hhea *hhea;
+  hb_blob_t *hhea_blob;
+};
+
+
+static hb_tt_font_t *
+_hb_tt_font_create (hb_font_t *font)
+{
+  /* TODO Remove this object altogether */
+  hb_tt_font_t *tt = (hb_tt_font_t *) calloc (1, sizeof (hb_tt_font_t));
+
+  tt->hhea_blob = Sanitizer<hhea>::sanitize (hb_face_reference_table (font->face, HB_OT_TAG_hhea));
+  tt->hhea = Sanitizer<hhea>::lock_instance (tt->hhea_blob);
+
+  return tt;
+}
+
+static void
+_hb_tt_font_destroy (hb_tt_font_t *tt)
+{
+  hb_blob_destroy (tt->hhea_blob);
+
+  free (tt);
+}
+
+static inline const hhea&
+_get_hhea (hb_face_t *face)
+{
+//  return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea);
+}
+
+
+/*
+ * hb_tt_font_funcs_t
+ */
+
+static hb_bool_t
+hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
+                      void *font_data HB_UNUSED,
+                      hb_codepoint_t unicode,
+                      hb_codepoint_t variation_selector,
+                      hb_codepoint_t *glyph,
+                      void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
+
+  *glyph = 0;
+  return false;
+}
+
+static hb_position_t
+hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
+                                void *font_data HB_UNUSED,
+                                hb_codepoint_t glyph,
+                                void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph));
+
+  return font->x_scale;
+}
+
+static hb_position_t
+hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
+                                void *font_data HB_UNUSED,
+                                hb_codepoint_t glyph,
+                                void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph));
+
+  return font->y_scale;
+}
+
+static hb_bool_t
+hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
+                               void *font_data HB_UNUSED,
+                               hb_codepoint_t glyph,
+                               hb_position_t *x,
+                               hb_position_t *y,
+                               void *user_data HB_UNUSED)
+{
+  if (font->parent) {
+    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent,
+                                               glyph,
+                                               x, y);
+    if (ret)
+      font->parent_scale_position (x, y);
+    return ret;
+  }
+
+  *x = *y = 0;
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
+                               void *font_data HB_UNUSED,
+                               hb_codepoint_t glyph,
+                               hb_position_t *x,
+                               hb_position_t *y,
+                               void *user_data HB_UNUSED)
+{
+  if (font->parent) {
+    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent,
+                                               glyph,
+                                               x, y);
+    if (ret)
+      font->parent_scale_position (x, y);
+    return ret;
+  }
+
+  *x = *y = 0;
+  return false;
+}
+
+static hb_position_t
+hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
+                                void *font_data HB_UNUSED,
+                                hb_codepoint_t left_glyph,
+                                hb_codepoint_t right_glyph,
+                                void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph));
+
+  return 0;
+}
+
+static hb_position_t
+hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
+                                void *font_data HB_UNUSED,
+                                hb_codepoint_t top_glyph,
+                                hb_codepoint_t bottom_glyph,
+                                void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph));
+
+  return 0;
+}
+
+static hb_bool_t
+hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
+                              void *font_data HB_UNUSED,
+                              hb_codepoint_t glyph,
+                              hb_glyph_extents_t *extents,
+                              void *user_data HB_UNUSED)
+{
+  if (font->parent) {
+    hb_bool_t ret = hb_font_get_glyph_extents (font->parent,
+                                              glyph,
+                                              extents);
+    if (ret) {
+      font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
+      font->parent_scale_distance (&extents->width, &extents->height);
+    }
+    return ret;
+  }
+
+  memset (extents, 0, sizeof (*extents));
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
+                                    void *font_data HB_UNUSED,
+                                    hb_codepoint_t glyph,
+                                    unsigned int point_index,
+                                    hb_position_t *x,
+                                    hb_position_t *y,
+                                    void *user_data HB_UNUSED)
+{
+  if (font->parent) {
+    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent,
+                                                    glyph, point_index,
+                                                    x, y);
+    if (ret)
+      font->parent_scale_position (x, y);
+    return ret;
+  }
+
+  *x = *y = 0;
+  return false;
+}
+
+
+static hb_font_funcs_t _hb_font_funcs_nil = {
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
+    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+  }
+};
+#endif
+
index 2b0ec99..fd1fd44 100644 (file)
@@ -36,7 +36,6 @@
 #include "hb-unicode.h"
 #include "hb-object-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -64,6 +63,7 @@ HB_BEGIN_DECLS
 
 struct _hb_unicode_funcs_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_unicode_funcs_t *parent;
 
@@ -91,19 +91,69 @@ struct _hb_unicode_funcs_t {
 };
 
 
-#if HAVE_GLIB
-extern HB_INTERNAL hb_unicode_funcs_t _hb_glib_unicode_funcs;
+#ifdef HAVE_GLIB
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
 #define _hb_unicode_funcs_default _hb_glib_unicode_funcs
-#elif HAVE_ICU
-extern HB_INTERNAL hb_unicode_funcs_t _hb_icu_unicode_funcs;
+#elif defined(HAVE_ICU)
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
 #define _hb_unicode_funcs_default _hb_icu_unicode_funcs
 #else
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_nil;
+#define HB_UNICODE_FUNCS_NIL 1
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
 #define _hb_unicode_funcs_default _hb_unicode_funcs_nil
 #endif
 
 
+HB_INTERNAL unsigned int
+_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs,
+                                     hb_codepoint_t      unicode);
 
-HB_END_DECLS
+static inline hb_bool_t
+_hb_unicode_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 */
+}
+
+/* Zero-Width invisible characters:
+ *
+ *  00AD  SOFT HYPHEN
+ *  034F  COMBINING GRAPHEME JOINER
+ *
+ *  200B  ZERO WIDTH SPACE
+ *  200C  ZERO WIDTH NON-JOINER
+ *  200D  ZERO WIDTH JOINER
+ *  200E  LEFT-TO-RIGHT MARK
+ *  200F  RIGHT-TO-LEFT MARK
+ *
+ *  2028  LINE SEPARATOR
+ *
+ *  202A  LEFT-TO-RIGHT EMBEDDING
+ *  202B  RIGHT-TO-LEFT EMBEDDING
+ *  202C  POP DIRECTIONAL FORMATTING
+ *  202D  LEFT-TO-RIGHT OVERRIDE
+ *  202E  RIGHT-TO-LEFT OVERRIDE
+ *
+ *  2060  WORD JOINER
+ *  2061  FUNCTION APPLICATION
+ *  2062  INVISIBLE TIMES
+ *  2063  INVISIBLE SEPARATOR
+ *
+ *  FEFF  ZERO WIDTH NO-BREAK SPACE
+ */
+static inline hb_bool_t
+_hb_unicode_is_zero_width (hb_codepoint_t ch)
+{
+  return ((ch & ~0x007F) == 0x2000 && (
+         (ch >= 0x200B && ch <= 0x200F) ||
+         (ch >= 0x202A && ch <= 0x202E) ||
+         (ch >= 0x2060 && ch <= 0x2063) ||
+         (ch == 0x2028)
+        )) || unlikely (ch == 0x0009
+                     || ch == 0x00AD
+                     || ch == 0x034F
+                     || ch == 0xFEFF);
+}
 
 #endif /* HB_UNICODE_PRIVATE_HH */
index aba2cd3..6a21ef3 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "hb-unicode-private.hh"
 
-HB_BEGIN_DECLS
 
 
 /*
@@ -86,8 +85,7 @@ hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
                        hb_codepoint_t     *ab        HB_UNUSED,
                        void               *user_data HB_UNUSED)
 {
-  /* TODO handle Hangul jamo here? */
-  return FALSE;
+  return false;
 }
 
 static hb_bool_t
@@ -97,28 +95,15 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
                          hb_codepoint_t     *b         HB_UNUSED,
                          void               *user_data HB_UNUSED)
 {
-  /* TODO handle Hangul jamo here? */
-  return FALSE;
+  return false;
 }
 
 
-hb_unicode_funcs_t _hb_unicode_funcs_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  NULL, /* parent */
-  TRUE, /* immutable */
-  {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
-    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
-  }
-};
-
 
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_default (void)
 {
-  return &_hb_unicode_funcs_default;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default);
 }
 
 hb_unicode_funcs_t *
@@ -127,10 +112,10 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
   hb_unicode_funcs_t *ufuncs;
 
   if (!(ufuncs = hb_object_create<hb_unicode_funcs_t> ()))
-    return &_hb_unicode_funcs_nil;
+    return hb_unicode_funcs_get_empty ();
 
   if (!parent)
-    parent = &_hb_unicode_funcs_nil;
+    parent = hb_unicode_funcs_get_empty ();
 
   hb_unicode_funcs_make_immutable (parent);
   ufuncs->parent = hb_unicode_funcs_reference (parent);
@@ -145,10 +130,24 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
   return ufuncs;
 }
 
+
+//extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
+const hb_unicode_funcs_t _hb_unicode_funcs_nil = {
+  HB_OBJECT_HEADER_STATIC,
+
+  NULL, /* parent */
+  true, /* immutable */
+  {
+#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
+    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+  }
+};
+
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_empty (void)
 {
-  return &_hb_unicode_funcs_nil;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil);
 }
 
 hb_unicode_funcs_t *
@@ -176,9 +175,10 @@ hb_bool_t
 hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs,
                                hb_user_data_key_t *key,
                                void *              data,
-                               hb_destroy_func_t   destroy)
+                               hb_destroy_func_t   destroy,
+                               hb_bool_t           replace)
 {
-  return hb_object_set_user_data (ufuncs, key, data, destroy);
+  return hb_object_set_user_data (ufuncs, key, data, destroy, replace);
 }
 
 void *
@@ -195,7 +195,7 @@ hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
   if (hb_object_is_inert (ufuncs))
     return;
 
-  ufuncs->immutable = TRUE;
+  ufuncs->immutable = true;
 }
 
 hb_bool_t
@@ -207,7 +207,7 @@ hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
 {
-  return ufuncs->parent ? ufuncs->parent : &_hb_unicode_funcs_nil;
+  return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty ();
 }
 
 
@@ -271,4 +271,58 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
   return ufuncs->func.decompose (ufuncs, ab, a, b, ufuncs->user_data.decompose);
 }
 
-HB_END_DECLS
+
+
+unsigned int
+_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs,
+                                     hb_codepoint_t      unicode)
+{
+  int c = hb_unicode_combining_class (ufuncs, unicode);
+
+  if (unlikely (hb_in_range<int> (c, 27, 33)))
+  {
+    /* Modify the combining-class to suit Arabic better.  See:
+     * http://unicode.org/faq/normalization.html#8
+     * http://unicode.org/faq/normalization.html#9
+     */
+    c = c == 33 ? 27 : c + 1;
+  }
+  else if (unlikely (hb_in_range<int> (c, 10, 25)))
+  {
+    /* The equivalent fix for Hebrew is more complex.
+     *
+     * We permute the "fixed-position" classes 10-25 into the order
+     * described in the SBL Hebrew manual:
+     *
+     * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
+     *
+     * (as recommended by:
+     *  http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
+     *
+     * More details here:
+     * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
+     */
+    static const int permuted_hebrew_classes[25 - 10 + 1] = {
+      /* 10 sheva */        22,
+      /* 11 hataf segol */  15,
+      /* 12 hataf patah */  16,
+      /* 13 hataf qamats */ 17,
+      /* 14 hiriq */        23,
+      /* 15 tsere */        18,
+      /* 16 segol */        19,
+      /* 17 patah */        20,
+      /* 18 qamats */       21,
+      /* 19 holam */        14,
+      /* 20 qubuts */       24,
+      /* 21 dagesh */       12,
+      /* 22 meteg */        25,
+      /* 23 rafe */         13,
+      /* 24 shin dot */     10,
+      /* 25 sin dot */      11,
+    };
+    c = permuted_hebrew_classes[c - 10];
+  }
+
+  return c;
+}
+
index 9aa97a6..b26168f 100644 (file)
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_UNICODE_H
 #define HB_UNICODE_H
 
@@ -66,8 +70,8 @@ hb_bool_t
 hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs,
                                hb_user_data_key_t *key,
                                void *              data,
-                               hb_destroy_func_t   destroy);
-
+                               hb_destroy_func_t   destroy,
+                               hb_bool_t           replace);
 
 void *
 hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs,
diff --git a/src/hb-uniscribe-private.hh b/src/hb-uniscribe-private.hh
new file mode 100644 (file)
index 0000000..239ab0c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2012  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_UNISCRIBE_PRIVATE_HH
+#define HB_UNISCRIBE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-uniscribe.h"
+
+
+HB_INTERNAL hb_bool_t
+_hb_uniscribe_shape (hb_font_t          *font,
+                    hb_buffer_t        *buffer,
+                    const hb_feature_t *features,
+                    unsigned int        num_features);
+
+
+#endif /* HB_UNISCRIBE_PRIVATE_HH */
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
new file mode 100644 (file)
index 0000000..9f84a3c
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright © 2011  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
+ */
+
+#define _WIN32_WINNT 0x0500
+
+#include "hb-private.hh"
+
+#include <windows.h>
+#include <usp10.h>
+
+typedef ULONG WIN_ULONG;
+
+#include "hb-uniscribe.h"
+
+#include "hb-ot-name-table.hh"
+#include "hb-ot-tag.h"
+
+#include "hb-font-private.hh"
+#include "hb-buffer-private.hh"
+
+
+
+#ifndef HB_DEBUG_UNISCRIBE
+#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
+#endif
+
+
+/*
+DWORD GetFontData(
+  __in   HDC hdc,
+  __in   DWORD dwTable,
+  __in   DWORD dwOffset,
+  __out  LPVOID lpvBuffer,
+  __in   DWORD cbData
+);
+*/
+
+static bool
+populate_log_font (LOGFONTW  *lf,
+                  HDC        hdc,
+                  hb_font_t *font)
+{
+  memset (lf, 0, sizeof (*lf));
+  int dpi = GetDeviceCaps (hdc, LOGPIXELSY);
+  lf->lfHeight = -font->y_scale;
+
+  hb_blob_t *blob = Sanitizer<name>::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e')));
+  const name *name_table = Sanitizer<name>::lock_instance (blob);
+  unsigned int len = name_table->get_name (3, 1, 0x409, 4,
+                                          lf->lfFaceName,
+                                          sizeof (lf->lfFaceName[0]) * LF_FACESIZE)
+                                         / sizeof (lf->lfFaceName[0]);
+  hb_blob_destroy (blob);
+
+  if (unlikely (!len)) {
+    DEBUG_MSG (UNISCRIBE, NULL, "Didn't find English name table entry");
+    return false;
+  }
+  if (unlikely (len >= LF_FACESIZE)) {
+    DEBUG_MSG (UNISCRIBE, NULL, "Font name too long");
+    return false;
+  }
+
+  for (unsigned int i = 0; i < len; i++)
+    lf->lfFaceName[i] = hb_be_uint16 (lf->lfFaceName[i]);
+  lf->lfFaceName[len] = 0;
+
+  return true;
+}
+
+
+static hb_user_data_key_t hb_uniscribe_data_key;
+
+
+static struct hb_uniscribe_face_data_t {
+  HANDLE fh;
+} _hb_uniscribe_face_data_nil = {0};
+
+static void
+_hb_uniscribe_face_data_destroy (hb_uniscribe_face_data_t *data)
+{
+  if (data->fh)
+    RemoveFontMemResourceEx (data->fh);
+  free (data);
+}
+
+static hb_uniscribe_face_data_t *
+_hb_uniscribe_face_get_data (hb_face_t *face)
+{
+  hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key);
+  if (likely (data)) return data;
+
+  data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t));
+  if (unlikely (!data))
+    return &_hb_uniscribe_face_data_nil;
+
+
+  hb_blob_t *blob = hb_face_reference_blob (face);
+  unsigned int blob_length;
+  const char *blob_data = hb_blob_get_data (blob, &blob_length);
+  if (unlikely (!blob_length))
+    DEBUG_MSG (UNISCRIBE, face, "Face has empty blob");
+
+  DWORD num_fonts_installed;
+  data->fh = AddFontMemResourceEx ((void *) blob_data, blob_length, 0, &num_fonts_installed);
+  hb_blob_destroy (blob);
+  if (unlikely (!data->fh))
+    DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed");
+
+
+  if (unlikely (!hb_face_set_user_data (face, &hb_uniscribe_data_key, data,
+                                       (hb_destroy_func_t) _hb_uniscribe_face_data_destroy,
+                                       false)))
+  {
+    _hb_uniscribe_face_data_destroy (data);
+    data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key);
+    if (data)
+      return data;
+    else
+      return &_hb_uniscribe_face_data_nil;
+  }
+
+  return data;
+}
+
+
+static struct hb_uniscribe_font_data_t {
+  HDC hdc;
+  LOGFONTW log_font;
+  HFONT hfont;
+  SCRIPT_CACHE script_cache;
+} _hb_uniscribe_font_data_nil = {NULL, NULL, NULL};
+
+static void
+_hb_uniscribe_font_data_destroy (hb_uniscribe_font_data_t *data)
+{
+  if (data->hdc)
+    ReleaseDC (NULL, data->hdc);
+  if (data->hfont)
+    DeleteObject (data->hfont);
+  if (data->script_cache)
+    ScriptFreeCache (&data->script_cache);
+  free (data);
+}
+
+static hb_uniscribe_font_data_t *
+_hb_uniscribe_font_get_data (hb_font_t *font)
+{
+  hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key);
+  if (likely (data)) return data;
+
+  data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t));
+  if (unlikely (!data))
+    return &_hb_uniscribe_font_data_nil;
+
+  data->hdc = GetDC (NULL);
+
+  if (unlikely (!populate_log_font (&data->log_font, data->hdc, font)))
+    DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed");
+  else {
+    data->hfont = CreateFontIndirectW (&data->log_font);
+    if (unlikely (!data->hfont))
+      DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed");
+    if (!SelectObject (data->hdc, data->hfont))
+      DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed");
+  }
+
+  if (unlikely (!hb_font_set_user_data (font, &hb_uniscribe_data_key, data,
+                                       (hb_destroy_func_t) _hb_uniscribe_font_data_destroy,
+                                       false)))
+  {
+    _hb_uniscribe_font_data_destroy (data);
+    data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key);
+    if (data)
+      return data;
+    else
+      return &_hb_uniscribe_font_data_nil;
+  }
+
+  return data;
+}
+
+LOGFONTW *
+hb_uniscribe_font_get_logfontw (hb_font_t *font)
+{
+  hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+  if (unlikely (!font_data))
+    return NULL;
+  return &font_data->log_font;
+}
+
+HFONT
+hb_uniscribe_font_get_hfont (hb_font_t *font)
+{
+  hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+  if (unlikely (!font_data))
+    return 0;
+  return font_data->hfont;
+}
+
+
+hb_bool_t
+_hb_uniscribe_shape (hb_font_t          *font,
+                    hb_buffer_t        *buffer,
+                    const hb_feature_t *features,
+                    unsigned int        num_features)
+{
+  buffer->guess_properties ();
+
+#define FAIL(...) \
+  HB_STMT_START { \
+    DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \
+    return false; \
+  } HB_STMT_END;
+
+  hb_uniscribe_face_data_t *face_data = _hb_uniscribe_face_get_data (font->face);
+  if (unlikely (!face_data->fh))
+    FAIL ("Couldn't get face data");
+
+  hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+  if (unlikely (!font_data->hfont))
+    FAIL ("Couldn't get font font");
+
+  if (unlikely (!buffer->len))
+    return true;
+
+  HRESULT hr;
+
+retry:
+
+  unsigned int scratch_size;
+  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+
+  /* Allocate char buffers; they all fit */
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+  Type *name = (Type *) scratch; \
+  scratch += len * sizeof (name[0]); \
+  scratch_size -= len * sizeof (name[0]);
+
+#define utf16_index() var1.u32
+
+  WCHAR *pchars = (WCHAR *) scratch;
+  unsigned int chars_len = 0;
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    hb_codepoint_t c = buffer->info[i].codepoint;
+    buffer->info[i].utf16_index() = chars_len;
+    if (likely (c < 0x10000))
+      pchars[chars_len++] = c;
+    else if (unlikely (c >= 0x110000))
+      pchars[chars_len++] = 0xFFFD;
+    else {
+      pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10);
+      pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1));
+    }
+  }
+
+  ALLOCATE_ARRAY (WCHAR, wchars, chars_len);
+  ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
+  ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len);
+
+  /* On Windows, we don't care about alignment...*/
+  unsigned int glyphs_size = scratch_size / (sizeof (WORD) +
+                                            sizeof (SCRIPT_GLYPHPROP) +
+                                            sizeof (int) +
+                                            sizeof (GOFFSET) +
+                                            sizeof (uint32_t));
+
+  ALLOCATE_ARRAY (WORD, glyphs, glyphs_size);
+  ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size);
+  ALLOCATE_ARRAY (int, advances, glyphs_size);
+  ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size);
+  ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
+
+
+#define MAX_ITEMS 10
+
+  SCRIPT_ITEM items[MAX_ITEMS + 1];
+  SCRIPT_CONTROL bidi_control = {0};
+  SCRIPT_STATE bidi_state = {0};
+  WIN_ULONG script_tags[MAX_ITEMS];
+  int item_count;
+
+  /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */
+  //bidi_control.fMergeNeutralItems = true;
+  *(uint32_t*)&bidi_control |= 1<<24;
+
+  bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
+//  bidi_state.fOverrideDirection = 1;
+
+  hr = ScriptItemizeOpenType (wchars,
+                             chars_len,
+                             MAX_ITEMS,
+                             &bidi_control,
+                             &bidi_state,
+                             items,
+                             script_tags,
+                             &item_count);
+  if (unlikely (FAILED (hr)))
+    FAIL ("ScriptItemizeOpenType() failed: 0x%08xL", hr);
+
+#undef MAX_ITEMS
+
+  int *range_char_counts = NULL;
+  TEXTRANGE_PROPERTIES **range_properties = NULL;
+  int range_count = 0;
+  if (num_features) {
+    /* TODO setup ranges */
+  }
+
+  OPENTYPE_TAG language_tag = hb_ot_tag_from_language (buffer->props.language);
+
+  unsigned int glyphs_offset = 0;
+  unsigned int glyphs_len;
+  for (unsigned int i = 0; i < item_count; i++)
+  {
+      unsigned int chars_offset = items[i].iCharPos;
+      unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset;
+      OPENTYPE_TAG script_tag = script_tags[i]; /* XXX buffer->props.script */
+
+      hr = ScriptShapeOpenType (font_data->hdc,
+                               &font_data->script_cache,
+                               &items[i].a,
+                               script_tag,
+                               language_tag,
+                               range_char_counts,
+                               range_properties,
+                               range_count,
+                               wchars + chars_offset,
+                               item_chars_len,
+                               glyphs_size - glyphs_offset,
+                               /* out */
+                               log_clusters + chars_offset,
+                               char_props + chars_offset,
+                               glyphs + glyphs_offset,
+                               glyph_props + glyphs_offset,
+                               (int *) &glyphs_len);
+
+      for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++)
+        log_clusters[j] += glyphs_offset;
+
+      if (unlikely (items[i].a.fNoGlyphIndex))
+       FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
+      if (unlikely (hr == E_OUTOFMEMORY))
+      {
+        buffer->ensure (buffer->allocated * 2);
+       if (buffer->in_error)
+         FAIL ("Buffer resize failed");
+       goto retry;
+      }
+      if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT))
+       FAIL ("ScriptShapeOpenType() failed: Font doesn't support script");
+      if (unlikely (FAILED (hr)))
+       FAIL ("ScriptShapeOpenType() failed: 0x%08xL", hr);
+
+      hr = ScriptPlaceOpenType (font_data->hdc,
+                               &font_data->script_cache,
+                               &items[i].a,
+                               script_tag,
+                               language_tag,
+                               range_char_counts,
+                               range_properties,
+                               range_count,
+                               wchars + chars_offset,
+                               log_clusters + chars_offset,
+                               char_props + chars_offset,
+                               item_chars_len,
+                               glyphs + glyphs_offset,
+                               glyph_props + glyphs_offset,
+                               glyphs_len,
+                               /* out */
+                               advances + glyphs_offset,
+                               offsets + glyphs_offset,
+                               NULL);
+      if (unlikely (FAILED (hr)))
+       FAIL ("ScriptPlaceOpenType() failed: 0x%08xL", hr);
+
+      glyphs_offset += glyphs_len;
+  }
+  glyphs_len = glyphs_offset;
+
+  /* Ok, we've got everything we need, now compose output buffer,
+   * very, *very*, carefully! */
+
+  /* Calculate visual-clusters.  That's what we ship. */
+  for (unsigned int i = 0; i < glyphs_len; i++)
+    vis_clusters[i] = -1;
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]];
+    *p = MIN (*p, buffer->info[i].cluster);
+  }
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+    for (unsigned int i = 1; i < glyphs_len; i++)
+      if (!glyph_props[i].sva.fClusterStart)
+       vis_clusters[i] = vis_clusters[i - 1];
+  } else {
+    for (int i = glyphs_len - 2; i >= 0; i--)
+      if (!glyph_props[i].sva.fClusterStart)
+       vis_clusters[i] = vis_clusters[i + 1];
+  }
+
+#undef utf16_index
+
+  buffer->ensure (glyphs_len);
+  if (buffer->in_error)
+    FAIL ("Buffer in error");
+
+#undef FAIL
+
+  /* Set glyph infos */
+  buffer->len = 0;
+  for (unsigned int i = 0; i < glyphs_len; i++)
+  {
+    hb_glyph_info_t *info = &buffer->info[buffer->len++];
+
+    info->codepoint = glyphs[i];
+    info->cluster = vis_clusters[i];
+
+    /* The rest is crap.  Let's store position info there for now. */
+    info->mask = advances[i];
+    info->var1.u32 = offsets[i].du;
+    info->var2.u32 = offsets[i].dv;
+  }
+
+  /* Set glyph positions */
+  buffer->clear_positions ();
+  for (unsigned int i = 0; i < glyphs_len; i++)
+  {
+    hb_glyph_info_t *info = &buffer->info[i];
+    hb_glyph_position_t *pos = &buffer->pos[i];
+
+    /* TODO vertical */
+    pos->x_advance = info->mask;
+    pos->x_offset = info->var1.u32;
+    pos->y_offset = info->var2.u32;
+  }
+
+  /* Wow, done! */
+  return true;
+}
+
+
similarity index 75%
rename from src/hb-ot-shape.h
rename to src/hb-uniscribe.h
index f9560e5..216610e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010  Red Hat, Inc.
+ * Copyright © 2011  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * 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_SHAPE_H
-#define HB_OT_SHAPE_H
+#ifndef HB_UNISCRIBE_H
+#define HB_UNISCRIBE_H
 
-#include "hb-common.h"
-#include "hb-shape.h"
+#include "hb.h"
 
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
 
 HB_BEGIN_DECLS
 
 
-void
-hb_ot_shape (hb_font_t          *font,
-            hb_buffer_t        *buffer,
-            const hb_feature_t *features,
-            unsigned int        num_features);
+LOGFONTW *
+hb_uniscribe_font_get_logfontw (hb_font_t *font);
+
+HFONT
+hb_uniscribe_font_get_hfont (hb_font_t *font);
 
 
 HB_END_DECLS
 
-#endif /* HB_OT_SHAPE_H */
+#endif /* HB_UNISCRIBE_H */
diff --git a/src/hb-version.h b/src/hb-version.h
new file mode 100644 (file)
index 0000000..43ec9cf
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2011  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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_VERSION_H
+#define HB_VERSION_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_VERSION_MAJOR 0
+#define HB_VERSION_MINOR 9
+#define HB_VERSION_MICRO 0
+
+#define HB_VERSION_STRING "0.9.0"
+
+#define HB_VERSION_CHECK(major,minor,micro) \
+       ((major)*10000+(minor)*100+(micro) >= \
+        HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
+
+
+void
+hb_version (unsigned int *major,
+           unsigned int *minor,
+           unsigned int *micro);
+
+const char *
+hb_version_string (void);
+
+hb_bool_t
+hb_version_check (unsigned int major,
+                 unsigned int minor,
+                 unsigned int micro);
+
+
+HB_END_DECLS
+
+#endif /* HB_VERSION_H */
index 47a9b30..43634f9 100644 (file)
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_VERSION_H
 #define HB_VERSION_H
 
diff --git a/src/hb-view.cc b/src/hb-view.cc
deleted file mode 100644 (file)
index 31b2a24..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Copyright © 2010  Behdad Esfahbod
- * Copyright © 2011  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
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <unistd.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <locale.h>
-
-#include <glib.h>
-
-#include <cairo-ft.h>
-#include <hb-ft.h>
-
-HB_BEGIN_DECLS
-
-
-/* Controlled by cmd-line options */
-static int margin_t = 10;
-static int margin_b = 10;
-static int margin_l = 10;
-static int margin_r = 10;
-static int line_space = 0;
-static int face_index = 0;
-static double font_size = 18;
-static const char *fore = "#000000";
-static const char *back = "#ffffff";
-static const char *text = NULL;
-static const char *font_file = NULL;
-static const char *out_file = "/dev/stdout";
-static const char *direction = NULL;
-static const char *script = NULL;
-static const char *language = NULL;
-static hb_feature_t *features = NULL;
-static unsigned int num_features;
-static hb_bool_t annotate = FALSE;
-static hb_bool_t debug = FALSE;
-
-/* Ugh, global vars.  Ugly, but does the job */
-static int width = 0;
-static int height = 0;
-static cairo_surface_t *surface = NULL;
-static cairo_pattern_t *fore_pattern = NULL;
-static cairo_pattern_t *back_pattern = NULL;
-static cairo_font_face_t *cairo_face;
-
-
-G_GNUC_NORETURN static void
-usage (FILE *f, int status)
-{
-  fprintf (f, "usage: hb-view [OPTS...] font-file text\n");
-  exit (status);
-}
-
-G_GNUC_NORETURN static void
-version (void)
-{
-  printf ("hb-view (harfbuzz) %s\n", HB_VERSION_STRING);
-  exit (0);
-}
-
-static void parse_features (char *s);
-
-static void
-parse_opts (int argc, char **argv)
-{
-  argv[0] = (char *) "hb-view";
-  while (1)
-    {
-      int option_index = 0, c;
-      static const struct option long_options[] = {
-       {"annotate", 0, &annotate, TRUE},
-       {"background", 1, 0, 'B'},
-       {"debug", 0, &debug, TRUE},
-       {"direction", 1, 0, 'd'},
-       {"features", 1, 0, 'f'},
-       {"font-size", 1, 0, 's'},
-       {"face-index", 1, 0, 'i'},
-       {"foreground", 1, 0, 'F'},
-       {"help", 0, 0, 'h'},
-       {"language", 1, 0, 'L'},
-       {"line-space", 1, 0, 'l'},
-       {"margin", 1, 0, 'm'},
-       {"output", 1, 0, 'o'},
-       {"script", 1, 0, 'S'},
-       {"version", 0, 0, 'v'},
-       {0, 0, 0, 0}
-      };
-
-      c = getopt_long (argc, argv, "", long_options, &option_index);
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-       case 0:
-         break;
-       case 'h':
-         usage (stdout, 0);
-         break;
-       case 'v':
-         version ();
-         break;
-       case 'i':
-         face_index = atoi (optarg);
-         break;
-       case 'l':
-         line_space = atoi (optarg);
-         break;
-       case 'm':
-         switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) {
-           default: break;
-           case 1: margin_r = margin_t;
-           case 2: margin_b = margin_t;
-           case 3: margin_l = margin_r;
-         }
-         break;
-       case 's':
-         font_size = strtod (optarg, NULL);
-         break;
-       case 'f':
-         parse_features (optarg);
-         break;
-       case 'F':
-         fore = optarg;
-         break;
-       case 'B':
-         back = optarg;
-         break;
-       case 't':
-         text = optarg;
-         break;
-       case 'd':
-         direction = optarg;
-         break;
-       case 'S':
-         script = optarg;
-         break;
-       case 'L':
-         language = optarg;
-         break;
-       case 'o':
-         out_file = optarg;
-         break;
-       case '?':
-         usage (stdout, 1);
-         break;
-       default:
-         break;
-       }
-    }
-  if (optind + 2 != argc)
-    usage (stderr, 1);
-  font_file = argv[optind++];
-  text = argv[optind++];
-}
-
-
-static void
-parse_space (char **pp)
-{
-  char c;
-#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
-  while (c = **pp, ISSPACE (c))
-    (*pp)++;
-#undef ISSPACE
-}
-
-static hb_bool_t
-parse_char (char **pp, char c)
-{
-  parse_space (pp);
-
-  if (**pp != c)
-    return FALSE;
-
-  (*pp)++;
-  return TRUE;
-}
-
-static hb_bool_t
-parse_uint (char **pp, unsigned int *pv)
-{
-  char *p = *pp;
-  unsigned int v;
-
-  v = strtol (p, pp, 0);
-
-  if (p == *pp)
-    return FALSE;
-
-  *pv = v;
-  return TRUE;
-}
-
-
-static hb_bool_t
-parse_feature_value_prefix (char **pp, hb_feature_t *feature)
-{
-  if (parse_char (pp, '-'))
-    feature->value = 0;
-  else {
-    parse_char (pp, '+');
-    feature->value = 1;
-  }
-
-  return TRUE;
-}
-
-static hb_bool_t
-parse_feature_tag (char **pp, hb_feature_t *feature)
-{
-  char *p = *pp, c;
-
-  parse_space (pp);
-
-#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
-  while (c = **pp, ISALNUM(c))
-    (*pp)++;
-#undef ISALNUM
-
-  if (p == *pp)
-    return FALSE;
-
-  **pp = '\0';
-  feature->tag = hb_tag_from_string (p);
-  **pp = c;
-
-  return TRUE;
-}
-
-static hb_bool_t
-parse_feature_indices (char **pp, hb_feature_t *feature)
-{
-  hb_bool_t has_start;
-
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
-
-  if (!parse_char (pp, '['))
-    return TRUE;
-
-  has_start = parse_uint (pp, &feature->start);
-
-  if (parse_char (pp, ':')) {
-    parse_uint (pp, &feature->end);
-  } else {
-    if (has_start)
-      feature->end = feature->start + 1;
-  }
-
-  return parse_char (pp, ']');
-}
-
-static hb_bool_t
-parse_feature_value_postfix (char **pp, hb_feature_t *feature)
-{
-  return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
-}
-
-
-static hb_bool_t
-parse_one_feature (char **pp, hb_feature_t *feature)
-{
-  return parse_feature_value_prefix (pp, feature) &&
-        parse_feature_tag (pp, feature) &&
-        parse_feature_indices (pp, feature) &&
-        parse_feature_value_postfix (pp, feature) &&
-        (parse_char (pp, ',') || **pp == '\0');
-}
-
-static void
-skip_one_feature (char **pp)
-{
-  char *e;
-  e = strchr (*pp, ',');
-  if (e)
-    *pp = e + 1;
-  else
-    *pp = *pp + strlen (*pp);
-}
-
-static void parse_features (char *s)
-{
-  char *p;
-
-  num_features = 0;
-  features = NULL;
-
-  if (!*s)
-    return;
-
-  /* count the features first, so we can allocate memory */
-  p = s;
-  do {
-    num_features++;
-    p = strchr (p, ',');
-    if (p)
-      p++;
-  } while (p);
-
-  features = (hb_feature_t *) calloc (num_features, sizeof (*features));
-
-  /* now do the actual parsing */
-  p = s;
-  num_features = 0;
-  while (*p) {
-    if (parse_one_feature (&p, &features[num_features]))
-      num_features++;
-    else
-      skip_one_feature (&p);
-  }
-}
-
-
-static cairo_glyph_t *
-_hb_cr_text_glyphs (cairo_t *cr,
-                   const char *utf8, int len,
-                   unsigned int *pnum_glyphs)
-{
-  cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
-  FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
-  hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
-  hb_buffer_t *hb_buffer;
-  cairo_glyph_t *cairo_glyphs;
-  hb_glyph_info_t *hb_glyph;
-  hb_glyph_position_t *hb_position;
-  unsigned int num_glyphs, i;
-  hb_position_t x, y;
-
-  hb_buffer = hb_buffer_create (0);
-
-  if (direction)
-    hb_buffer_set_direction (hb_buffer, hb_direction_from_string (direction));
-  if (script)
-    hb_buffer_set_script (hb_buffer, hb_script_from_string (script));
-  if (language)
-    hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
-
-  if (len < 0)
-    len = strlen (utf8);
-  hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len);
-
-  hb_shape (hb_font, hb_buffer, features, num_features);
-
-  num_glyphs = hb_buffer_get_length (hb_buffer);
-  hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
-  hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
-  cairo_glyphs = cairo_glyph_allocate (num_glyphs);
-  x = 0;
-  y = 0;
-  for (i = 0; i < num_glyphs; i++)
-    {
-      cairo_glyphs[i].index = hb_glyph->codepoint;
-      cairo_glyphs[i].x = ( hb_position->x_offset + x) * (1./64);
-      cairo_glyphs[i].y = (-hb_position->y_offset + y) * (1./64);
-      x +=  hb_position->x_advance;
-      y += -hb_position->y_advance;
-
-      hb_glyph++;
-      hb_position++;
-    }
-  hb_buffer_destroy (hb_buffer);
-  hb_font_destroy (hb_font);
-  cairo_ft_scaled_font_unlock_face (scaled_font);
-
-  if (pnum_glyphs)
-    *pnum_glyphs = num_glyphs;
-  return cairo_glyphs;
-}
-
-static cairo_t *
-create_context (void)
-{
-  cairo_t *cr;
-  unsigned int fr, fg, fb, fa, br, bg, bb, ba;
-
-  if (surface)
-    cairo_surface_destroy (surface);
-  if (back_pattern)
-    cairo_pattern_destroy (back_pattern);
-  if (fore_pattern)
-    cairo_pattern_destroy (fore_pattern);
-
-  br = bg = bb = ba = 255;
-  sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
-  fr = fg = fb = 0; fa = 255;
-  sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
-
-  if (!annotate && ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
-    /* grayscale.  use A8 surface */
-    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
-    cr = cairo_create (surface);
-    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-    cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
-    cairo_paint (cr);
-    back_pattern = cairo_pattern_reference (cairo_get_source (cr));
-    cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
-    fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
-  } else {
-    /* color.  use (A)RGB surface */
-    if (ba != 255)
-      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
-    else
-      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
-    cr = cairo_create (surface);
-    cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
-    cairo_paint (cr);
-    back_pattern = cairo_pattern_reference (cairo_get_source (cr));
-    cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
-    fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
-  }
-
-  cairo_set_font_face (cr, cairo_face);
-
-  return cr;
-}
-
-static void
-draw (void)
-{
-  cairo_t *cr;
-  cairo_font_extents_t font_extents;
-
-  cairo_glyph_t *glyphs = NULL;
-  unsigned int num_glyphs = 0;
-
-  const char *end, *p = text;
-  double x, y;
-
-  cr= create_context ();
-
-  cairo_set_font_size (cr, font_size);
-  cairo_font_extents (cr, &font_extents);
-
-  height = 0;
-  width = 0;
-
-  x = margin_l;
-  y = margin_t;
-
-  do {
-    cairo_text_extents_t extents;
-
-    end = strchr (p, '\n');
-    if (!end)
-      end = p + strlen (p);
-
-    if (p != text)
-       y += line_space;
-
-    if (p != end) {
-      glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
-
-      cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
-
-      y += ceil (font_extents.ascent);
-      width = MAX (width, extents.x_advance);
-      cairo_save (cr);
-      cairo_translate (cr, x, y);
-      if (annotate) {
-        unsigned int i;
-        cairo_save (cr);
-
-       /* Draw actual glyph origins */
-       cairo_set_source_rgba (cr, 1., 0., 0., .5);
-       cairo_set_line_width (cr, 5);
-       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
-       for (i = 0; i < num_glyphs; i++) {
-         cairo_move_to (cr, glyphs[i].x, glyphs[i].y);
-         cairo_rel_line_to (cr, 0, 0);
-       }
-       cairo_stroke (cr);
-
-        cairo_restore (cr);
-      }
-      cairo_show_glyphs (cr, glyphs, num_glyphs);
-      cairo_restore (cr);
-      y += ceil (font_extents.height - ceil (font_extents.ascent));
-
-      cairo_glyph_free (glyphs);
-    }
-
-    p = end + 1;
-  } while (*end);
-
-  height = y + margin_b;
-  width += margin_l + margin_r;
-
-  cairo_destroy (cr);
-}
-
-
-
-int
-main (int argc, char **argv)
-{
-  static FT_Library ft_library;
-  static FT_Face ft_face;
-  cairo_status_t status;
-
-  setlocale (LC_ALL, "");
-
-  parse_opts (argc, argv);
-
-  FT_Init_FreeType (&ft_library);
-  if (FT_New_Face (ft_library, font_file, face_index, &ft_face)) {
-    fprintf (stderr, "Failed to open font file `%s'\n", font_file);
-    exit (1);
-  }
-  cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
-
-  draw ();
-  draw ();
-
-  status = cairo_surface_write_to_png (surface, out_file);
-  if (status != CAIRO_STATUS_SUCCESS) {
-    fprintf (stderr, "Failed to write output file `%s': %s\n",
-            out_file, cairo_status_to_string (status));
-    exit (1);
-  }
-
-  if (debug) {
-    free (features);
-
-    cairo_pattern_destroy (fore_pattern);
-    cairo_pattern_destroy (back_pattern);
-    cairo_surface_destroy (surface);
-    cairo_font_face_destroy (cairo_face);
-    cairo_debug_reset_static_data ();
-
-    FT_Done_Face (ft_face);
-    FT_Done_FreeType (ft_library);
-  }
-
-  return 0;
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-warning.cc b/src/hb-warning.cc
new file mode 100644 (file)
index 0000000..4f1f65f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2012  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-atomic-private.hh"
+#include "hb-mutex-private.hh"
+
+
+#if defined(HB_ATOMIC_INT_NIL)
+#ifdef _MSC_VER
+#pragma message("Could not find any system to define atomic_int macros, library may NOT be thread-safe")
+#else
+#warning "Could not find any system to define atomic_int macros, library may NOT be thread-safe"
+#endif
+#endif
+
+#if defined(HB_MUTEX_IMPL_NIL)
+#ifdef _MSC_VER
+#pragma message("Could not find any system to define mutex macros, library may NOT be thread-safe")
+#else
+#warning "Could not find any system to define mutex macros, library may NOT be thread-safe"
+#endif
+#endif
+
+#if defined(HB_ATOMIC_INT_NIL) || defined(HB_MUTEX_IMPL_NIL)
+#ifdef _MSC_VER
+#pragma message("To suppress these warnings, define HB_NO_MT")
+#else
+#warning "To suppress these warnings, define HB_NO_MT"
+#endif
+#endif
+
+
+#include "hb-unicode-private.hh"
+
+#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
+#ifdef _MSC_VER
+#pragma message("Could not find any Unicode functions implementation, you have to provide your own")
+#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS")
+#else
+#warning "Could not find any Unicode functions implementation, you have to provide your own"
+#warning "To suppress this warning, define HB_NO_UNICODE_FUNCS"
+#endif
+#endif
index 0a2ebd9..d36040e 100644 (file)
--- a/src/hb.h
+++ b/src/hb.h
 
 #ifndef HB_H
 #define HB_H
+#define HB_H_IN
 
 #include "hb-blob.h"
 #include "hb-buffer.h"
 #include "hb-common.h"
 #include "hb-font.h"
+#include "hb-set.h"
 #include "hb-shape.h"
 #include "hb-unicode.h"
 #include "hb-version.h"
@@ -38,4 +40,5 @@
 HB_BEGIN_DECLS
 HB_END_DECLS
 
+#undef HB_H_IN
 #endif /* HB_H */
diff --git a/src/indic.cc b/src/indic.cc
new file mode 100644 (file)
index 0000000..3b44076
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012  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-indic-private.hh"
+
+int
+main (void)
+{
+  hb_unicode_funcs_t *funcs = hb_unicode_funcs_get_default ();
+
+  printf ("There are split matras without a Unicode decomposition:\n");
+  for (hb_codepoint_t u = 0; u < 0x110000; u++)
+  {
+    unsigned int type = get_indic_categories (u);
+
+    unsigned int category = type & 0x0F;
+    unsigned int position = type >> 4;
+
+    hb_codepoint_t a, b;
+    if (!hb_unicode_decompose (funcs, u, &a, &b))
+      printf ("U+%04X %x %x\n", u, category, position);
+  }
+}
index 4bf809e..07d3d69 100644 (file)
@@ -24,9 +24,9 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#define HB_OT_LAYOUT_CC
+#include "hb-mutex-private.hh"
 #include "hb-open-file-private.hh"
-#include "hb-ot-layout-gdef-private.hh"
+#include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 #ifdef HAVE_GLIB
@@ -35,7 +35,6 @@
 #include <stdlib.h>
 #include <stdio.h>
 
-HB_BEGIN_DECLS
 
 
 int
@@ -50,7 +49,7 @@ main (int argc, char **argv)
   int len = 0;
 
 #ifdef HAVE_GLIB
-  GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL);
+  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
@@ -125,10 +124,11 @@ main (int argc, char **argv)
            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 (n_langsys == -1)
+             printf ("      Default Language System\n");
+           else
+             printf ("      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");
 
@@ -193,4 +193,3 @@ main (int argc, char **argv)
 }
 
 
-HB_END_DECLS
diff --git a/src/test.cc b/src/test.cc
deleted file mode 100644 (file)
index e3747ce..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright © 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;
-    hb_memory_mode_t mm;
-
-#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;
-    mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
-#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);
-    mm = HB_MEMORY_MODE_WRITABLE;
-#endif
-
-    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
-  }
-
-  printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob));
-
-  /* Create the face */
-  face = hb_face_create (blob, 0 /* first face */);
-
-  /* So, what now? */
-
-  hb_face_destroy (face);
-  hb_blob_destroy (blob);
-
-  return 0;
-}
-
-
-HB_END_DECLS
diff --git a/test/.valgrind-suppressions b/test/.valgrind-suppressions
deleted file mode 100644 (file)
index e69de29..0000000
index bad1118..16a3cd2 100644 (file)
@@ -1,131 +1,5 @@
 # Process this file with automake to produce Makefile.in
 
-NULL =
-EXTRA_DIST =
-CLEANFILES =
-DISTCLEANFILES =
-MAINTAINERCLEANFILES =
-
-if HAVE_GLIB
-AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ $(GLIB_CFLAGS) $(GTHREAD_CFLAGS)
-LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS) $(GTHREAD_LIBS)
-
-EXTRA_DIST += hb-test.h
-
-check_PROGRAMS = $(TEST_PROGS)
-noinst_PROGRAMS = $(TEST_PROGS)
-
-TEST_PROGS = \
-       test-blob \
-       test-buffer \
-       test-common \
-       test-font \
-       test-object \
-       test-shape \
-       test-unicode \
-       test-version \
-       $(NULL)
-
-TEST_PROGS += \
-       test-ot-tag \
-       $(NULL)
-
-# Tests for header compilation
-TEST_PROGS += \
-       test-c \
-       test-cplusplus \
-       $(NULL)
-test_cplusplus_SOURCES = test-cplusplus.cc
-test_c_CPPFLAGS = $(AM_CPPFLAGS)
-test_cplusplus_CPPFLAGS = $(AM_CPPFLAGS)
-if HAVE_ICU
-test_c_CPPFLAGS += $(ICU_CFLAGS)
-test_cplusplus_CPPFLAGS += $(ICU_CFLAGS)
-endif
-if HAVE_FREETYPE
-test_c_CPPFLAGS += $(FREETYPE_CFLAGS)
-test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS)
-# TODO replace freetype with other stuff in the following test
-test_object_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
-test_object_LDADD = $(LDADD) $(FREETYPE_LIBS)
-
-TEST_PROGS += \
-       test-shape-complex \
-       $(NULL)
-test_shape_complex_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
-test_shape_complex_LDADD = $(LDADD) $(FREETYPE_LIBS)
-endif
-
-
-# Default test running environment
-TESTS = $(TEST_PROGS)
-TESTS_ENVIRONMENT = \
-       MALLOC_CHECK_=2 \
-       MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \
-       G_DEBUG=gc-friendly \
-       G_SLICE=always-malloc \
-       srcdir=$(srcdir) \
-       $(ENV)
-
-
-# check-tool: Run tests under $(TOOL)
-check-tool:
-       $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \
-       TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute \
-       env $(TOOL)'
-# check-tool-raw: Run tests under $(TOOL), but don't run under libtool
-check-tool-raw:
-       $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \
-       TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) \
-       env $(TOOL)'
-
-# check-gtester: Run tests under gtester
-GTESTER = gtester
-check-gtester:
-       $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool-raw TOOL="$(GTESTER) --verbose --keep-going"
-
-
-# Check tests under valgrind.  Saves log to log-valgrind.txt
-VALGRIND_FLAGS = \
-       --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \
-       --track-origins=yes \
-       --leak-check=yes
-       $(EXTRA_VALGRIND_FLAGS)
-#      Can't do for now: --show-reachable=yes
-CLEANFILES +=  log-valgrind.txt
-valgrind_verbose = $(valgrind_verbose_$(V))
-valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY))
-valgrind_verbose_0 = | \
-       grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0'
-# TODO: The following check does not fail if valgrind finds error.  It should.
-check-valgrind:
-       $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool TOOL="valgrind $(VALGRIND_FLAGS)" \
-       2>&1 | tee log-valgrind.txt $(valgrind_verbose)
-
-
-# check-symbols: Finds untested API symbols
-symbols-tested.txt: $(TEST_PROGS)
-       $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
-       | grep ' U hb_' | sed 's/.* U hb_/hb_/' \
-       | sort | uniq > $@.tmp && mv $@.tmp $@
-symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so
-       $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
-       | grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \
-       | sort | uniq > $@.tmp && mv $@.tmp $@
-symbols-untested.txt: symbols-tested.txt symbols-exported.txt
-       $(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@
-CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt
-check-symbols: symbols-untested.txt
-       @! cat $^ | grep .
-
-
-
-else
-check-am:
-       @echo "You need to have glib support enabled to run the tests"
-       @exit 77
-endif
-
-.PHONY: check-symbols check-tool check-valgrind
+SUBDIRS = api shaping
 
 -include $(top_srcdir)/git.mk
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
new file mode 100644 (file)
index 0000000..e6c0c01
--- /dev/null
@@ -0,0 +1,125 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+
+if HAVE_GLIB
+AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ -I$(top_builddir)/src/ $(GLIB_CFLAGS) $(GTHREAD_CFLAGS)
+LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS) $(GTHREAD_LIBS)
+
+EXTRA_DIST += hb-test.h
+
+check_PROGRAMS = $(TEST_PROGS)
+noinst_PROGRAMS = $(TEST_PROGS)
+
+TEST_PROGS = \
+       test-blob \
+       test-buffer \
+       test-common \
+       test-font \
+       test-object \
+       test-shape \
+       test-unicode \
+       test-version \
+       $(NULL)
+
+if HAVE_OT
+TEST_PROGS += \
+       test-ot-tag \
+       $(NULL)
+endif
+
+# Tests for header compilation
+TEST_PROGS += \
+       test-c \
+       test-cplusplus \
+       $(NULL)
+test_cplusplus_SOURCES = test-cplusplus.cc
+test_c_CPPFLAGS = $(AM_CPPFLAGS)
+test_cplusplus_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_ICU
+test_c_CPPFLAGS += $(ICU_CFLAGS)
+test_cplusplus_CPPFLAGS += $(ICU_CFLAGS)
+endif
+if HAVE_FREETYPE
+test_c_CPPFLAGS += $(FREETYPE_CFLAGS)
+test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS)
+endif
+
+
+# Default test running environment
+TESTS = $(TEST_PROGS)
+TESTS_ENVIRONMENT = \
+       MALLOC_CHECK_=2 \
+       MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \
+       G_DEBUG=gc-friendly \
+       G_SLICE=always-malloc \
+       srcdir=$(srcdir) \
+       $(ENV)
+
+
+# check-tool: Run tests under $(TOOL)
+check-tool:
+       $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \
+       TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute \
+       env $(TOOL)'
+# check-tool-raw: Run tests under $(TOOL), but don't run under libtool
+check-tool-raw:
+       $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \
+       TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) \
+       env $(TOOL)'
+
+# check-gtester: Run tests under gtester
+GTESTER = gtester
+check-gtester:
+       $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool-raw TOOL="$(GTESTER) --verbose --keep-going"
+
+
+# Check tests under valgrind.  Saves log to log-valgrind.txt
+VALGRIND_FLAGS = \
+       --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \
+       --track-origins=yes \
+       --leak-check=yes
+       $(EXTRA_VALGRIND_FLAGS)
+#      Can't do for now: --show-reachable=yes
+CLEANFILES +=  log-valgrind.txt
+valgrind_verbose = $(valgrind_verbose_$(V))
+valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY))
+valgrind_verbose_0 = | \
+       grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0'
+# TODO: The following check does not fail if valgrind finds error.  It should.
+check-valgrind:
+       $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool TOOL="valgrind $(VALGRIND_FLAGS)" \
+       2>&1 | tee log-valgrind.txt $(valgrind_verbose)
+
+
+# check-symbols: Finds untested API symbols
+symbols-tested.txt: $(TEST_PROGS)
+       $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
+       | grep ' U hb_' | sed 's/.* U hb_/hb_/' \
+       | sort | uniq > $@.tmp && mv $@.tmp $@
+symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so
+       $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
+       | grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \
+       | sort | uniq > $@.tmp && mv $@.tmp $@
+symbols-untested.txt: symbols-tested.txt symbols-exported.txt
+       $(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@
+CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt
+check-symbols: symbols-untested.txt
+       @! cat $^ | grep .
+
+
+
+else
+check-am: err-glib
+err-glib:
+       @echo "You need to have glib support enabled to run the tests"
+       @exit 77
+endif
+
+.PHONY: check-symbols check-tool check-valgrind
+
+-include $(top_srcdir)/git.mk
similarity index 98%
rename from test/hb-test.h
rename to test/api/hb-test.h
index de9a79a..f36d53b 100644 (file)
@@ -152,8 +152,7 @@ typedef GTestFixtureFunc hb_test_fixture_func_t;
 #else
 typedef void (*hb_test_func_t)         (void);
 typedef void (*hb_test_data_func_t)    (gconstpointer user_data);
-typedef void (*hb_test_fixture_func_t) (gpointer      fixture,
-                                        gconstpointer user_data);
+typedef void (*hb_test_fixture_func_t) (void);
 #endif
 
 #if !GLIB_CHECK_VERSION(2,30,0)
similarity index 100%
rename from test/test-blob.c
rename to test/api/test-blob.c
similarity index 98%
rename from test/test-buffer.c
rename to test/api/test-buffer.c
index d387284..ab818d0 100644 (file)
@@ -62,7 +62,7 @@ fixture_init (fixture_t *fixture, gconstpointer user_data)
   hb_buffer_t *b;
   unsigned int i;
 
-  b = fixture->buffer = hb_buffer_create (0);
+  b = fixture->buffer = hb_buffer_create ();
 
   switch (GPOINTER_TO_INT (user_data))
   {
@@ -124,8 +124,8 @@ test_buffer_properties (fixture_t *fixture, gconstpointer user_data)
   hb_buffer_set_script (b, HB_SCRIPT_ARABIC);
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_ARABIC);
 
-  hb_buffer_set_language (b, hb_language_from_string ("fa"));
-  g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa"));
+  hb_buffer_set_language (b, hb_language_from_string ("fa", -1));
+  g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa", -1));
 
 
   /* test reset clears properties */
@@ -344,7 +344,7 @@ test_buffer_utf8_conversion (void)
   hb_glyph_info_t *glyphs;
   unsigned int bytes, chars, i, j, len;
 
-  b = hb_buffer_create (0);
+  b = hb_buffer_create ();
 
   for (i = 0; i < G_N_ELEMENTS (utf8_conversion_tests); i++)
   {
@@ -627,7 +627,7 @@ test_buffer_utf8_validity (void)
   hb_buffer_t *b;
   unsigned int i;
 
-  b = hb_buffer_create (0);
+  b = hb_buffer_create ();
 
   for (i = 0; i < G_N_ELEMENTS (utf8_validity_tests); i++)
   {
@@ -684,7 +684,7 @@ test_buffer_utf16_conversion (void)
   hb_buffer_t *b;
   unsigned int i;
 
-  b = hb_buffer_create (0);
+  b = hb_buffer_create ();
 
   for (i = 0; i < G_N_ELEMENTS (utf16_conversion_tests); i++)
   {
@@ -726,7 +726,6 @@ test_buffer_empty (void)
 
   g_assert (hb_buffer_get_empty ());
   g_assert (hb_buffer_get_empty () == b);
-  g_assert (hb_buffer_get_empty () == hb_buffer_create (-1));
 
   g_assert (!hb_buffer_allocation_successful (b));
 
similarity index 87%
rename from test/test-c.c
rename to test/api/test-c.c
index 543fa7b..25a38e5 100644 (file)
 
 #include <hb.h>
 
-#if HAVE_GLIB
+#ifdef HAVE_GLIB
 #include <hb-glib.h>
 #endif
 
-#if HAVE_ICU
+#ifdef HAVE_ICU
 #include <hb-icu.h>
 #endif
 
-#if HAVE_FREETYPE
+#ifdef HAVE_FREETYPE
 #include <hb-ft.h>
 #endif
 
+#ifdef HAVE_OT
+#include <hb-ot.h>
+#endif
+
+#ifdef HAVE_UNISCRIBE
+#include <hb-uniscribe.h>
+#endif
+
 int
 main (int argc, char **argv)
 {
-  return 0;
+  return !*hb_shape_list_shapers ();
 }
similarity index 70%
rename from test/test-common.c
rename to test/api/test-common.c
index d5cbfc7..74b50be 100644 (file)
@@ -51,42 +51,55 @@ test_types_int (void)
 static void
 test_types_direction (void)
 {
-  g_assert_cmpint ((signed) HB_DIRECTION_INVALID, ==, -1);
-  g_assert_cmpint (HB_DIRECTION_LTR, ==, 0);
+  g_assert_cmpint ((signed) HB_DIRECTION_INVALID, ==, 0);
+  g_assert_cmpint (HB_DIRECTION_LTR, !=, 0);
 
   g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_LTR));
   g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_RTL));
   g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_TTB));
   g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_BTT));
+  g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_INVALID));
 
   g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_LTR));
   g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_RTL));
   g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_TTB));
   g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_BTT));
+  g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_INVALID));
 
   g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_LTR));
   g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_TTB));
   g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_RTL));
   g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_BTT));
+  g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_INVALID));
 
   g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_LTR));
   g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_TTB));
   g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_RTL));
   g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_BTT));
+  g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_INVALID));
+
+  g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_LTR));
+  g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_TTB));
+  g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_RTL));
+  g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_BTT));
+  g_assert (!HB_DIRECTION_IS_VALID (HB_DIRECTION_INVALID));
+  g_assert (!HB_DIRECTION_IS_VALID ((hb_direction_t) 0x12345678));
 
   g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_LTR), ==, HB_DIRECTION_RTL);
   g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_RTL), ==, HB_DIRECTION_LTR);
   g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_TTB), ==, HB_DIRECTION_BTT);
   g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_BTT), ==, HB_DIRECTION_TTB);
-
-  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (NULL));
-  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (""));
-  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("x"));
-  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("r"));
-  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("rtl"));
-  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("RtL"));
-  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("right-to-left"));
-  g_assert_cmpint (HB_DIRECTION_TTB, ==, hb_direction_from_string ("ttb"));
+  //g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_INVALID), ==, HB_DIRECTION_INVALID);
+
+  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (NULL, -1));
+  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("", -1));
+  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("t", 0));
+  g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("x", -1));
+  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("r", -1));
+  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("rtl", -1));
+  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("RtL", -1));
+  g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("right-to-left", -1));
+  g_assert_cmpint (HB_DIRECTION_TTB, ==, hb_direction_from_string ("ttb", -1));
 
   g_assert (0 == strcmp ("ltr", hb_direction_to_string (HB_DIRECTION_LTR)));
   g_assert (0 == strcmp ("rtl", hb_direction_to_string (HB_DIRECTION_RTL)));
@@ -102,14 +115,20 @@ test_types_tag (void)
 
   g_assert_cmphex (HB_TAG ('a','B','c','D'), ==, 0x61426344);
 
-  g_assert_cmphex (hb_tag_from_string ("aBcDe"), ==, 0x61426344);
-  g_assert_cmphex (hb_tag_from_string ("aBcD"),  ==, 0x61426344);
-  g_assert_cmphex (hb_tag_from_string ("aBc"),   ==, 0x61426320);
-  g_assert_cmphex (hb_tag_from_string ("aB"),    ==, 0x61422020);
-  g_assert_cmphex (hb_tag_from_string ("a"),     ==, 0x61202020);
-
-  g_assert_cmphex (hb_tag_from_string (""),      ==, HB_TAG_NONE);
-  g_assert_cmphex (hb_tag_from_string (NULL),    ==, HB_TAG_NONE);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe", -1), ==, 0x61426344);
+  g_assert_cmphex (hb_tag_from_string ("aBcD", -1),  ==, 0x61426344);
+  g_assert_cmphex (hb_tag_from_string ("aBc", -1),   ==, 0x61426320);
+  g_assert_cmphex (hb_tag_from_string ("aB", -1),    ==, 0x61422020);
+  g_assert_cmphex (hb_tag_from_string ("a", -1),     ==, 0x61202020);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe",  1), ==, 0x61202020);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe",  2), ==, 0x61422020);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe",  3), ==, 0x61426320);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe",  4), ==, 0x61426344);
+  g_assert_cmphex (hb_tag_from_string ("aBcDe",  4), ==, 0x61426344);
+
+  g_assert_cmphex (hb_tag_from_string ("", -1),      ==, HB_TAG_NONE);
+  g_assert_cmphex (hb_tag_from_string ("x", 0),      ==, HB_TAG_NONE);
+  g_assert_cmphex (hb_tag_from_string (NULL, -1),    ==, HB_TAG_NONE);
 }
 
 static void
@@ -127,23 +146,26 @@ test_types_script (void)
   g_assert_cmpint (HB_SCRIPT_INVALID, ==, (hb_script_t) HB_TAG_NONE);
   g_assert_cmphex (HB_SCRIPT_ARABIC, !=, HB_SCRIPT_LATIN);
 
-  g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (NULL));
-  g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (""));
-  g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x"));
+  g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (NULL, -1));
+  g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("", -1));
+  g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("x", 0));
+  g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x", -1));
 
-  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("arab"));
-  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arab"));
-  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("ARAB"));
+  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("arab", -1));
+  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arab", -1));
+  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("ARAB", -1));
+  g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arabic", 6));
+  g_assert_cmphex (HB_SCRIPT_ARABIC, !=, hb_script_from_string ("Arabic", 3));
 
   g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (arab));
   g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (Arab));
   g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (ARAB));
 
   /* Arbitrary tags that look like may be valid ISO 15924 should be preserved. */
-  g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_string ("wWyZ"));
+  g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_string ("wWyZ", -1));
   g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_iso15924_tag (wWyZ));
   /* Otherwise, UNKNOWN should be returned. */
-  g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x123"));
+  g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x123", -1));
   g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_iso15924_tag (x123));
 
   g_assert_cmphex (hb_script_to_iso15924_tag (HB_SCRIPT_ARABIC), ==, Arab);
@@ -157,10 +179,10 @@ test_types_script (void)
 static void
 test_types_language (void)
 {
-  hb_language_t fa = hb_language_from_string ("fa");
-  hb_language_t fa_IR = hb_language_from_string ("fa_IR");
-  hb_language_t fa_ir = hb_language_from_string ("fa-ir");
-  hb_language_t en = hb_language_from_string ("en");
+  hb_language_t fa = hb_language_from_string ("fa", -1);
+  hb_language_t fa_IR = hb_language_from_string ("fa_IR", -1);
+  hb_language_t fa_ir = hb_language_from_string ("fa-ir", -1);
+  hb_language_t en = hb_language_from_string ("en", -1);
 
   g_assert (HB_LANGUAGE_INVALID == NULL);
 
@@ -172,11 +194,14 @@ test_types_language (void)
   g_assert (en != fa);
 
   /* Test recall */
-  g_assert (en == hb_language_from_string ("en"));
-  g_assert (en == hb_language_from_string ("eN"));
-
-  g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (NULL));
-  g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (""));
+  g_assert (en == hb_language_from_string ("en", -1));
+  g_assert (en == hb_language_from_string ("eN", -1));
+  g_assert (en == hb_language_from_string ("Enx", 2));
+
+  g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (NULL, -1));
+  g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("", -1));
+  g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("en", 0));
+  g_assert (HB_LANGUAGE_INVALID != hb_language_from_string ("en", 1));
   g_assert (NULL == hb_language_to_string (HB_LANGUAGE_INVALID));
 
   /* Not sure how to test this better.  Setting env vars
similarity index 100%
rename from test/test-font.c
rename to test/api/test-font.c
similarity index 93%
rename from test/test-object.c
rename to test/api/test-object.c
index d0145c5..66e8d33 100644 (file)
 /* Unit tests for hb-object-private.h */
 
 
-#ifdef HAVE_FREETYPE
-#include <hb-ft.h>
-#endif
-
-
 static void *
 create_blob (void)
 {
@@ -49,12 +44,12 @@ create_blob_inert (void)
 static void *
 create_buffer (void)
 {
-  return hb_buffer_create (0);
+  return hb_buffer_create ();
 }
 static void *
 create_buffer_inert (void)
 {
-  return hb_buffer_create (-1);
+  return NULL;
 }
 
 static void *
@@ -93,11 +88,7 @@ create_font_funcs (void)
 static void *
 create_font_funcs_inert (void)
 {
-#ifdef HAVE_FREETYPE
-  return hb_ft_get_font_funcs ();
-#else
   return NULL;
-#endif
 }
 
 static void *
@@ -116,7 +107,7 @@ create_unicode_funcs_inert (void)
 typedef void     *(*create_func_t)         (void);
 typedef void     *(*reference_func_t)      (void *obj);
 typedef void      (*destroy_func_t)        (void *obj);
-typedef hb_bool_t (*set_user_data_func_t)  (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy);
+typedef hb_bool_t (*set_user_data_func_t)  (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy, hb_bool_t replace);
 typedef void *    (*get_user_data_func_t)  (void *obj, hb_user_data_key_t *key);
 typedef void      (*make_immutable_func_t) (void *obj);
 typedef hb_bool_t (*is_immutable_func_t)   (void *obj);
@@ -247,7 +238,7 @@ test_object (void)
       if (o->is_immutable)
        g_assert (!o->is_immutable (obj));
 
-      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
       g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
 
       if (o->is_immutable) {
@@ -256,38 +247,39 @@ test_object (void)
       }
 
       /* Should still work even if object is made immutable */
-      g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1));
+      g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1, TRUE));
       g_assert (o->get_user_data (obj, &key[1]) == &data[1]);
 
-      g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0));
+      g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE));
       g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
-      g_assert (o->set_user_data (obj, &key[0], &data[1], NULL));
+      g_assert (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE));
       g_assert (data[0].freed);
       g_assert (o->get_user_data (obj, &key[0]) == &data[1]);
       g_assert (!data[1].freed);
 
       data[0].freed = FALSE;
-      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
       g_assert (!data[0].freed);
-      g_assert (o->set_user_data (obj, &key[0], NULL, NULL));
+      g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE));
       g_assert (data[0].freed);
 
       data[0].freed = FALSE;
       global_data = 0;
-      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
+      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE));
       g_assert_cmpuint (global_data, ==, 0);
-      g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up));
+      g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE));
       g_assert_cmpuint (global_data, ==, 0);
-      g_assert (o->set_user_data (obj, &key[0], NULL, NULL));
+      g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE));
       g_assert_cmpuint (global_data, ==, 1);
 
       global_data = 0;
       for (j = 2; j < 1000; j++)
-       g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up));
+       g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE));
       for (j = 2; j < 1000; j++)
        g_assert (o->get_user_data (obj, &key[j]) == &data[j]);
       for (j = 100; j < 1000; j++)
-       g_assert (o->set_user_data (obj, &key[j], NULL, NULL));
+       g_assert (o->set_user_data (obj, &key[j], NULL, NULL, TRUE));
       for (j = 2; j < 100; j++)
        g_assert (o->get_user_data (obj, &key[j]) == &data[j]);
       for (j = 100; j < 1000; j++)
@@ -298,8 +290,8 @@ test_object (void)
        * Make sure it doesn't deadlock or corrupt memory. */
       deadlock_test.klass = o;
       deadlock_test.object = obj;
-      g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test));
-      g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL));
+      g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test, TRUE));
+      g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL, TRUE));
 
       g_assert (!data[1].freed);
       o->destroy (obj);
@@ -321,7 +313,7 @@ test_object (void)
       if (o->is_immutable)
        g_assert (o->is_immutable (obj));
 
-      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
       g_assert (!o->get_user_data (obj, &key[0]));
 
       o->destroy (obj);
@@ -349,7 +341,7 @@ test_object (void)
       if (o->is_immutable)
        g_assert (o->is_immutable (obj));
 
-      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
       g_assert (!o->get_user_data (obj, &key[0]));
 
       o->destroy (obj);
similarity index 89%
rename from test/test-ot-tag.c
rename to test/api/test-ot-tag.c
index d440dbd..81b6678 100644 (file)
@@ -40,7 +40,7 @@ test_simple_tags (const char *s, hb_script_t script)
   hb_script_t t1, t2;
 
   g_test_message ("Testing script %c%c%c%c: tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s);
-  tag = hb_tag_from_string (s);
+  tag = hb_tag_from_string (s, -1);
 
   hb_ot_tags_from_script (script, &t1, &t2);
 
@@ -57,8 +57,8 @@ test_indic_tags (const char *s1, const char *s2, hb_script_t script)
   hb_script_t t1, t2;
 
   g_test_message ("Testing script %c%c%c%c: new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2);
-  tag1 = hb_tag_from_string (s1);
-  tag2 = hb_tag_from_string (s2);
+  tag1 = hb_tag_from_string (s1, -1);
+  tag2 = hb_tag_from_string (s2, -1);
 
   hb_ot_tags_from_script (script, &t1, &t2);
 
@@ -85,14 +85,14 @@ test_ot_tag_script_degenerate (void)
   test_simple_tags ("DFLT", HB_SCRIPT_INVALID);
 
   /* Spaces are replaced */
-  g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be  ")), ==, hb_script_from_string ("Beee"));
+  g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be  ")), ==, hb_script_from_string ("Beee", -1));
 }
 
 static void
 test_ot_tag_script_simple (void)
 {
   /* Arbitrary non-existent script */
-  test_simple_tags ("wwyz", hb_script_from_string ("wWyZ"));
+  test_simple_tags ("wwyz", hb_script_from_string ("wWyZ", -1));
 
   /* These we don't really care about */
   test_simple_tags ("zyyy", HB_SCRIPT_COMMON);
@@ -141,8 +141,8 @@ test_ot_tag_script_indic (void)
 static void
 test_language_two_way (const char *tag_s, const char *lang_s)
 {
-  hb_language_t lang = hb_language_from_string (lang_s);
-  hb_tag_t tag = hb_tag_from_string (tag_s);
+  hb_language_t lang = hb_language_from_string (lang_s, -1);
+  hb_tag_t tag = hb_tag_from_string (tag_s, -1);
 
   g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s);
 
@@ -153,8 +153,8 @@ test_language_two_way (const char *tag_s, const char *lang_s)
 static void
 test_tag_from_language (const char *tag_s, const char *lang_s)
 {
-  hb_language_t lang = hb_language_from_string (lang_s);
-  hb_tag_t tag = hb_tag_from_string (tag_s);
+  hb_language_t lang = hb_language_from_string (lang_s, -1);
+  hb_tag_t tag = hb_tag_from_string (tag_s, -1);
 
   g_test_message ("Testing language %s -> tag %s", lang_s, tag_s);
 
@@ -164,8 +164,8 @@ test_tag_from_language (const char *tag_s, const char *lang_s)
 static void
 test_tag_to_language (const char *tag_s, const char *lang_s)
 {
-  hb_language_t lang = hb_language_from_string (lang_s);
-  hb_tag_t tag = hb_tag_from_string (tag_s);
+  hb_language_t lang = hb_language_from_string (lang_s, -1);
+  hb_tag_t tag = hb_tag_from_string (tag_s, -1);
 
   g_test_message ("Testing tag %s -> language %s", tag_s, lang_s);
 
@@ -213,12 +213,17 @@ test_ot_tag_language (void)
 
   test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-zxc");
 
+  test_tag_from_language ("dflt", "xy");
+  test_tag_from_language ("XYZ", "xyz"); /* Unknown ISO 639-3 */
+  test_tag_from_language ("XYZ", "xyz-qw"); /* Unknown ISO 639-3 */
+
   /* Test that x-hbot overrides the base language */
   test_tag_from_language ("ABC", "fa-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "fa-ir-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "zh-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "zh-cn-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "zh-xy-x-hbotabc-zxc");
+  test_tag_from_language ("ABC", "xyz-xy-x-hbotabc-zxc");
 }
 
 int
similarity index 92%
rename from test/test-shape.c
rename to test/api/test-shape.c
index 5a41f0c..ccf6eed 100644 (file)
@@ -106,7 +106,7 @@ test_shape (void)
   hb_font_set_funcs (font, ffuncs, NULL, NULL);
   hb_font_funcs_destroy (ffuncs);
 
-  buffer =  hb_buffer_create (0);
+  buffer =  hb_buffer_create ();
   hb_buffer_set_direction (buffer, HB_DIRECTION_LTR);
   hb_buffer_add_utf8 (buffer, TesT, 4, 0, 4);
 
@@ -138,6 +138,18 @@ test_shape (void)
   hb_font_destroy (font);
 }
 
+static void
+test_shape_list (void)
+{
+  const char **shapers = hb_shape_list_shapers ();
+
+  unsigned int i;
+  for (i = 0; shapers[i]; i++)
+    ;
+
+  g_assert_cmpint (i, >, 1);
+  g_assert (!strcmp (shapers[i - 1], "fallback"));
+}
 
 int
 main (int argc, char **argv)
@@ -145,6 +157,9 @@ main (int argc, char **argv)
   hb_test_init (&argc, &argv);
 
   hb_test_add (test_shape);
+  /* TODO test fallback shaper */
+  /* TODO test shaper_full */
+  hb_test_add (test_shape_list);
 
   return hb_test_run();
 }
similarity index 99%
rename from test/test-unicode.c
rename to test/api/test-unicode.c
index 9f526d7..a420bf3 100644 (file)
@@ -142,13 +142,15 @@ static const test_pair_t combining_class_tests[] =
   {   0x302C, 232 },
   {   0x0362, 233 },
   {   0x0360, 234 },
-  {   0x1DCD, 234 },
   {   0x0345, 240 },
 
   { 0x111111, 0 }
 };
 static const test_pair_t combining_class_tests_more[] =
 {
+  /* Unicode-5.1 character additions */
+  {   0x1DCD, 234 },
+
   /* Unicode-5.2 character additions */
   {   0xA8E0, 230 },
 
@@ -779,7 +781,6 @@ test_unicode_script_roundtrip (gconstpointer user_data)
 }
 
 
-/* TODO test compose() and decompose() */
 static void
 test_unicode_normalization (gconstpointer user_data)
 {
@@ -824,6 +825,7 @@ test_unicode_normalization (gconstpointer user_data)
   /* Not decomposable */
   g_assert (!hb_unicode_decompose (uf, 0x0041, &a, &b) && a == 0x0041 && b == 0);
   g_assert (!hb_unicode_decompose (uf, 0xFB01, &a, &b) && a == 0xFB01 && b == 0);
+  g_assert (!hb_unicode_decompose (uf, 0x1F1EF, &a, &b) && a == 0x1F1EF && b == 0);
 
   /* Singletons */
   g_assert (hb_unicode_decompose (uf, 0x212B, &a, &b) && a == 0x00C5 && b == 0);
similarity index 100%
rename from test/test-version.c
rename to test/api/test-version.c
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
new file mode 100644 (file)
index 0000000..4fb762c
--- /dev/null
@@ -0,0 +1,35 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+
+manifests:
+       @$(srcdir)/hb-manifest-update "$(srcdir)/texts" "$(srcdir)/fonts"
+
+EXTRA_DIST += \
+       hb-diff \
+       hb-diff-colorize \
+       hb-diff-filter-failures \
+       hb-diff-ngrams \
+       hb-diff-stat \
+       hb-manifest-read \
+       hb-manifest-update \
+       hb-unicode-decode \
+       hb-unicode-encode \
+       hb-unicode-prettyname \
+       $(NULL)
+
+# TODO Figure out Python stuff
+EXTRA_DIST += \
+       hb_test_tools.py \
+       $(NULL)
+CLEANFILES += \
+       hb_test_tools.py[co] \
+       $(NULL)
+
+.PHONY: manifests
+
+-include $(top_srcdir)/git.mk
diff --git a/test/shaping/Makefile.in b/test/shaping/Makefile.in
new file mode 100644 (file)
index 0000000..c1969c4
--- /dev/null
@@ -0,0 +1,406 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Process this file with automake to produce Makefile.in
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = test/shaping
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo "  GEN   " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_FT_CFLAGS = @CAIRO_FT_CFLAGS@
+CAIRO_FT_LIBS = @CAIRO_FT_LIBS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
+GOBJECT_LIBS = @GOBJECT_LIBS@
+GRAPHITE2_CFLAGS = @GRAPHITE2_CFLAGS@
+GRAPHITE2_LIBS = @GRAPHITE2_LIBS@
+GREP = @GREP@
+GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
+GTHREAD_LIBS = @GTHREAD_LIBS@
+HB_LIBTOOL_VERSION_INFO = @HB_LIBTOOL_VERSION_INFO@
+HB_VERSION = @HB_VERSION@
+HB_VERSION_MAJOR = @HB_VERSION_MAJOR@
+HB_VERSION_MICRO = @HB_VERSION_MICRO@
+HB_VERSION_MINOR = @HB_VERSION_MINOR@
+ICU_CFLAGS = @ICU_CFLAGS@
+ICU_LIBS = @ICU_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+UNISCRIBE_CFLAGS = @UNISCRIBE_CFLAGS@
+UNISCRIBE_LIBS = @UNISCRIBE_LIBS@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+NULL = 
+
+# TODO Figure out Python stuff
+EXTRA_DIST = hb-diff hb-diff-colorize hb-diff-filter-failures \
+       hb-diff-ngrams hb-diff-stat hb-manifest-read \
+       hb-manifest-update hb-unicode-decode hb-unicode-encode \
+       hb-unicode-prettyname $(NULL) hb_test_tools.py $(NULL)
+CLEANFILES = hb_test_tools.py[co] $(NULL)
+DISTCLEANFILES = 
+MAINTAINERCLEANFILES = 
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu test/shaping/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --gnu test/shaping/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+       -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+       -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+       distclean distclean-generic distclean-libtool distdir dvi \
+       dvi-am html html-am info info-am install install-am \
+       install-data install-data-am install-dvi install-dvi-am \
+       install-exec install-exec-am install-html install-html-am \
+       install-info install-info-am install-man install-pdf \
+       install-pdf-am install-ps install-ps-am install-strip \
+       installcheck installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-generic \
+       mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
+
+
+manifests:
+       @$(srcdir)/hb-manifest-update "$(srcdir)/texts" "$(srcdir)/fonts"
+
+.PHONY: manifests
+
+-include $(top_srcdir)/git.mk
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/test/shaping/hb-diff b/test/shaping/hb-diff
new file mode 100755 (executable)
index 0000000..6a13fa2
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+import sys, os
+
+if len (sys.argv) < 2:
+       print "usage: %s FILES..." % sys.argv[0]
+       sys.exit (1)
+
+ZipDiffer.diff_files (FileHelpers.open_file_or_stdin (f) for f in sys.argv[1:])
diff --git a/test/shaping/hb-diff-colorize b/test/shaping/hb-diff-colorize
new file mode 100755 (executable)
index 0000000..4e045d2
--- /dev/null
@@ -0,0 +1,7 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+formatter = ColorFormatter.Auto (sys.argv)
+colorizer = DiffColorizer (formatter=formatter)
+UtilMains.process_multiple_files (FilterHelpers.filter_printer_function_no_newline (colorizer.colorize_diff))
diff --git a/test/shaping/hb-diff-filter-failures b/test/shaping/hb-diff-filter-failures
new file mode 100755 (executable)
index 0000000..4fe218a
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (FilterHelpers.filter_printer_function_no_newline (DiffFilters.filter_failures))
diff --git a/test/shaping/hb-diff-ngrams b/test/shaping/hb-diff-ngrams
new file mode 100755 (executable)
index 0000000..a496447
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (DiffSinks.print_ngrams)
diff --git a/test/shaping/hb-diff-stat b/test/shaping/hb-diff-stat
new file mode 100755 (executable)
index 0000000..81626e1
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (DiffSinks.print_stat)
diff --git a/test/shaping/hb-manifest-read b/test/shaping/hb-manifest-read
new file mode 100755 (executable)
index 0000000..f486bcc
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_args (FilterHelpers.filter_printer_function (Manifest.read), mnemonic="DIR")
diff --git a/test/shaping/hb-manifest-update b/test/shaping/hb-manifest-update
new file mode 100755 (executable)
index 0000000..b963f22
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_args (Manifest.update_recursive, mnemonic="DIR")
diff --git a/test/shaping/hb-unicode-decode b/test/shaping/hb-unicode-decode
new file mode 100755 (executable)
index 0000000..5b00eae
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.decode, "UNICODE_STRING")
diff --git a/test/shaping/hb-unicode-encode b/test/shaping/hb-unicode-encode
new file mode 100755 (executable)
index 0000000..11bf365
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.encode, "UNICODE_STRING", '')
diff --git a/test/shaping/hb-unicode-prettyname b/test/shaping/hb-unicode-prettyname
new file mode 100755 (executable)
index 0000000..ecc26cc
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.pretty_names, "UNICODE_CODEPOINTS", \
+                                           concat_separator = ' ')
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
new file mode 100644 (file)
index 0000000..47fa6eb
--- /dev/null
@@ -0,0 +1,511 @@
+#!/usr/bin/python
+
+import sys, os, re, difflib, unicodedata, errno, cgi
+from itertools import *
+
+diff_symbols = "-+=*&^%$#@!~/"
+diff_colors = ['red', 'green', 'blue']
+
+class ColorFormatter:
+
+       class Null:
+               @staticmethod
+               def start_color (c): return ''
+               @staticmethod
+               def end_color (): return ''
+               @staticmethod
+               def escape (s): return s
+               @staticmethod
+               def newline (): return '\n'
+
+       class ANSI:
+               @staticmethod
+               def start_color (c):
+                       return {
+                               'red': '\033[41;37;1m',
+                               'green': '\033[42;37;1m',
+                               'blue': '\033[44;37;1m',
+                       }[c]
+               @staticmethod
+               def end_color ():
+                       return '\033[m'
+               @staticmethod
+               def escape (s): return s
+               @staticmethod
+               def newline (): return '\n'
+
+       class HTML:
+               @staticmethod
+               def start_color (c):
+                       return '<span style="background:%s">' % c
+               @staticmethod
+               def end_color ():
+                       return '</span>'
+               @staticmethod
+               def escape (s): return cgi.escape (s)
+               @staticmethod
+               def newline (): return '<br/>\n'
+
+       @staticmethod
+       def Auto (argv = [], out = sys.stdout):
+               format = ColorFormatter.ANSI
+               if "--format" in argv:
+                       argv.remove ("--format")
+                       format = ColorFormatter.ANSI
+               if "--format=ansi" in argv:
+                       argv.remove ("--format=ansi")
+                       format = ColorFormatter.ANSI
+               if "--format=html" in argv:
+                       argv.remove ("--format=html")
+                       format = ColorFormatter.HTML
+               if "--no-format" in argv:
+                       argv.remove ("--no-format")
+                       format = ColorFormatter.Null
+               return format
+
+
+class DiffColorizer:
+
+       diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)')
+
+       def __init__ (self, formatter, colors=diff_colors, symbols=diff_symbols):
+               self.formatter = formatter
+               self.colors = colors
+               self.symbols = symbols
+
+       def colorize_lines (self, lines):
+               lines = (l if l else '' for l in lines)
+               ss = [self.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in lines]
+               oo = ["",""]
+               st = [False, False]
+               for l in difflib.Differ().compare (*ss):
+                       if l[0] == '?':
+                               continue
+                       if l[0] == ' ':
+                               for i in range(2):
+                                       if st[i]:
+                                               oo[i] += self.formatter.end_color ()
+                                               st[i] = False
+                               oo = [o + self.formatter.escape (l[2:]) for o in oo]
+                               continue
+                       if l[0] in self.symbols:
+                               i = self.symbols.index (l[0])
+                               if not st[i]:
+                                       oo[i] += self.formatter.start_color (self.colors[i])
+                                       st[i] = True
+                               oo[i] += self.formatter.escape (l[2:])
+                               continue
+               for i in range(2):
+                       if st[i]:
+                               oo[i] += self.formatter.end_color ()
+                               st[i] = False
+               oo = [o.replace ('\n', '') for o in oo]
+               return [s1+s2+self.formatter.newline () for (s1,s2) in zip (self.symbols, oo) if s2]
+
+       def colorize_diff (self, f):
+               lines = [None, None]
+               for l in f:
+                       if l[0] not in self.symbols:
+                               yield self.formatter.escape (l).replace ('\n', self.formatter.newline ())
+                               continue
+                       i = self.symbols.index (l[0])
+                       if lines[i]:
+                               # Flush
+                               for line in self.colorize_lines (lines):
+                                       yield line
+                               lines = [None, None]
+                       lines[i] = l[1:]
+                       if (all (lines)):
+                               # Flush
+                               for line in self.colorize_lines (lines):
+                                       yield line
+                               lines = [None, None]
+               if (any (lines)):
+                       # Flush
+                       for line in self.colorize_lines (lines):
+                               yield line
+
+
+class ZipDiffer:
+
+       @staticmethod
+       def diff_files (files, symbols=diff_symbols):
+               files = tuple (files) # in case it's a generator, copy it
+               try:
+                       for lines in izip_longest (*files):
+                               if all (lines[0] == line for line in lines[1:]):
+                                       sys.stdout.writelines ([" ", lines[0]])
+                                       continue
+
+                               for i, l in enumerate (lines):
+                                       if l:
+                                               sys.stdout.writelines ([symbols[i], l])
+               except IOError as e:
+                       if e.errno != errno.EPIPE:
+                               print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+                               sys.exit (1)
+
+
+class DiffFilters:
+
+       @staticmethod
+       def filter_failures (f):
+               for key, lines in DiffHelpers.separate_test_cases (f):
+                       lines = list (lines)
+                       if not DiffHelpers.test_passed (lines):
+                               for l in lines: yield l
+
+class Stat:
+
+       def __init__ (self):
+               self.count = 0
+               self.freq = 0
+
+       def add (self, test):
+               self.count += 1
+               self.freq += test.freq
+
+class Stats:
+
+       def __init__ (self):
+               self.passed = Stat ()
+               self.failed = Stat ()
+               self.total  = Stat ()
+
+       def add (self, test):
+               self.total.add (test)
+               if test.passed:
+                       self.passed.add (test)
+               else:
+                       self.failed.add (test)
+
+       def mean (self):
+               return float (self.passed.count) / self.total.count
+
+       def variance (self):
+               return (float (self.passed.count) / self.total.count) * \
+                      (float (self.failed.count) / self.total.count)
+
+       def stddev (self):
+               return self.variance () ** .5
+
+       def zscore (self, population):
+               """Calculate the standard score.
+                  Population is the Stats for population.
+                  Self is Stats for sample.
+                  Returns larger absolute value if sample is highly unlikely to be random.
+                  Anything outside of -3..+3 is very unlikely to be random.
+                  See: http://en.wikipedia.org/wiki/Standard_score"""
+
+               return (self.mean () - population.mean ()) / population.stddev ()
+
+
+
+
+class DiffSinks:
+
+       @staticmethod
+       def print_stat (f):
+               passed = 0
+               failed = 0
+               # XXX port to Stats, but that would really slow us down here
+               for key, lines in DiffHelpers.separate_test_cases (f):
+                       if DiffHelpers.test_passed (lines):
+                               passed += 1
+                       else:
+                               failed += 1
+               total = passed + failed
+               print "%d out of %d tests passed.  %d failed (%g%%)" % (passed, total, failed, 100. * failed / total)
+
+       @staticmethod
+       def print_ngrams (f, ns=(1,2,3)):
+               gens = tuple (Ngram.generator (n) for n in ns)
+               allstats = Stats ()
+               allgrams = {}
+               for key, lines in DiffHelpers.separate_test_cases (f):
+                       test = Test (lines)
+                       allstats.add (test)
+
+                       for gen in gens:
+                               for ngram in gen (test.unicodes):
+                                       if ngram not in allgrams:
+                                               allgrams[ngram] = Stats ()
+                                       allgrams[ngram].add (test)
+
+               importantgrams = {}
+               for ngram, stats in allgrams.iteritems ():
+                       if stats.failed.count >= 30: # for statistical reasons
+                               importantgrams[ngram] = stats
+               allgrams = importantgrams
+               del importantgrams
+
+               for ngram, stats in allgrams.iteritems ():
+                       print "zscore: %9f failed: %6d passed: %6d ngram: <%s>" % (stats.zscore (allstats), stats.failed.count, stats.passed.count, ','.join ("U+%04X" % u for u in ngram))
+
+
+
+class Test:
+
+       def __init__ (self, lines):
+               self.freq = 1
+               self.passed = True
+               self.identifier = None
+               self.text = None
+               self.unicodes = None
+               self.glyphs = None
+               for l in lines:
+                       symbol = l[0]
+                       if symbol != ' ':
+                               self.passed = False
+                       i = 1
+                       if ':' in l:
+                               i = l.index (':')
+                               if not self.identifier:
+                                       self.identifier = l[1:i]
+                               i = i + 2 # Skip colon and space
+                       j = -1
+                       if l[j] == '\n':
+                               j -= 1
+                       brackets = l[i] + l[j]
+                       l = l[i+1:-2]
+                       if brackets == '()':
+                               self.text = l
+                       elif brackets == '<>':
+                               self.unicodes = Unicode.parse (l)
+                       elif brackets == '[]':
+                               # XXX we don't handle failed tests here
+                               self.glyphs = l
+
+
+class DiffHelpers:
+
+       @staticmethod
+       def separate_test_cases (f):
+               '''Reads lines from f, and if the lines have identifiers, ie.
+                  have a colon character, groups them by identifier,
+                  yielding lists of all lines with the same identifier.'''
+
+               def identifier (l):
+                       if ':' in l[1:]:
+                               return l[1:l.index (':')]
+                       return l
+               return groupby (f, key=identifier)
+
+       @staticmethod
+       def test_passed (lines):
+               return all (l[0] == ' ' for l in lines)
+
+
+class FilterHelpers:
+
+       @staticmethod
+       def filter_printer_function (filter_callback):
+               def printer (f):
+                       for line in filter_callback (f):
+                               print line
+               return printer
+
+       @staticmethod
+       def filter_printer_function_no_newline (filter_callback):
+               def printer (f):
+                       for line in filter_callback (f):
+                               sys.stdout.writelines ([line])
+               return printer
+
+
+class Ngram:
+
+       @staticmethod
+       def generator (n):
+
+               def gen (f):
+                       l = []
+                       for x in f:
+                               l.append (x)
+                               if len (l) == n:
+                                       yield tuple (l)
+                                       l[:1] = []
+
+               gen.n = n
+               return gen
+
+
+class UtilMains:
+
+       @staticmethod
+       def process_multiple_files (callback, mnemonic = "FILE"):
+
+               if "--help" in sys.argv:
+                       print "Usage: %s %s..." % (sys.argv[0], mnemonic)
+                       sys.exit (1)
+
+               try:
+                       files = sys.argv[1:] if len (sys.argv) > 1 else ['-']
+                       for s in files:
+                               callback (FileHelpers.open_file_or_stdin (s))
+               except IOError as e:
+                       if e.errno != errno.EPIPE:
+                               print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+                               sys.exit (1)
+
+       @staticmethod
+       def process_multiple_args (callback, mnemonic):
+
+               if len (sys.argv) == 1 or "--help" in sys.argv:
+                       print "Usage: %s %s..." % (sys.argv[0], mnemonic)
+                       sys.exit (1)
+
+               try:
+                       for s in sys.argv[1:]:
+                               callback (s)
+               except IOError as e:
+                       if e.errno != errno.EPIPE:
+                               print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+                               sys.exit (1)
+
+       @staticmethod
+       def filter_multiple_strings_or_stdin (callback, mnemonic, \
+                                             separator = " ", \
+                                             concat_separator = False):
+
+               if "--help" in sys.argv:
+                       print "Usage:\n  %s %s...\nor:\n  %s\n\nWhen called with no arguments, input is read from standard input." \
+                             % (sys.argv[0], mnemonic, sys.argv[0])
+                       sys.exit (1)
+
+               try:
+                       if len (sys.argv) == 1:
+                               while (1):
+                                       line = sys.stdin.readline ()
+                                       if not len (line):
+                                               break
+                                       if line[-1] == '\n':
+                                               line = line[:-1]
+                                       print callback (line)
+                       else:
+                               args = sys.argv[1:]
+                               if concat_separator != False:
+                                       args = [concat_separator.join (args)]
+                               print separator.join (callback (x) for x in (args))
+               except IOError as e:
+                       if e.errno != errno.EPIPE:
+                               print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+                               sys.exit (1)
+
+
+class Unicode:
+
+       @staticmethod
+       def decode (s):
+               return '<' + u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8') + '>'
+
+       @staticmethod
+       def parse (s):
+               s = re.sub (r"[<+>,\\uU\n       ]", " ", s)
+               s = re.sub (r"0[xX]", " ", s)
+               return [int (x, 16) for x in s.split (' ') if len (x)]
+
+       @staticmethod
+       def encode (s):
+               return u''.join (unichr (x) for x in Unicode.parse (s)).encode ('utf-8')
+
+       shorthands = {
+               "ZERO WIDTH NON-JOINER": "ZWNJ",
+               "ZERO WIDTH JOINER": "ZWJ",
+               "NARROW NO-BREAK SPACE": "NNBSP",
+               "COMBINING GRAPHEME JOINER": "CGJ",
+               "LEFT-TO-RIGHT MARK": "LRM",
+               "RIGHT-TO-LEFT MARK": "RLM",
+               "LEFT-TO-RIGHT EMBEDDING": "LRE",
+               "RIGHT-TO-LEFT EMBEDDING": "RLE",
+               "POP DIRECTIONAL FORMATTING": "PDF",
+               "LEFT-TO-RIGHT OVERRIDE": "LRO",
+               "RIGHT-TO-LEFT OVERRIDE": "RLO",
+       }
+
+       @staticmethod
+       def pretty_name (u):
+               try:
+                       s = unicodedata.name (u)
+               except ValueError:
+                       return "XXX"
+               s = re.sub (".* LETTER ", "", s)
+               s = re.sub (".* VOWEL SIGN (.*)", r"\1-MATRA", s)
+               s = re.sub (".* SIGN ", "", s)
+               s = re.sub (".* COMBINING ", "", s)
+               if re.match (".* VIRAMA", s):
+                       s = "HALANT"
+               if s in Unicode.shorthands:
+                       s = Unicode.shorthands[s]
+               return s
+
+       @staticmethod
+       def pretty_names (s):
+               s = re.sub (r"[<+>\\uU]", " ", s)
+               s = re.sub (r"0[xX]", " ", s)
+               s = [unichr (int (x, 16)) for x in re.split ('[, \n]', s) if len (x)]
+               return u' + '.join (Unicode.pretty_name (x) for x in s).encode ('utf-8')
+
+
+class FileHelpers:
+
+       @staticmethod
+       def open_file_or_stdin (f):
+               if f == '-':
+                       return sys.stdin
+               return file (f)
+
+
+class Manifest:
+
+       @staticmethod
+       def read (s, strict = True):
+
+               if not os.path.exists (s):
+                       if strict:
+                               print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], s)
+                               sys.exit (1)
+                       return
+
+               s = os.path.normpath (s)
+
+               if os.path.isdir (s):
+
+                       try:
+                               m = file (os.path.join (s, "MANIFEST"))
+                               items = [x.strip () for x in m.readlines ()]
+                               for f in items:
+                                       for p in Manifest.read (os.path.join (s, f)):
+                                               yield p
+                       except IOError:
+                               if strict:
+                                       print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], os.path.join (s, "MANIFEST"))
+                                       sys.exit (1)
+                               return
+               else:
+                       yield s
+
+       @staticmethod
+       def update_recursive (s):
+
+               for dirpath, dirnames, filenames in os.walk (s, followlinks=True):
+
+                       for f in ["MANIFEST", "README", "LICENSE", "COPYING", "AUTHORS", "SOURCES", "ChangeLog"]:
+                               if f in dirnames:
+                                       dirnames.remove (f)
+                               if f in filenames:
+                                       filenames.remove (f)
+                       dirnames.sort ()
+                       filenames.sort ()
+                       ms = os.path.join (dirpath, "MANIFEST")
+                       print "  GEN    %s" % ms
+                       m = open (ms, "w")
+                       for f in filenames:
+                               print >> m, f
+                       for f in dirnames:
+                               print >> m, f
+                       for f in dirnames:
+                               Manifest.update_recursive (os.path.join (dirpath, f))
+
+if __name__ == '__main__':
+       pass
diff --git a/util/Makefile.am b/util/Makefile.am
new file mode 100644 (file)
index 0000000..944b1aa
--- /dev/null
@@ -0,0 +1,74 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+
+bin_PROGRAMS =
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/ \
+       -I$(top_builddir)/src/ \
+       $(GLIB_CFLAGS) \
+       $(FREETYPE_CFLAGS) \
+       $(CAIRO_FT_CFLAGS) \
+       $(NULL)
+LDADD = \
+       $(top_builddir)/src/libharfbuzz.la \
+       -lm \
+       $(GLIB_LIBS) \
+       $(FREETYPE_LIBS) \
+       $(NULL)
+
+if HAVE_GLIB
+if HAVE_FREETYPE
+
+if HAVE_CAIRO_FT
+hb_view_SOURCES = \
+       hb-view.cc \
+       options.cc \
+       options.hh \
+       main-font-text.hh \
+       shape-consumer.hh \
+       ansi-print.cc \
+       ansi-print.hh \
+       helper-cairo.cc \
+       helper-cairo.hh \
+       helper-cairo-ansi.cc \
+       helper-cairo-ansi.hh \
+       view-cairo.cc \
+       view-cairo.hh \
+       $(NULL)
+hb_view_LDADD = \
+       $(LDADD) \
+       $(CAIRO_LIBS) \
+       $(CAIRO_FT_LIBS) \
+       $(NULL)
+bin_PROGRAMS += hb-view
+endif # HAVE_CAIRO_FT
+
+hb_shape_SOURCES = \
+       hb-shape.cc \
+       options.cc \
+       options.hh \
+       main-font-text.hh \
+       shape-consumer.hh \
+       $(NULL)
+bin_PROGRAMS += hb-shape
+
+if HAVE_OT
+hb_ot_shape_closure_SOURCES = \
+       hb-ot-shape-closure.cc \
+       options.cc \
+       options.hh \
+       main-font-text.hh \
+       $(NULL)
+bin_PROGRAMS += hb-ot-shape-closure
+endif # HAVE_OT
+
+endif # HAVE_FREETYPE
+endif # HAVE_GLIB
+
+-include $(top_srcdir)/git.mk
diff --git a/util/ansi-print.cc b/util/ansi-print.cc
new file mode 100644 (file)
index 0000000..873bee8
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Copyright © 2012  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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ansi-print.hh"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for isatty() */
+#endif
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define CELL_W 8
+#define CELL_H (2 * CELL_W)
+
+struct color_diff_t
+{
+  int dot (const color_diff_t &o)
+  { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; }
+
+  int v[4];
+};
+
+struct color_t
+{
+  static color_t from_ansi (unsigned int x)
+  {
+    color_t c = {(0xFF<<24) | ((0xFF*(x&1))<<16) | ((0xFF*((x >> 1)&1))<<8) | (0xFF*((x >> 2)&1))};
+    return c;
+  }
+  unsigned int to_ansi (void)
+  {
+    return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4);
+  }
+
+  color_diff_t diff (const color_t &o)
+  {
+    color_diff_t d;
+    for (unsigned int i = 0; i < 4; i++)
+      d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF);
+    return d;
+  }
+
+  uint32_t v;
+};
+
+struct image_t
+{
+  public:
+
+  image_t (unsigned int width_,
+          unsigned int height_,
+          const uint32_t *data_,
+          unsigned int stride_) :
+               width (width_),
+               height (height_),
+               own_data (false),
+               data ((color_t *) data_),
+               stride (stride_) {}
+  image_t (unsigned int width_,
+          unsigned int height_) :
+               width (width_),
+               height (height_),
+               own_data (true),
+               data ((color_t *) malloc (sizeof (data[0]) * width * height)),
+               stride (width) {}
+  ~image_t (void)
+  { if (own_data) free (data); }
+
+  color_t &operator () (unsigned int x, unsigned int y)
+  { return data[x + y * stride]; }
+
+  color_t operator () (unsigned int x, unsigned int y) const
+  { return data[x + y * stride]; }
+
+  void
+  copy_sub_image (const image_t &s,
+                 unsigned int x, unsigned int y,
+                 unsigned int w, unsigned int h)
+  {
+    assert (x < width);
+    assert (y < height);
+    for (unsigned int row = 0; row < h; row++) {
+      color_t *p = data + x + MIN (y + row, height - 1) * stride;
+      color_t *q = s.data + row * s.stride;
+      if (x + w <= width)
+       for (unsigned int col = 0; col < w; col++)
+         *q++ = *p++;
+      else {
+        unsigned int limit = width - x;
+       for (unsigned int col = 0; col < limit; col++)
+         *q++ = *p++;
+       p--;
+       for (unsigned int col = limit; col < w; col++)
+         *q++ = *p;
+      }
+    }
+  }
+
+  const unsigned int width;
+  const unsigned int height;
+
+  private:
+  bool own_data;
+  color_t * const data;
+  const unsigned int stride;
+};
+
+struct biimage_t
+{
+  public:
+
+  biimage_t (unsigned int width, unsigned int height) :
+               width (width),
+               height (height),
+               data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {}
+  ~biimage_t (void)
+  { free (data); }
+
+  void set (const image_t &image)
+  {
+    assert (image.width == width);
+    assert (image.height == height);
+    int freq[8] = {0};
+    for (unsigned int y = 0; y < height; y++)
+      for (unsigned int x = 0; x < width; x++) {
+        color_t c = image (x, y);
+        freq[c.to_ansi ()]++;
+      }
+    bg = 0;
+    for (unsigned int i = 1; i < 8; i++)
+      if (freq[bg] < freq[i])
+        bg = i;
+    fg = 0;
+    for (unsigned int i = 1; i < 8; i++)
+      if (i != bg && freq[fg] < freq[i])
+        fg = i;
+    if (fg == bg || freq[fg] == 0) {
+      fg = bg;
+      unicolor = true;
+    }
+    else
+      unicolor = false;
+
+    /* Set the data... */
+
+    if (unicolor) {
+      memset (data, 0, sizeof (data[0]) * width * height);
+      return;
+    }
+
+    color_t bgc = color_t::from_ansi (bg);
+    color_t fgc = color_t::from_ansi (fg);
+    color_diff_t diff = fgc.diff (bgc);
+    int dd = diff.dot (diff);
+    for (unsigned int y = 0; y < height; y++)
+      for (unsigned int x = 0; x < width; x++) {
+        int d = diff.dot (image (x, y).diff (bgc));
+       (*this)(x, y) = d < 0 ? 0 : d > dd ? 255 : lround (d * 255. / dd);
+      }
+  }
+
+  uint8_t &operator () (unsigned int x, unsigned int y)
+  { return data[x + y * width]; }
+
+  uint8_t operator () (unsigned int x, unsigned int y) const
+  { return data[x + y * width]; }
+
+  const unsigned int width;
+  const unsigned int height;
+  unsigned int bg;
+  unsigned int fg;
+  bool unicolor;
+
+  private:
+  uint8_t * const data;
+};
+
+const char *
+block_best (const biimage_t &bi, unsigned int *score, bool *inverse)
+{
+  assert (bi.width  <= CELL_W);
+  assert (bi.height <= CELL_H);
+
+  unsigned int row_sum[CELL_H] = {0};
+  unsigned int col_sum[CELL_W] = {0};
+  unsigned int row_sum_i[CELL_H] = {0};
+  unsigned int col_sum_i[CELL_W] = {0};
+  unsigned int quad[2][2] = {{0}};
+  unsigned int quad_i[2][2] = {{0}};
+  unsigned int total = 0;
+  unsigned int total_i = 0;
+  for (unsigned int y = 0; y < bi.height; y++)
+    for (unsigned int x = 0; x < bi.width; x++) {
+      unsigned int c = bi (x, y);
+      unsigned int c_i = 255 - c;
+      row_sum[y] += c;
+      row_sum_i[y] += c_i;
+      col_sum[x] += c;
+      col_sum_i[x] += c_i;
+      quad[2 * y / bi.height][2 * x / bi.width] += c;
+      quad_i[2 * y / bi.height][2 * x / bi.width] += c_i;
+      total += c;
+      total_i += c_i;
+    }
+
+  /* Make the sums cummulative */
+  for (unsigned int i = 1; i < bi.height; i++) {
+    row_sum[i] += row_sum[i - 1];
+    row_sum_i[i] += row_sum_i[i - 1];
+  }
+  for (unsigned int i = 1; i < bi.width;  i++) {
+    col_sum[i] += col_sum[i - 1];
+    col_sum_i[i] += col_sum_i[i - 1];
+  }
+
+  const char *best_c = " ";
+
+  /* Maybe empty is better! */
+  if (total < *score) {
+    *score = total;
+    *inverse = false;
+    best_c = " ";
+  }
+  /* Maybe full is better! */
+  if (total_i < *score) {
+    *score = total_i;
+    *inverse = true;
+    best_c = " ";
+  }
+
+  /* Find best lower line */
+  if (1) {
+    unsigned int best_s = (unsigned int) -1;
+    bool best_inv = false;
+    int best_i = 0;
+    for (unsigned int i = 0; i < bi.height - 1; i++)
+    {
+      unsigned int s;
+      s = row_sum[i] + total_i - row_sum_i[i];
+      if (s < best_s) {
+        best_s = s;
+       best_i = i;
+       best_inv = false;
+      }
+      s = row_sum_i[i] + total - row_sum[i];
+      if (s < best_s) {
+        best_s = s;
+       best_i = i;
+       best_inv = true;
+      }
+    }
+    if (best_s < *score) {
+      static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"};
+      unsigned int which = lround (((best_i + 1) * 8) / bi.height);
+      if (1 <= which && which <= 7) {
+       *score = best_s;
+       *inverse = best_inv;
+       best_c = lower[7 - which];
+      }
+    }
+  }
+
+  /* Find best left line */
+  if (1) {
+    unsigned int best_s = (unsigned int) -1;
+    bool best_inv = false;
+    int best_i = 0;
+    for (unsigned int i = 0; i < bi.width - 1; i++)
+    {
+      unsigned int s;
+      s = col_sum[i] + total_i - col_sum_i[i];
+      if (s < best_s) {
+        best_s = s;
+       best_i = i;
+       best_inv = true;
+      }
+      s = col_sum_i[i] + total - col_sum[i];
+      if (s < best_s) {
+        best_s = s;
+       best_i = i;
+       best_inv = false;
+      }
+    }
+    if (best_s < *score) {
+      static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"};
+      unsigned int which = lround (((best_i + 1) * 8) / bi.width);
+      if (1 <= which && which <= 7) {
+       *score = best_s;
+       *inverse = best_inv;
+       best_c = left[which - 1];
+      }
+    }
+  }
+
+  /* Find best quadrant */
+  if (1) {
+    unsigned int q = 0;
+    unsigned int qs = 0;
+    for (unsigned int i = 0; i < 2; i++)
+      for (unsigned int j = 0; j < 2; j++)
+       if (quad[i][j] > quad_i[i][j]) {
+         q += 1 << (2 * i + j);
+         qs += quad_i[i][j];
+       } else
+         qs += quad[i][j];
+    if (qs < *score) {
+      const char *c = NULL;
+      bool inv = false;
+      switch (q) {
+       case 1:  c = "▟"; inv = true;  break;
+       case 2:  c = "▙"; inv = true;  break;
+       case 4:  c = "▖"; inv = false; break;
+       case 8:  c = "▗"; inv = false; break;
+       case 9:  c = "▚"; inv = false; break;
+       case 6:  c = "▞"; inv = false; break;
+       case 7:  c = "▜"; inv = true;  break;
+       case 11: c = "▜"; inv = true;  break;
+       case 13: c = "▙"; inv = true;  break;
+       case 14: c = "▟"; inv = true;  break;
+      }
+      if (c) {
+       *score = qs;
+       *inverse = inv;
+       best_c = c;
+      }
+    }
+  }
+
+  return best_c;
+}
+
+void
+ansi_print_image_rgb24 (const uint32_t *data,
+                       unsigned int width,
+                       unsigned int height,
+                       unsigned int stride)
+{
+  image_t image (width, height, data, stride);
+
+  unsigned int rows = (height + CELL_H - 1) / CELL_H;
+  unsigned int cols = (width +  CELL_W - 1) / CELL_W;
+  image_t cell (CELL_W, CELL_H);
+  biimage_t bi (CELL_W, CELL_H);
+  unsigned int last_bg = -1, last_fg = -1;
+  for (unsigned int row = 0; row < rows; row++) {
+    for (unsigned int col = 0; col < cols; col++) {
+      image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H);
+      bi.set (cell);
+      if (bi.unicolor) {
+        if (last_bg != bi.bg) {
+         printf ("\e[%dm", 40 + bi.bg);
+         last_bg = bi.bg;
+       }
+       printf (" ");
+      } else {
+        /* Figure out the closest character to the biimage */
+        unsigned int score = (unsigned int) -1;
+       bool inverse = false;
+        const char *c = block_best (bi, &score, &inverse);
+       if (inverse) {
+         if (last_bg != bi.fg || last_fg != bi.bg) {
+           printf ("\e[%d;%dm", 30 + bi.bg, 40 + bi.fg);
+           last_bg = bi.fg;
+           last_fg = bi.bg;
+         }
+       } else {
+         if (last_bg != bi.bg || last_fg != bi.fg) {
+           printf ("\e[%d;%dm", 40 + bi.bg, 30 + bi.fg);
+           last_bg = bi.bg;
+           last_fg = bi.fg;
+         }
+       }
+       printf ("%s", c);
+      }
+    }
+    printf ("\e[0m\n"); /* Reset */
+    last_bg = last_fg = -1;
+  }
+}
diff --git a/util/ansi-print.hh b/util/ansi-print.hh
new file mode 100644 (file)
index 0000000..dad4d4c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2012  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 ANSI_PRINT_HH
+#define ANSI_PRINT_HH
+
+#include <hb.h> /* for int types */
+
+void
+ansi_print_image_rgb24 (const uint32_t *data,
+                       unsigned int width,
+                       unsigned int height,
+                       unsigned int stride);
+
+
+#endif
diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc
new file mode 100644 (file)
index 0000000..6dce7a1
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2012  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 "main-font-text.hh"
+
+#ifdef HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
+
+struct shape_closure_consumer_t : option_group_t
+{
+  shape_closure_consumer_t (option_parser_t *parser) :
+                           shaper (parser),
+                           show_glyph_names (true)
+  {
+    add_options (parser);
+  }
+
+  void add_options (struct option_parser_t *parser)
+  {
+    GOptionEntry entries[] =
+    {
+      {"no-glyph-names",       0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,    &this->show_glyph_names,        "Use glyph indices instead of names",   NULL},
+      {NULL}
+    };
+    parser->add_group (entries,
+                      "format",
+                      "Format options:",
+                      "Options controlling output formatting",
+                      this);
+  }
+
+  void init (const font_options_t *font_opts)
+  {
+    glyphs = hb_set_create ();
+    font = hb_font_reference (font_opts->get_font ());
+    failed = false;
+  }
+  void consume_line (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len)
+  {
+    hb_set_clear (glyphs);
+    shaper.shape_closure (text, text_len, font, buffer, glyphs);
+
+    if (hb_set_empty (glyphs))
+      return;
+
+    /* Print it out! */
+    bool first = true;
+    for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);)
+      if (hb_set_has (glyphs, i)) {
+        if (first)
+         first = false;
+       else
+         printf (" ");
+       char glyph_name[32];
+       if (show_glyph_names) {
+         hb_font_get_glyph_name (font, i, glyph_name, sizeof (glyph_name));
+         printf ("%s", glyph_name);
+       } else
+         printf ("%u", i);
+      }
+  }
+  void finish (const font_options_t *font_opts)
+  {
+    printf ("\n");
+    hb_font_destroy (font);
+    font = NULL;
+    hb_set_destroy (glyphs);
+    glyphs = NULL;
+  }
+
+  bool failed;
+
+  protected:
+  shape_options_t shaper;
+  hb_bool_t show_glyph_names;
+
+  hb_set_t *glyphs;
+  hb_font_t *font;
+};
+
+int
+main (int argc, char **argv)
+{
+  main_font_text_t<shape_closure_consumer_t> driver;
+  return driver.main (argc, argv);
+}
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
new file mode 100644 (file)
index 0000000..b23519b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2010  Behdad Esfahbod
+ * Copyright © 2011,2012  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 "main-font-text.hh"
+#include "shape-consumer.hh"
+
+struct output_buffer_t
+{
+  output_buffer_t (option_parser_t *parser)
+                 : options (parser),
+                   format (parser) {}
+
+  void init (const font_options_t *font_opts)
+  {
+    options.get_file_handle ();
+    gs = g_string_new (NULL);
+    line_no = 0;
+    font = hb_font_reference (font_opts->get_font ());
+  }
+  void new_line (void)
+  {
+    line_no++;
+  }
+  void consume_text (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len,
+                    hb_bool_t     utf8_clusters)
+  {
+    g_string_set_size (gs, 0);
+    format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+    fprintf (options.fp, "%s", gs->str);
+  }
+  void shape_failed (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len,
+                    hb_bool_t     utf8_clusters)
+  {
+    g_string_set_size (gs, 0);
+    format.serialize_message (line_no, "msg: all shapers failed", gs);
+    fprintf (options.fp, "%s", gs->str);
+  }
+  void consume_glyphs (hb_buffer_t  *buffer,
+                      const char   *text,
+                      unsigned int  text_len,
+                      hb_bool_t     utf8_clusters)
+  {
+    g_string_set_size (gs, 0);
+    format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+    fprintf (options.fp, "%s", gs->str);
+  }
+  void finish (const font_options_t *font_opts)
+  {
+    hb_font_destroy (font);
+    g_string_free (gs, true);
+    gs = NULL;
+    font = NULL;
+  }
+
+  protected:
+  output_options_t options;
+  format_options_t format;
+
+  GString *gs;
+  unsigned int line_no;
+  hb_font_t *font;
+};
+
+int
+main (int argc, char **argv)
+{
+  main_font_text_t<shape_consumer_t<output_buffer_t> > driver;
+  return driver.main (argc, argv);
+}
diff --git a/util/hb-view.cc b/util/hb-view.cc
new file mode 100644 (file)
index 0000000..26fad66
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2010  Behdad Esfahbod
+ * Copyright © 2011,2012  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 "main-font-text.hh"
+#include "shape-consumer.hh"
+#include "view-cairo.hh"
+
+int
+main (int argc, char **argv)
+{
+  main_font_text_t<shape_consumer_t<view_cairo_t> > driver;
+  return driver.main (argc, argv);
+}
diff --git a/util/helper-cairo-ansi.cc b/util/helper-cairo-ansi.cc
new file mode 100644 (file)
index 0000000..f7c0660
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2012  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 "helper-cairo-ansi.hh"
+#include "options.hh"
+
+#include "ansi-print.hh"
+
+
+cairo_status_t
+helper_cairo_surface_write_to_ansi_stream (cairo_surface_t     *surface,
+                                          cairo_write_func_t   write_func,
+                                          void                 *closure)
+{
+  unsigned int width = cairo_image_surface_get_width (surface);
+  unsigned int height = cairo_image_surface_get_height (surface);
+  if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
+    cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+    cairo_t *cr = cairo_create (new_surface);
+    if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) {
+      cairo_set_source_rgb (cr, 0., 0., 0.);
+      cairo_paint (cr);
+      cairo_set_source_rgb (cr, 1., 1., 1.);
+      cairo_mask_surface (cr, surface, 0, 0);
+    } else {
+      cairo_set_source_rgb (cr, 1., 1., 1.);
+      cairo_paint (cr);
+      cairo_set_source_surface (cr, surface, 0, 0);
+      cairo_paint (cr);
+    }
+    cairo_destroy (cr);
+    surface = new_surface;
+  } else
+    cairo_surface_reference (surface);
+
+  unsigned int stride = cairo_image_surface_get_stride (surface);
+  const uint32_t *data = (uint32_t *) cairo_image_surface_get_data (surface);
+
+  /* We don't have rows to spare on the terminal window...
+   * Find the tight image top/bottom and only print in between. */
+
+  /* Use corner color as background color. */
+  uint32_t bg_color = * (uint32_t *) data;
+
+  /* Drop first row while empty */
+  while (height) 
+  {
+    unsigned int i;
+    for (i = 0; i < width; i++)
+      if (data[i] != bg_color)
+        break;
+    if (i < width)
+      break;
+    data += stride / 4;
+    height--;
+  }
+
+  /* Drop last row while empty */
+  unsigned int orig_height = height;
+  while (height)
+  {
+    const uint32_t *row = data + (height - 1) * stride / 4;
+    unsigned int i;
+    for (i = 0; i < width; i++)
+      if (row[i] != bg_color)
+        break;
+    if (i < width)
+      break;
+    height--;
+  }
+  if (height < orig_height)
+    height++; /* Add one last blank row for padding. */
+
+  if (width && height)
+    ansi_print_image_rgb24 (data, width, height, stride / 4);
+
+  cairo_surface_destroy (surface);
+  return CAIRO_STATUS_SUCCESS;
+}
diff --git a/util/helper-cairo-ansi.hh b/util/helper-cairo-ansi.hh
new file mode 100644 (file)
index 0000000..eeaaa50
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2012  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 <cairo.h>
+
+#ifndef HELPER_CAIRO_ANSI_HH
+#define HELPER_CAIRO_ANSI_HH
+
+
+cairo_status_t
+helper_cairo_surface_write_to_ansi_stream (cairo_surface_t     *surface,
+                                          cairo_write_func_t   write_func,
+                                          void                 *closure);
+
+
+#endif
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
new file mode 100644 (file)
index 0000000..0cdfd63
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * Copyright © 2011  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 "helper-cairo.hh"
+
+#include <cairo-ft.h>
+#include <hb-ft.h>
+
+#include "helper-cairo-ansi.hh"
+#ifdef CAIRO_HAS_SVG_SURFACE
+#  include <cairo-svg.h>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+#  include <cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#  include <cairo-ps.h>
+#  if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
+#    define HAS_EPS 1
+
+static cairo_surface_t *
+_cairo_eps_surface_create_for_stream (cairo_write_func_t  write_func,
+                                     void               *closure,
+                                     double              width,
+                                     double              height)
+{
+  cairo_surface_t *surface;
+
+  surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
+  cairo_ps_surface_set_eps (surface, true);
+
+  return surface;
+}
+
+#  else
+#    undef HAS_EPS
+#  endif
+#endif
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+                                double font_size)
+{
+  hb_font_t *font = hb_font_reference (font_opts->get_font ());
+
+  cairo_font_face_t *cairo_face;
+  FT_Face ft_face = hb_ft_font_get_face (font);
+  if (!ft_face)
+    /* This allows us to get some boxes at least... */
+    cairo_face = cairo_toy_font_face_create ("@cairo:sans",
+                                            CAIRO_FONT_SLANT_NORMAL,
+                                            CAIRO_FONT_WEIGHT_NORMAL);
+  else
+    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+  cairo_matrix_t ctm, font_matrix;
+  cairo_font_options_t *font_options;
+
+  cairo_matrix_init_identity (&ctm);
+  cairo_matrix_init_scale (&font_matrix,
+                          font_size, font_size);
+  font_options = cairo_font_options_create ();
+  cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+  cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+
+  cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
+                                                              &font_matrix,
+                                                              &ctm,
+                                                              font_options);
+
+  cairo_font_options_destroy (font_options);
+  cairo_font_face_destroy (cairo_face);
+
+  static cairo_user_data_key_t key;
+  if (cairo_scaled_font_set_user_data (scaled_font,
+                                      &key,
+                                      (void *) font,
+                                      (cairo_destroy_func_t) hb_font_destroy))
+    hb_font_destroy (font);
+
+  return scaled_font;
+}
+
+
+struct finalize_closure_t {
+  void (*callback)(finalize_closure_t *);
+  cairo_surface_t *surface;
+  cairo_write_func_t write_func;
+  void *closure;
+};
+static cairo_user_data_key_t finalize_closure_key;
+
+
+static void
+finalize_ansi (finalize_closure_t *closure)
+{
+  cairo_status_t status;
+  status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
+                                                     closure->write_func,
+                                                     closure->closure);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (false, "Failed to write output: %s",
+         cairo_status_to_string (status));
+}
+
+static cairo_surface_t *
+_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
+                                      void *closure,
+                                      double width,
+                                      double height,
+                                      cairo_content_t content)
+{
+  cairo_surface_t *surface;
+  int w = ceil (width);
+  int h = ceil (height);
+
+  switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
+      break;
+    default:
+    case CAIRO_CONTENT_COLOR:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+      break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+      break;
+  }
+  cairo_status_t status = cairo_surface_status (surface);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (false, "Failed to create cairo surface: %s",
+         cairo_status_to_string (status));
+
+  finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
+  ansi_closure->callback = finalize_ansi;
+  ansi_closure->surface = surface;
+  ansi_closure->write_func = write_func;
+  ansi_closure->closure = closure;
+
+  if (cairo_surface_set_user_data (surface,
+                                  &finalize_closure_key,
+                                  (void *) ansi_closure,
+                                  (cairo_destroy_func_t) g_free))
+    g_free ((void *) closure);
+
+  return surface;
+}
+
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+
+static void
+finalize_png (finalize_closure_t *closure)
+{
+  cairo_status_t status;
+  status = cairo_surface_write_to_png_stream (closure->surface,
+                                             closure->write_func,
+                                             closure->closure);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (false, "Failed to write output: %s",
+         cairo_status_to_string (status));
+}
+
+static cairo_surface_t *
+_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
+                                     void *closure,
+                                     double width,
+                                     double height,
+                                     cairo_content_t content)
+{
+  cairo_surface_t *surface;
+  int w = ceil (width);
+  int h = ceil (height);
+
+  switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
+      break;
+    default:
+    case CAIRO_CONTENT_COLOR:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+      break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+      break;
+  }
+  cairo_status_t status = cairo_surface_status (surface);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (false, "Failed to create cairo surface: %s",
+         cairo_status_to_string (status));
+
+  finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
+  png_closure->callback = finalize_png;
+  png_closure->surface = surface;
+  png_closure->write_func = write_func;
+  png_closure->closure = closure;
+
+  if (cairo_surface_set_user_data (surface,
+                                  &finalize_closure_key,
+                                  (void *) png_closure,
+                                  (cairo_destroy_func_t) g_free))
+    g_free ((void *) closure);
+
+  return surface;
+}
+
+#endif
+
+static cairo_status_t
+stdio_write_func (void                *closure,
+                 const unsigned char *data,
+                 unsigned int         size)
+{
+  FILE *fp = (FILE *) closure;
+
+  while (size) {
+    size_t ret = fwrite (data, 1, size, fp);
+    size -= ret;
+    data += ret;
+    if (size && ferror (fp))
+      fail (false, "Failed to write output: %s", strerror (errno));
+  }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+                            view_options_t *view_opts,
+                            output_options_t *out_opts)
+{
+  cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
+                                  void *closure,
+                                  double width,
+                                  double height) = NULL;
+  cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
+                                   void *closure,
+                                   double width,
+                                   double height,
+                                   cairo_content_t content) = NULL;
+
+  const char *extension = out_opts->output_format;
+  if (!extension) {
+#if HAVE_ISATTY
+    if (isatty (fileno (out_opts->get_file_handle ())))
+      extension = "ansi";
+    else
+#endif
+      extension = "png";
+  }
+  if (0)
+    ;
+    else if (0 == strcasecmp (extension, "ansi"))
+      constructor2 = _cairo_ansi_surface_create_for_stream;
+  #ifdef CAIRO_HAS_PNG_FUNCTIONS
+    else if (0 == strcasecmp (extension, "png"))
+      constructor2 = _cairo_png_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_SVG_SURFACE
+    else if (0 == strcasecmp (extension, "svg"))
+      constructor = cairo_svg_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_PDF_SURFACE
+    else if (0 == strcasecmp (extension, "pdf"))
+      constructor = cairo_pdf_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_PS_SURFACE
+    else if (0 == strcasecmp (extension, "ps"))
+      constructor = cairo_ps_surface_create_for_stream;
+   #ifdef HAS_EPS
+    else if (0 == strcasecmp (extension, "eps"))
+      constructor = _cairo_eps_surface_create_for_stream;
+   #endif
+  #endif
+
+
+  unsigned int fr, fg, fb, fa, br, bg, bb, ba;
+  br = bg = bb = 0; ba = 255;
+  sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
+  fr = fg = fb = 0; fa = 255;
+  sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
+
+  cairo_content_t content;
+  if (!view_opts->annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
+    content = CAIRO_CONTENT_ALPHA;
+  else if (ba == 255)
+    content = CAIRO_CONTENT_COLOR;
+  else
+    content = CAIRO_CONTENT_COLOR_ALPHA;
+
+  cairo_surface_t *surface;
+  FILE *f = out_opts->get_file_handle ();
+  if (constructor)
+    surface = constructor (stdio_write_func, f, w, h);
+  else if (constructor2)
+    surface = constructor2 (stdio_write_func, f, w, h, content);
+  else
+    fail (false, "Unknown output format `%s'", extension);
+
+  cairo_t *cr = cairo_create (surface);
+  content = cairo_surface_get_content (surface);
+
+  switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
+      cairo_paint (cr);
+      cairo_set_source_rgba (cr, 1., 1., 1.,
+                            (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
+      break;
+    default:
+    case CAIRO_CONTENT_COLOR:
+    case CAIRO_CONTENT_COLOR_ALPHA:
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
+      cairo_paint (cr);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
+      break;
+  }
+
+  cairo_surface_destroy (surface);
+  return cr;
+}
+
+void
+helper_cairo_destroy_context (cairo_t *cr)
+{
+  finalize_closure_t *closure = (finalize_closure_t *)
+                               cairo_surface_get_user_data (cairo_get_target (cr),
+                                                            &finalize_closure_key);
+  if (closure)
+    closure->callback (closure);
+
+  cairo_status_t status = cairo_status (cr);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (false, "Failed: %s",
+         cairo_status_to_string (status));
+  cairo_destroy (cr);
+}
+
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+                              hb_buffer_t         *buffer,
+                              const char          *text,
+                              unsigned int         text_len,
+                              double               scale,
+                              hb_bool_t            utf8_clusters)
+{
+  memset (l, 0, sizeof (*l));
+
+  l->num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
+  l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
+
+  if (text) {
+    l->utf8 = g_strndup (text, text_len);
+    l->utf8_len = text_len;
+    l->num_clusters = l->num_glyphs ? 1 : 0;
+    for (unsigned int i = 1; i < l->num_glyphs; i++)
+      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+       l->num_clusters++;
+    l->clusters = cairo_text_cluster_allocate (l->num_clusters);
+  }
+
+  if ((l->num_glyphs && !l->glyphs) ||
+      (l->utf8_len && !l->utf8) ||
+      (l->num_clusters && !l->clusters))
+  {
+    l->finish ();
+    return;
+  }
+
+  hb_position_t x = 0, y = 0;
+  int i;
+  for (i = 0; i < (int) l->num_glyphs; i++)
+  {
+    l->glyphs[i].index = hb_glyph[i].codepoint;
+    l->glyphs[i].x = ( hb_position->x_offset + x) * scale;
+    l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
+    x +=  hb_position->x_advance;
+    y += -hb_position->y_advance;
+
+    hb_position++;
+  }
+  l->glyphs[i].index = -1;
+  l->glyphs[i].x = x * scale;
+  l->glyphs[i].y = y * scale;
+
+  if (l->num_clusters) {
+    memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
+    hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
+    l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
+    unsigned int cluster = 0;
+    const char *start = l->utf8, *end = start;
+    l->clusters[cluster].num_glyphs++;
+    if (backward) {
+      for (i = l->num_glyphs - 2; i >= 0; i--) {
+       if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
+         g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
+         if (utf8_clusters)
+           end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
+         else
+           end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster);
+         l->clusters[cluster].num_bytes = end - start;
+         start = end;
+         cluster++;
+       }
+       l->clusters[cluster].num_glyphs++;
+      }
+      l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
+    } else {
+      for (i = 1; i < (int) l->num_glyphs; i++) {
+       if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
+         g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
+         if (utf8_clusters)
+           end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
+         else
+           end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster);
+         l->clusters[cluster].num_bytes = end - start;
+         start = end;
+         cluster++;
+       }
+       l->clusters[cluster].num_glyphs++;
+      }
+      l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
+    }
+  }
+}
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
new file mode 100644 (file)
index 0000000..2f2c9d4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2011  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 "options.hh"
+
+#include <cairo.h>
+
+#ifndef HELPER_CAIRO_HH
+#define HELPER_CAIRO_HH
+
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+                                double font_size);
+
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+                            view_options_t *view_opts,
+                            output_options_t *out_opts);
+
+void
+helper_cairo_destroy_context (cairo_t *cr);
+
+
+struct helper_cairo_line_t {
+  cairo_glyph_t *glyphs;
+  unsigned int num_glyphs;
+  char *utf8;
+  unsigned int utf8_len;
+  cairo_text_cluster_t *clusters;
+  unsigned int num_clusters;
+  cairo_text_cluster_flags_t cluster_flags;
+
+  void finish (void) {
+    if (glyphs)
+      cairo_glyph_free (glyphs);
+    if (clusters)
+      cairo_text_cluster_free (clusters);
+    if (utf8)
+      g_free (utf8);
+  }
+
+  void get_advance (double *x_advance, double *y_advance) {
+    *x_advance = glyphs[num_glyphs].x;
+    *y_advance = glyphs[num_glyphs].y;
+  }
+};
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+                              hb_buffer_t         *buffer,
+                              const char          *text,
+                              unsigned int         text_len,
+                              double               scale,
+                              hb_bool_t            utf8_clusters);
+
+#endif
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
new file mode 100644 (file)
index 0000000..44e3bfb
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2011,2012  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 "options.hh"
+
+#ifndef HB_MAIN_FONT_TEXT_HH
+#define HB_MAIN_FONT_TEXT_HH
+
+/* main() body for utilities taking font and processing text.*/
+
+template <typename consumer_t>
+struct main_font_text_t
+{
+  main_font_text_t (void)
+                 : options ("[FONT-FILE] [TEXT]"),
+                   font_opts (&options),
+                   input (&options),
+                   consumer (&options) {}
+
+  int
+  main (int argc, char **argv)
+  {
+    options.parse (&argc, &argv);
+
+    argc--, argv++;
+    if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++;
+    if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++;
+    if (argc)
+      fail (true, "Too many arguments on the command line");
+    if (!font_opts.font_file)
+      options.usage ();
+    if (!input.text && !input.text_file)
+      input.text_file = "-";
+
+    consumer.init (&font_opts);
+
+    hb_buffer_t *buffer = hb_buffer_create ();
+    unsigned int text_len;
+    const char *text;
+    while ((text = input.get_line (&text_len)))
+      consumer.consume_line (buffer, text, text_len);
+    hb_buffer_destroy (buffer);
+
+    consumer.finish (&font_opts);
+
+    return consumer.failed ? 1 : 0;
+  }
+
+  protected:
+  option_parser_t options;
+  font_options_t font_opts;
+  text_options_t input;
+  consumer_t consumer;
+};
+
+#endif
+
diff --git a/util/options.cc b/util/options.cc
new file mode 100644 (file)
index 0000000..db1b244
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * Copyright © 2011,2012  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 "options.hh"
+
+#ifdef HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
+
+
+void
+fail (hb_bool_t suggest_help, const char *format, ...)
+{
+  const char *msg;
+
+  va_list vap;
+  va_start (vap, format);
+  msg = g_strdup_vprintf (format, vap);
+  const char *prgname = g_get_prgname ();
+  g_printerr ("%s: %s\n", prgname, msg);
+  if (suggest_help)
+    g_printerr ("Try `%s --help' for more information.\n", prgname);
+
+  exit (1);
+}
+
+
+hb_bool_t debug = false;
+
+static gchar *
+shapers_to_string (void)
+{
+  GString *shapers = g_string_new (NULL);
+  const char **shaper_list = hb_shape_list_shapers ();
+
+  for (; *shaper_list; shaper_list++) {
+    g_string_append (shapers, *shaper_list);
+    g_string_append_c (shapers, ',');
+  }
+  g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
+
+  return g_string_free (shapers, false);
+}
+
+static G_GNUC_NORETURN gboolean
+show_version (const char *name G_GNUC_UNUSED,
+             const char *arg G_GNUC_UNUSED,
+             gpointer    data G_GNUC_UNUSED,
+             GError    **error G_GNUC_UNUSED)
+{
+  g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
+
+  char *shapers = shapers_to_string ();
+  g_printf ("Available shapers: %s\n", shapers);
+  g_free (shapers);
+  if (strcmp (HB_VERSION_STRING, hb_version_string ()))
+    g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
+
+  exit(0);
+}
+
+
+void
+option_parser_t::add_main_options (void)
+{
+  GOptionEntry entries[] =
+  {
+    {"version",                0, G_OPTION_FLAG_NO_ARG,
+                             G_OPTION_ARG_CALLBACK,    (gpointer) &show_version,       "Show version numbers",                 NULL},
+    {"debug",          0, 0, G_OPTION_ARG_NONE,        &debug,                         "Free all resources before exit",       NULL},
+    {NULL}
+  };
+  g_option_context_add_main_entries (context, entries, NULL);
+}
+
+static gboolean
+pre_parse (GOptionContext *context G_GNUC_UNUSED,
+          GOptionGroup *group G_GNUC_UNUSED,
+          gpointer data,
+          GError **error)
+{
+  option_group_t *option_group = (option_group_t *) data;
+  option_group->pre_parse (error);
+  return *error == NULL;
+}
+
+static gboolean
+post_parse (GOptionContext *context G_GNUC_UNUSED,
+           GOptionGroup *group G_GNUC_UNUSED,
+           gpointer data,
+           GError **error)
+{
+  option_group_t *option_group = static_cast<option_group_t *>(data);
+  option_group->post_parse (error);
+  return *error == NULL;
+}
+
+void
+option_parser_t::add_group (GOptionEntry   *entries,
+                           const gchar    *name,
+                           const gchar    *description,
+                           const gchar    *help_description,
+                           option_group_t *option_group)
+{
+  GOptionGroup *group = g_option_group_new (name, description, help_description,
+                                           static_cast<gpointer>(option_group), NULL);
+  g_option_group_add_entries (group, entries);
+  g_option_group_set_parse_hooks (group, pre_parse, post_parse);
+  g_option_context_add_group (context, group);
+}
+
+void
+option_parser_t::parse (int *argc, char ***argv)
+{
+  setlocale (LC_ALL, "");
+
+  GError *parse_error = NULL;
+  if (!g_option_context_parse (context, argc, argv, &parse_error))
+  {
+    if (parse_error != NULL) {
+      fail (true, "%s", parse_error->message);
+      //g_error_free (parse_error);
+    } else
+      fail (true, "Option parse error");
+  }
+}
+
+
+static gboolean
+parse_margin (const char *name G_GNUC_UNUSED,
+             const char *arg,
+             gpointer    data,
+             GError    **error G_GNUC_UNUSED)
+{
+  view_options_t *view_opts = (view_options_t *) data;
+  view_options_t::margin_t &m = view_opts->margin;
+  switch (sscanf (arg, "%lf %lf %lf %lf", &m.t, &m.r, &m.b, &m.l)) {
+    case 1: m.r = m.t;
+    case 2: m.b = m.t;
+    case 3: m.l = m.r;
+    case 4: return true;
+    default:
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                  "%s argument should be one to four space-separated numbers",
+                  name);
+      return false;
+  }
+}
+
+
+static gboolean
+parse_shapers (const char *name G_GNUC_UNUSED,
+              const char *arg,
+              gpointer    data,
+              GError    **error G_GNUC_UNUSED)
+{
+  shape_options_t *shape_opts = (shape_options_t *) data;
+  g_free (shape_opts->shapers);
+  shape_opts->shapers = g_strsplit (arg, ",", 0);
+  return true;
+}
+
+static G_GNUC_NORETURN gboolean
+list_shapers (const char *name G_GNUC_UNUSED,
+             const char *arg G_GNUC_UNUSED,
+             gpointer    data G_GNUC_UNUSED,
+             GError    **error G_GNUC_UNUSED)
+{
+  for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
+    g_printf ("%s\n", *shaper);
+
+  exit(0);
+}
+
+
+
+static void
+parse_space (char **pp)
+{
+  char c;
+#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
+  while (c = **pp, ISSPACE (c))
+    (*pp)++;
+#undef ISSPACE
+}
+
+static hb_bool_t
+parse_char (char **pp, char c)
+{
+  parse_space (pp);
+
+  if (**pp != c)
+    return false;
+
+  (*pp)++;
+  return true;
+}
+
+static hb_bool_t
+parse_uint (char **pp, unsigned int *pv)
+{
+  char *p = *pp;
+  unsigned int v;
+
+  v = strtol (p, pp, 0);
+
+  if (p == *pp)
+    return false;
+
+  *pv = v;
+  return true;
+}
+
+
+static hb_bool_t
+parse_feature_value_prefix (char **pp, hb_feature_t *feature)
+{
+  if (parse_char (pp, '-'))
+    feature->value = 0;
+  else {
+    parse_char (pp, '+');
+    feature->value = 1;
+  }
+
+  return true;
+}
+
+static hb_bool_t
+parse_feature_tag (char **pp, hb_feature_t *feature)
+{
+  char *p = *pp, c;
+
+  parse_space (pp);
+
+#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
+  while (c = **pp, ISALNUM(c))
+    (*pp)++;
+#undef ISALNUM
+
+  if (p == *pp)
+    return false;
+
+  feature->tag = hb_tag_from_string (p, *pp - p);
+  return true;
+}
+
+static hb_bool_t
+parse_feature_indices (char **pp, hb_feature_t *feature)
+{
+  parse_space (pp);
+
+  hb_bool_t has_start;
+
+  feature->start = 0;
+  feature->end = (unsigned int) -1;
+
+  if (!parse_char (pp, '['))
+    return true;
+
+  has_start = parse_uint (pp, &feature->start);
+
+  if (parse_char (pp, ':')) {
+    parse_uint (pp, &feature->end);
+  } else {
+    if (has_start)
+      feature->end = feature->start + 1;
+  }
+
+  return parse_char (pp, ']');
+}
+
+static hb_bool_t
+parse_feature_value_postfix (char **pp, hb_feature_t *feature)
+{
+  return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
+}
+
+
+static hb_bool_t
+parse_one_feature (char **pp, hb_feature_t *feature)
+{
+  return parse_feature_value_prefix (pp, feature) &&
+        parse_feature_tag (pp, feature) &&
+        parse_feature_indices (pp, feature) &&
+        parse_feature_value_postfix (pp, feature) &&
+        (parse_char (pp, ',') || **pp == '\0');
+}
+
+static void
+skip_one_feature (char **pp)
+{
+  char *e;
+  e = strchr (*pp, ',');
+  if (e)
+    *pp = e + 1;
+  else
+    *pp = *pp + strlen (*pp);
+}
+
+static gboolean
+parse_features (const char *name G_GNUC_UNUSED,
+               const char *arg,
+               gpointer    data,
+               GError    **error G_GNUC_UNUSED)
+{
+  shape_options_t *shape_opts = (shape_options_t *) data;
+  char *s = (char *) arg;
+  char *p;
+
+  shape_opts->num_features = 0;
+  g_free (shape_opts->features);
+  shape_opts->features = NULL;
+
+  if (!*s)
+    return true;
+
+  /* count the features first, so we can allocate memory */
+  p = s;
+  do {
+    shape_opts->num_features++;
+    p = strchr (p, ',');
+    if (p)
+      p++;
+  } while (p);
+
+  shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
+
+  /* now do the actual parsing */
+  p = s;
+  shape_opts->num_features = 0;
+  while (*p) {
+    if (parse_one_feature (&p, &shape_opts->features[shape_opts->num_features]))
+      shape_opts->num_features++;
+    else
+      skip_one_feature (&p);
+  }
+
+  return true;
+}
+
+
+void
+view_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"annotate",       0, 0, G_OPTION_ARG_NONE,        &this->annotate,                "Annotate output rendering",                            NULL},
+    {"background",     0, 0, G_OPTION_ARG_STRING,      &this->back,                    "Set background color (default: "DEFAULT_BACK")",       "red/#rrggbb/#rrggbbaa"},
+    {"foreground",     0, 0, G_OPTION_ARG_STRING,      &this->fore,                    "Set foreground color (default: "DEFAULT_FORE")",       "red/#rrggbb/#rrggbbaa"},
+    {"line-space",     0, 0, G_OPTION_ARG_DOUBLE,      &this->line_space,              "Set space between lines (default: 0)",                 "units"},
+    {"margin",         0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_margin,       "Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
+    {"font-size",      0, 0, G_OPTION_ARG_DOUBLE,      &this->font_size,               "Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "view",
+                    "View options:",
+                    "Options controlling output rendering",
+                    this);
+}
+
+void
+shape_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"list-shapers",   0, G_OPTION_FLAG_NO_ARG,
+                             G_OPTION_ARG_CALLBACK,    (gpointer) &list_shapers,       "List available shapers and quit",      NULL},
+    {"shaper",         0, G_OPTION_FLAG_HIDDEN,
+                             G_OPTION_ARG_CALLBACK,    (gpointer) &parse_shapers,      "Hidden duplicate of --shapers",        NULL},
+    {"shapers",                0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_shapers,      "Comma-separated list of shapers to try","list"},
+    {"direction",      0, 0, G_OPTION_ARG_STRING,      &this->direction,               "Set text direction (default: auto)",   "ltr/rtl/ttb/btt"},
+    {"language",       0, 0, G_OPTION_ARG_STRING,      &this->language,                "Set text language (default: $LANG)",   "langstr"},
+    {"script",         0, 0, G_OPTION_ARG_STRING,      &this->script,                  "Set text script (default: auto)",      "ISO-15924 tag"},
+    {"utf8-clusters",  0, 0, G_OPTION_ARG_NONE,        &this->utf8_clusters,           "Use UTF8 byte indices, not char indices",      NULL},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "shape",
+                    "Shape options:",
+                    "Options controlling the shaping process",
+                    this);
+
+  const gchar *features_help = "Comma-separated list of font features\n"
+    "\n"
+    "    Features can be enabled or disabled, either globally or limited to\n"
+    "    specific character ranges.\n"
+    "\n"
+    "    The range indices refer to the positions between Unicode characters,\n"
+    "    unless the --utf8-clusters is provided, in which case range indices\n"
+    "    refer to UTF-8 byte indices. The position before the first character\n"
+    "    is always 0.\n"
+    "\n"
+    "    The format is Python-esque.  Here is how it all works:\n"
+    "\n"
+    "      Syntax:       Value:    Start:    End:\n"
+    "\n"
+    "    Setting value:\n"
+    "      \"kern\"        1         0         ∞         # Turn feature on\n"
+    "      \"+kern\"       1         0         ∞         # Turn feature on\n"
+    "      \"-kern\"       0         0         ∞         # Turn feature off\n"
+    "      \"kern=0\"      0         0         ∞         # Turn feature off\n"
+    "      \"kern=1\"      1         0         ∞         # Turn feature on\n"
+    "      \"aalt=2\"      2         0         ∞         # Choose 2nd alternate\n"
+    "\n"
+    "    Setting index:\n"
+    "      \"kern[]\"      1         0         ∞         # Turn feature on\n"
+    "      \"kern[:]\"     1         0         ∞         # Turn feature on\n"
+    "      \"kern[5:]\"    1         5         ∞         # Turn feature on, partial\n"
+    "      \"kern[:5]\"    1         0         5         # Turn feature on, partial\n"
+    "      \"kern[3:5]\"   1         3         5         # Turn feature on, range\n"
+    "      \"kern[3]\"     1         3         3+1       # Turn feature on, single char\n"
+    "\n"
+    "    Mixing it all:\n"
+    "\n"
+    "      \"kern[3:5]=0\" 1         3         5         # Turn feature off for range";
+
+  GOptionEntry entries2[] =
+  {
+    {"features",       0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_features,     features_help,  "list"},
+    {NULL}
+  };
+  parser->add_group (entries2,
+                    "features",
+                    "Features options:",
+                    "Options controlling font features used",
+                    this);
+}
+
+void
+font_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"font-file",      0, 0, G_OPTION_ARG_STRING,      &this->font_file,               "Font file-name",                                       "filename"},
+    {"face-index",     0, 0, G_OPTION_ARG_INT,         &this->face_index,              "Face index (default: 0)",                              "index"},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "font",
+                    "Font options:",
+                    "Options controlling the font",
+                    this);
+}
+
+void
+text_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"text",           0, 0, G_OPTION_ARG_STRING,      &this->text,                    "Set input text",                       "string"},
+    {"text-file",      0, 0, G_OPTION_ARG_STRING,      &this->text_file,               "Set input text file-name\n\n    If no text is provided, standard input is used for input.",            "filename"},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "text",
+                    "Text options:",
+                    "Options controlling the input text",
+                    this);
+}
+
+void
+output_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"output-file",    0, 0, G_OPTION_ARG_STRING,      &this->output_file,             "Set output file-name (default: stdout)","filename"},
+    {"output-format",  0, 0, G_OPTION_ARG_STRING,      &this->output_format,           "Set output format",                    "format"},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "output",
+                    "Output options:",
+                    "Options controlling the output",
+                    this);
+}
+
+
+
+hb_font_t *
+font_options_t::get_font (void) const
+{
+  if (font)
+    return font;
+
+  hb_blob_t *blob = NULL;
+
+  /* Create the blob */
+  {
+    char *font_data;
+    unsigned int len = 0;
+    hb_destroy_func_t destroy;
+    void *user_data;
+    hb_memory_mode_t mm;
+
+    /* This is a hell of a lot of code for just reading a file! */
+    if (!font_file)
+      fail (true, "No font file set");
+
+    if (0 == strcmp (font_file, "-")) {
+      /* read it */
+      GString *gs = g_string_new (NULL);
+      char buf[BUFSIZ];
+#ifdef HAVE__SETMODE
+      _setmode (fileno (stdin), _O_BINARY);
+#endif
+      while (!feof (stdin)) {
+       size_t ret = fread (buf, 1, sizeof (buf), stdin);
+       if (ferror (stdin))
+         fail (false, "Failed reading font from standard input: %s",
+               strerror (errno));
+       g_string_append_len (gs, buf, ret);
+      }
+      len = gs->len;
+      font_data = g_string_free (gs, false);
+      user_data = font_data;
+      destroy = (hb_destroy_func_t) g_free;
+      mm = HB_MEMORY_MODE_WRITABLE;
+    } else {
+      GError *error = NULL;
+      GMappedFile *mf = g_mapped_file_new (font_file, false, &error);
+      if (mf) {
+       font_data = g_mapped_file_get_contents (mf);
+       len = g_mapped_file_get_length (mf);
+       if (len) {
+         destroy = (hb_destroy_func_t) g_mapped_file_unref;
+         user_data = (void *) mf;
+         mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+       } else
+         g_mapped_file_unref (mf);
+      } else {
+       fail (false, "%s", error->message);
+       //g_error_free (error);
+      }
+      if (!len) {
+       /* GMappedFile is buggy, it doesn't fail if file isn't regular.
+        * Try reading.
+        * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */
+        GError *error = NULL;
+       gsize l;
+       if (g_file_get_contents (font_file, &font_data, &l, &error)) {
+         len = l;
+         destroy = (hb_destroy_func_t) g_free;
+         user_data = (void *) font_data;
+         mm = HB_MEMORY_MODE_WRITABLE;
+       } else {
+         fail (false, "%s", error->message);
+         //g_error_free (error);
+       }
+      }
+    }
+
+    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
+  }
+
+  /* Create the face */
+  hb_face_t *face = hb_face_create (blob, face_index);
+  hb_blob_destroy (blob);
+
+
+  font = hb_font_create (face);
+
+  unsigned int upem = hb_face_get_upem (face);
+  hb_font_set_scale (font, upem, upem);
+  hb_face_destroy (face);
+
+#ifdef HAVE_FREETYPE
+  hb_ft_font_set_funcs (font);
+#endif
+
+  return font;
+}
+
+
+const char *
+text_options_t::get_line (unsigned int *len)
+{
+  if (text) {
+    if (text_len == (unsigned int) -1)
+      text_len = strlen (text);
+
+    if (!text_len) {
+      *len = 0;
+      return NULL;
+    }
+
+    const char *ret = text;
+    const char *p = (const char *) memchr (text, '\n', text_len);
+    unsigned int ret_len;
+    if (!p) {
+      ret_len = text_len;
+      text += ret_len;
+      text_len = 0;
+    } else {
+      ret_len = p - ret;
+      text += ret_len + 1;
+      text_len -= ret_len + 1;
+    }
+
+    *len = ret_len;
+    return ret;
+  }
+
+  if (!fp) {
+    if (!text_file)
+      fail (true, "At least one of text or text-file must be set");
+
+    if (0 != strcmp (text_file, "-"))
+      fp = fopen (text_file, "r");
+    else
+      fp = stdin;
+
+    if (!fp)
+      fail (false, "Failed opening text file `%s': %s",
+           text_file, strerror (errno));
+
+    gs = g_string_new (NULL);
+  }
+
+  g_string_set_size (gs, 0);
+  char buf[BUFSIZ];
+  while (fgets (buf, sizeof (buf), fp)) {
+    unsigned int bytes = strlen (buf);
+    if (bytes && buf[bytes - 1] == '\n') {
+      bytes--;
+      g_string_append_len (gs, buf, bytes);
+      break;
+    }
+      g_string_append_len (gs, buf, bytes);
+  }
+  if (ferror (fp))
+    fail (false, "Failed reading text: %s",
+         strerror (errno));
+  *len = gs->len;
+  return !*len && feof (fp) ? NULL : gs->str;
+}
+
+
+FILE *
+output_options_t::get_file_handle (void)
+{
+  if (fp)
+    return fp;
+
+  if (output_file)
+    fp = fopen (output_file, "wb");
+  else {
+#ifdef HAVE__SETMODE
+    _setmode (fileno (stdout), _O_BINARY);
+#endif
+    fp = stdout;
+  }
+  if (!fp)
+    fail (false, "Cannot open output file `%s': %s",
+         g_filename_display_name (output_file), strerror (errno));
+
+  return fp;
+}
+
+static gboolean
+parse_verbose (const char *name G_GNUC_UNUSED,
+              const char *arg G_GNUC_UNUSED,
+              gpointer    data G_GNUC_UNUSED,
+              GError    **error G_GNUC_UNUSED)
+{
+  format_options_t *format_opts = (format_options_t *) data;
+  format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
+  return true;
+}
+
+void
+format_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,    &this->show_glyph_names,        "Use glyph indices instead of names",   NULL},
+    {"no-positions",   0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,    &this->show_positions,          "Do not show glyph positions",          NULL},
+    {"no-clusters",    0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,    &this->show_clusters,           "Do not show cluster mapping",          NULL},
+    {"show-text",      0, 0,                     G_OPTION_ARG_NONE,    &this->show_text,               "Show input text",                      NULL},
+    {"show-unicode",   0, 0,                     G_OPTION_ARG_NONE,    &this->show_unicode,            "Show input Unicode codepoints",        NULL},
+    {"show-line-num",  0, 0,                     G_OPTION_ARG_NONE,    &this->show_line_num,           "Show line numbers",                    NULL},
+    {"verbose",                0, G_OPTION_FLAG_NO_ARG,  G_OPTION_ARG_CALLBACK,(gpointer) &parse_verbose,      "Show everything",                      NULL},
+    {NULL}
+  };
+  parser->add_group (entries,
+                    "format",
+                    "Format options:",
+                    "Options controlling the formatting of buffer contents",
+                    this);
+}
+
+void
+format_options_t::serialize_unicode (hb_buffer_t *buffer,
+                                    GString     *gs)
+{
+  unsigned int num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+
+  g_string_append_c (gs, '<');
+  for (unsigned int i = 0; i < num_glyphs; i++)
+  {
+    if (i)
+      g_string_append_c (gs, ',');
+    g_string_append_printf (gs, "U+%04X", info->codepoint);
+    info++;
+  }
+  g_string_append_c (gs, '>');
+}
+
+void
+format_options_t::serialize_glyphs (hb_buffer_t *buffer,
+                                   hb_font_t   *font,
+                                   hb_bool_t    utf8_clusters,
+                                   GString     *gs)
+{
+  unsigned int num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+  g_string_append_c (gs, '[');
+  for (unsigned int i = 0; i < num_glyphs; i++)
+  {
+    if (i)
+      g_string_append_c (gs, '|');
+
+    char glyph_name[32];
+    if (show_glyph_names) {
+      hb_font_get_glyph_name (font, info->codepoint, glyph_name, sizeof (glyph_name));
+      g_string_append_printf (gs, "%s", glyph_name);
+    } else
+      g_string_append_printf (gs, "%u", info->codepoint);
+
+    if (show_clusters) {
+      g_string_append_printf (gs, "=%u", info->cluster);
+      if (utf8_clusters)
+       g_string_append (gs, "u8");
+    }
+
+    if (show_positions && (pos->x_offset || pos->y_offset)) {
+      g_string_append_c (gs, '@');
+      if (pos->x_offset) g_string_append_printf (gs, "%d", pos->x_offset);
+      if (pos->y_offset) g_string_append_printf (gs, ",%d", pos->y_offset);
+    }
+    if (show_positions && (pos->x_advance || pos->y_advance)) {
+      g_string_append_c (gs, '+');
+      if (pos->x_advance) g_string_append_printf (gs, "%d", pos->x_advance);
+      if (pos->y_advance) g_string_append_printf (gs, ",%d", pos->y_advance);
+    }
+
+    info++;
+    pos++;
+  }
+  g_string_append_c (gs, ']');
+}
+void
+format_options_t::serialize_line_no (unsigned int  line_no,
+                                    GString      *gs)
+{
+  if (show_line_num)
+    g_string_append_printf (gs, "%d: ", line_no);
+}
+void
+format_options_t::serialize_buffer_of_text (hb_buffer_t  *buffer,
+                                           unsigned int  line_no,
+                                           const char   *text,
+                                           unsigned int  text_len,
+                                           hb_font_t    *font,
+                                           hb_bool_t     utf8_clusters,
+                                           GString      *gs)
+{
+  if (show_text) {
+    serialize_line_no (line_no, gs);
+    g_string_append_c (gs, '(');
+    g_string_append_len (gs, text, text_len);
+    g_string_append_c (gs, ')');
+    g_string_append_c (gs, '\n');
+  }
+
+  if (show_unicode) {
+    serialize_line_no (line_no, gs);
+    serialize_unicode (buffer, gs);
+    g_string_append_c (gs, '\n');
+  }
+}
+void
+format_options_t::serialize_message (unsigned int  line_no,
+                                    const char   *msg,
+                                    GString      *gs)
+{
+  serialize_line_no (line_no, gs);
+  g_string_append_printf (gs, "%s", msg);
+  g_string_append_c (gs, '\n');
+}
+void
+format_options_t::serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
+                                             unsigned int  line_no,
+                                             const char   *text,
+                                             unsigned int  text_len,
+                                             hb_font_t    *font,
+                                             hb_bool_t     utf8_clusters,
+                                             GString      *gs)
+{
+  serialize_line_no (line_no, gs);
+  serialize_glyphs (buffer, font, utf8_clusters, gs);
+  g_string_append_c (gs, '\n');
+}
diff --git a/util/options.hh b/util/options.hh
new file mode 100644 (file)
index 0000000..9b7baa7
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright © 2011  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 OPTIONS_HH
+#define OPTIONS_HH
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for isatty() */
+#endif
+#ifdef HAVE_IO_H
+#include <io.h> /* for _setmode() under Windows */
+#endif
+
+#include <hb.h>
+#ifdef HAVE_OT
+#include <hb-ot.h>
+#endif
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#undef MIN
+template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
+
+#undef MAX
+template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
+
+
+void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN;
+
+
+extern hb_bool_t debug;
+
+struct option_group_t
+{
+  virtual void add_options (struct option_parser_t *parser) = 0;
+
+  virtual void pre_parse (GError **error G_GNUC_UNUSED) {};
+  virtual void post_parse (GError **error G_GNUC_UNUSED) {};
+};
+
+
+struct option_parser_t
+{
+  option_parser_t (const char *usage) {
+    memset (this, 0, sizeof (*this));
+    usage_str = usage;
+    context = g_option_context_new (usage);
+
+    add_main_options ();
+  }
+  ~option_parser_t (void) {
+    g_option_context_free (context);
+  }
+
+  void add_main_options (void);
+
+  void add_group (GOptionEntry   *entries,
+                 const gchar    *name,
+                 const gchar    *description,
+                 const gchar    *help_description,
+                 option_group_t *option_group);
+
+  void parse (int *argc, char ***argv);
+
+  G_GNUC_NORETURN void usage (void) {
+    g_printerr ("Usage: %s [OPTION...] %s\n", g_get_prgname (), usage_str);
+    exit (1);
+  }
+
+  const char *usage_str;
+  GOptionContext *context;
+};
+
+
+#define DEFAULT_MARGIN 16
+#define DEFAULT_FORE "#000000"
+#define DEFAULT_BACK "#FFFFFF"
+#define DEFAULT_FONT_SIZE 256
+
+struct view_options_t : option_group_t
+{
+  view_options_t (option_parser_t *parser) {
+    annotate = false;
+    fore = DEFAULT_FORE;
+    back = DEFAULT_BACK;
+    line_space = 0;
+    margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
+    font_size = DEFAULT_FONT_SIZE;
+
+    add_options (parser);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  hb_bool_t annotate;
+  const char *fore;
+  const char *back;
+  double line_space;
+  struct margin_t {
+    double t, r, b, l;
+  } margin;
+  double font_size;
+};
+
+
+struct shape_options_t : option_group_t
+{
+  shape_options_t (option_parser_t *parser)
+  {
+    direction = language = script = NULL;
+    features = NULL;
+    num_features = 0;
+    shapers = NULL;
+    utf8_clusters = false;
+
+    add_options (parser);
+  }
+  ~shape_options_t (void)
+  {
+    free (features);
+    g_free (shapers);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  void setup_buffer (hb_buffer_t *buffer)
+  {
+    hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
+    hb_buffer_set_script (buffer, hb_script_from_string (script, -1));
+    hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
+  }
+
+  void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len)
+  {
+    hb_buffer_reset (buffer);
+    hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
+
+    if (!utf8_clusters) {
+      /* Reset cluster values to refer to Unicode character index
+       * instead of UTF-8 index. */
+      unsigned int num_glyphs = hb_buffer_get_length (buffer);
+      hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+      for (unsigned int i = 0; i < num_glyphs; i++)
+      {
+       info->cluster = i;
+       info++;
+      }
+    }
+
+    setup_buffer (buffer);
+  }
+
+  hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer)
+  {
+    return hb_shape_full (font, buffer, features, num_features, shapers);
+  }
+
+  void shape_closure (const char *text, int text_len,
+                     hb_font_t *font, hb_buffer_t *buffer,
+                     hb_set_t *glyphs)
+  {
+    hb_buffer_reset (buffer);
+    hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
+    setup_buffer (buffer);
+    hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs);
+  }
+
+  const char *direction;
+  const char *language;
+  const char *script;
+  hb_feature_t *features;
+  unsigned int num_features;
+  char **shapers;
+  hb_bool_t utf8_clusters;
+};
+
+
+struct font_options_t : option_group_t
+{
+  font_options_t (option_parser_t *parser) {
+    font_file = NULL;
+    face_index = 0;
+
+    font = NULL;
+
+    add_options (parser);
+  }
+  ~font_options_t (void) {
+    hb_font_destroy (font);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  hb_font_t *get_font (void) const;
+
+  const char *font_file;
+  int face_index;
+
+  private:
+  mutable hb_font_t *font;
+};
+
+
+struct text_options_t : option_group_t
+{
+  text_options_t (option_parser_t *parser) {
+    text = NULL;
+    text_file = NULL;
+
+    fp = NULL;
+    gs = NULL;
+    text_len = (unsigned int) -1;
+
+    add_options (parser);
+  }
+  ~text_options_t (void) {
+    if (gs)
+      g_string_free (gs, true);
+    if (fp)
+      fclose (fp);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  void post_parse (GError **error G_GNUC_UNUSED) {
+    if (text && text_file)
+      g_set_error (error,
+                  G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                  "Only one of text and text-file can be set");
+
+  };
+
+  const char *get_line (unsigned int *len);
+
+  const char *text;
+  const char *text_file;
+
+  private:
+  FILE *fp;
+  GString *gs;
+  unsigned int text_len;
+};
+
+struct output_options_t : option_group_t
+{
+  output_options_t (option_parser_t *parser) {
+    output_file = NULL;
+    output_format = NULL;
+
+    fp = NULL;
+
+    add_options (parser);
+  }
+  ~output_options_t (void) {
+    if (fp)
+      fclose (fp);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  void post_parse (GError **error G_GNUC_UNUSED)
+  {
+    if (output_file && !output_format) {
+      output_format = strrchr (output_file, '.');
+      if (output_format)
+         output_format++; /* skip the dot */
+    }
+
+    if (output_file && 0 == strcmp (output_file, "-"))
+      output_file = NULL; /* STDOUT */
+  }
+
+  FILE *get_file_handle (void);
+
+  const char *output_file;
+  const char *output_format;
+
+  mutable FILE *fp;
+};
+
+struct format_options_t : option_group_t
+{
+  format_options_t (option_parser_t *parser) {
+    show_glyph_names = true;
+    show_positions = true;
+    show_clusters = true;
+    show_text = false;
+    show_unicode = false;
+    show_line_num = false;
+
+    add_options (parser);
+  }
+
+  void add_options (option_parser_t *parser);
+
+  void serialize_unicode (hb_buffer_t  *buffer,
+                         GString      *gs);
+  void serialize_glyphs (hb_buffer_t  *buffer,
+                        hb_font_t    *font,
+                        hb_bool_t    utf8_clusters,
+                        GString      *gs);
+  void serialize_line_no (unsigned int  line_no,
+                         GString      *gs);
+  void serialize_buffer_of_text (hb_buffer_t  *buffer,
+                                unsigned int  line_no,
+                                const char   *text,
+                                unsigned int  text_len,
+                                hb_font_t    *font,
+                                hb_bool_t     utf8_clusters,
+                                GString      *gs);
+  void serialize_message (unsigned int  line_no,
+                         const char   *msg,
+                         GString      *gs);
+  void serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
+                                  unsigned int  line_no,
+                                  const char   *text,
+                                  unsigned int  text_len,
+                                  hb_font_t    *font,
+                                  hb_bool_t     utf8_clusters,
+                                  GString      *gs);
+
+
+  hb_bool_t show_glyph_names;
+  hb_bool_t show_positions;
+  hb_bool_t show_clusters;
+  hb_bool_t show_text;
+  hb_bool_t show_unicode;
+  hb_bool_t show_line_num;
+};
+
+
+#endif
diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
new file mode 100644 (file)
index 0000000..220daa4
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2011,2012  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 "options.hh"
+
+#ifndef HB_SHAPE_CONSUMER_HH
+#define HB_SHAPE_CONSUMER_HH
+
+
+template <typename output_t>
+struct shape_consumer_t
+{
+  shape_consumer_t (option_parser_t *parser)
+                 : shaper (parser),
+                   output (parser) {}
+
+  void init (const font_options_t *font_opts)
+  {
+    font = hb_font_reference (font_opts->get_font ());
+    output.init (font_opts);
+    failed = false;
+  }
+  void consume_line (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len)
+  {
+    output.new_line ();
+
+    shaper.populate_buffer (buffer, text, text_len);
+    output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
+
+    if (!shaper.shape (font, buffer)) {
+      failed = true;
+      hb_buffer_set_length (buffer, 0);
+      output.shape_failed (buffer, text, text_len, shaper.utf8_clusters);
+      return;
+    }
+
+    output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters);
+  }
+  void finish (const font_options_t *font_opts)
+  {
+    output.finish (font_opts);
+    hb_font_destroy (font);
+    font = NULL;
+  }
+
+  public:
+  bool failed;
+
+  protected:
+  shape_options_t shaper;
+  output_t output;
+
+  hb_font_t *font;
+};
+
+
+#endif
diff --git a/util/view-cairo.cc b/util/view-cairo.cc
new file mode 100644 (file)
index 0000000..666013e
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2011  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 "view-cairo.hh"
+
+void
+view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font,
+                               double *w, double *h)
+{
+  cairo_font_extents_t font_extents;
+
+  cairo_scaled_font_extents (scaled_font, &font_extents);
+
+  bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
+  (vertical ? *w : *h) = (int) lines->len * (font_extents.height + view_options.line_space) - view_options.line_space;
+  (vertical ? *h : *w) = 0;
+  for (unsigned int i = 0; i < lines->len; i++) {
+    helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
+    double x_advance, y_advance;
+    line.get_advance (&x_advance, &y_advance);
+    if (vertical)
+      *h =  MAX (*h, y_advance);
+    else
+      *w =  MAX (*w, x_advance);
+  }
+
+  *w += view_options.margin.l + view_options.margin.r;
+  *h += view_options.margin.t + view_options.margin.b;
+}
+
+void
+view_cairo_t::render (const font_options_t *font_opts)
+{
+  cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts, view_options.font_size);
+  double w, h;
+  get_surface_size (scaled_font, &w, &h);
+  cairo_t *cr = helper_cairo_create_context (w, h, &view_options, &output_options);
+  cairo_set_scaled_font (cr, scaled_font);
+  cairo_scaled_font_destroy (scaled_font);
+
+  draw (cr);
+
+  helper_cairo_destroy_context (cr);
+}
+
+void
+view_cairo_t::draw (cairo_t *cr)
+{
+  cairo_save (cr);
+
+  bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
+  int v = vertical ? 1 : 0;
+  int h = vertical ? 0 : 1;
+  cairo_font_extents_t font_extents;
+  cairo_font_extents (cr, &font_extents);
+  cairo_translate (cr, view_options.margin.l, view_options.margin.t);
+  double descent;
+  if (vertical)
+    descent = font_extents.height * (lines->len + .5);
+  else
+    descent = font_extents.height - font_extents.ascent;
+  cairo_translate (cr, v * descent, h * -descent);
+  for (unsigned int i = 0; i < lines->len; i++)
+  {
+    helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i);
+
+    if (i)
+      cairo_translate (cr, v * -view_options.line_space, h * view_options.line_space);
+
+    cairo_translate (cr, v * -font_extents.height, h * font_extents.height);
+
+    if (view_options.annotate) {
+      cairo_save (cr);
+
+      /* Draw actual glyph origins */
+      cairo_set_source_rgba (cr, 1., 0., 0., .5);
+      cairo_set_line_width (cr, 5);
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+      for (unsigned i = 0; i < l.num_glyphs; i++) {
+       cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y);
+       cairo_rel_line_to (cr, 0, 0);
+      }
+      cairo_stroke (cr);
+
+      cairo_restore (cr);
+    }
+
+    if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) {
+      /* cairo_show_glyphs() doesn't support subpixel positioning */
+      cairo_glyph_path (cr, l.glyphs, l.num_glyphs);
+      cairo_fill (cr);
+    } else if (l.num_clusters)
+      cairo_show_text_glyphs (cr,
+                             l.utf8, l.utf8_len,
+                             l.glyphs, l.num_glyphs,
+                             l.clusters, l.num_clusters,
+                             l.cluster_flags);
+    else
+      cairo_show_glyphs (cr, l.glyphs, l.num_glyphs);
+  }
+
+  cairo_restore (cr);
+}
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
new file mode 100644 (file)
index 0000000..c621984
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2011  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 "options.hh"
+#include "helper-cairo.hh"
+
+#ifndef VIEW_CAIRO_HH
+#define VIEW_CAIRO_HH
+
+
+struct view_cairo_t {
+  view_cairo_t (option_parser_t *parser)
+              : output_options (parser),
+                view_options (parser) {}
+  ~view_cairo_t (void) {
+    if (debug)
+      cairo_debug_reset_static_data ();
+  }
+
+  void init (const font_options_t *font_opts)
+  {
+    lines = g_array_new (false, false, sizeof (helper_cairo_line_t));
+    scale = double (view_options.font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
+  }
+  void new_line (void)
+  {
+  }
+  void consume_text (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len,
+                    hb_bool_t     utf8_clusters)
+  {
+  }
+  void shape_failed (hb_buffer_t  *buffer,
+                    const char   *text,
+                    unsigned int  text_len,
+                    hb_bool_t     utf8_clusters)
+  {
+    fail (false, "all shapers failed");
+  }
+  void consume_glyphs (hb_buffer_t  *buffer,
+                      const char   *text,
+                      unsigned int  text_len,
+                      hb_bool_t     utf8_clusters)
+  {
+    direction = hb_buffer_get_direction (buffer);
+    helper_cairo_line_t l;
+    helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale, utf8_clusters);
+    g_array_append_val (lines, l);
+  }
+  void finish (const font_options_t *font_opts)
+  {
+    render (font_opts);
+
+    for (unsigned int i = 0; i < lines->len; i++) {
+      helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
+      line.finish ();
+    }
+    g_array_unref (lines);
+  }
+
+  protected:
+
+  output_options_t output_options;
+  view_options_t view_options;
+
+  void render (const font_options_t *font_opts);
+  void get_surface_size (cairo_scaled_font_t *scaled_font, double *w, double *h);
+  void draw (cairo_t *cr);
+
+  hb_direction_t direction; // Remove this, make segment_properties accessible
+  GArray *lines;
+  double scale;
+};
+
+#endif