diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /gfx/harfbuzz/src | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/harfbuzz/src')
150 files changed, 54202 insertions, 0 deletions
diff --git a/gfx/harfbuzz/src/Makefile.am b/gfx/harfbuzz/src/Makefile.am new file mode 100644 index 000000000..8cfe4ac7c --- /dev/null +++ b/gfx/harfbuzz/src/Makefile.am @@ -0,0 +1,372 @@ +# Process this file with automake to produce Makefile.in + +SUBDIRS = +DIST_SUBDIRS = +BUILT_SOURCES = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +DISTCHECK_CONFIGURE_FLAGS = --enable-introspection + +# The following warning options are useful for debugging: -Wpadded +#AM_CXXFLAGS = + +# Convenience targets: +lib: $(BUILT_SOURCES) libharfbuzz.la +fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la + +lib_LTLIBRARIES = libharfbuzz.la + +include Makefile.sources + +HBCFLAGS = +HBLIBS = +HBNONPCLIBS = +HBDEPS = +HBSOURCES = $(HB_BASE_sources) +HBHEADERS = $(HB_BASE_headers) +HBNODISTHEADERS = $(HB_NODIST_headers) + +if HAVE_OT +HBSOURCES += $(HB_OT_sources) +HBHEADERS += $(HB_OT_headers) +endif + +if HAVE_FALLBACK +HBSOURCES += $(HB_FALLBACK_sources) +endif + +if HAVE_PTHREAD +HBCFLAGS += $(PTHREAD_CFLAGS) +HBNONPCLIBS += $(PTHREAD_LIBS) +endif + +if HAVE_GLIB +HBCFLAGS += $(GLIB_CFLAGS) +HBLIBS += $(GLIB_LIBS) +HBDEPS += $(GLIB_DEPS) +HBSOURCES += $(HB_GLIB_sources) +HBHEADERS += $(HB_GLIB_headers) +endif + +if HAVE_FREETYPE +HBCFLAGS += $(FREETYPE_CFLAGS) +HBLIBS += $(FREETYPE_LIBS) +# XXX +# The following creates a recursive dependency on FreeType if FreeType is +# built with HarfBuzz support enabled. Newer pkg-config handles that just +# fine but pkg-config 0.26 as shipped in Ubuntu 14.04 crashes. Remove +# in a year or two, or otherwise work around it... +#HBDEPS += $(FREETYPE_DEPS) +HBSOURCES += $(HB_FT_sources) +HBHEADERS += $(HB_FT_headers) +endif + +if HAVE_GRAPHITE2 +HBCFLAGS += $(GRAPHITE2_CFLAGS) +HBLIBS += $(GRAPHITE2_LIBS) +HBDEPS += $(GRAPHITE2_DEPS) +HBSOURCES += $(HB_GRAPHITE2_sources) +HBHEADERS += $(HB_GRAPHITE2_headers) +endif + +if HAVE_UNISCRIBE +HBCFLAGS += $(UNISCRIBE_CFLAGS) +HBNONPCLIBS += $(UNISCRIBE_LIBS) +HBSOURCES += $(HB_UNISCRIBE_sources) +HBHEADERS += $(HB_UNISCRIBE_headers) +endif + +if HAVE_DIRECTWRITE +HBCFLAGS += $(DIRECTWRITE_CXXFLAGS) +HBNONPCLIBS += $(DIRECTWRITE_LIBS) +HBSOURCES += $(HB_DIRECTWRITE_sources) +HBHEADERS += $(HB_DIRECTWRITE_headers) +endif + +if HAVE_CORETEXT +HBCFLAGS += $(CORETEXT_CFLAGS) +HBNONPCLIBS += $(CORETEXT_LIBS) +HBSOURCES += $(HB_CORETEXT_sources) +HBHEADERS += $(HB_CORETEXT_headers) +endif + +if HAVE_UCDN +SUBDIRS += hb-ucdn +HBCFLAGS += -I$(srcdir)/hb-ucdn +HBLIBS += hb-ucdn/libhb-ucdn.la +HBSOURCES += $(HB_UCDN_sources) +endif +DIST_SUBDIRS += hb-ucdn + + +# Put the library together + +HBLIBS += $(HBNONPCLIBS) + +if OS_WIN32 +export_symbols = -export-symbols harfbuzz.def +harfbuzz_def_dependency = harfbuzz.def +libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS) +else +# Use a C linker for GCC, not C++; Don't link to libstdc++ +if HAVE_GCC +libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS) +else +libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS) +endif +endif + +libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS) +libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) +libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined +libharfbuzz_la_LIBADD = $(HBLIBS) +EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency) +pkginclude_HEADERS = $(HBHEADERS) +nodist_pkginclude_HEADERS = $(HBNODISTHEADERS) +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = harfbuzz.pc +EXTRA_DIST += harfbuzz.pc.in + +FUZZING_CPPFLAGS= \ + -DHB_NDEBUG \ + -DHB_MAX_NESTING_LEVEL=3 \ + -DHB_SANITIZE_MAX_EDITS=3 \ + -DHB_BUFFER_MAX_EXPANSION_FACTOR=3 \ + -DHB_BUFFER_MAX_LEN_MIN=8 \ + -DHB_BUFFER_MAX_LEN_DEFAULT=128 \ + $(NULL) +EXTRA_LTLIBRARIES = libharfbuzz-fuzzing.la +libharfbuzz_fuzzing_la_LINK = $(libharfbuzz_la_LINK) +libharfbuzz_fuzzing_la_SOURCES = $(libharfbuzz_la_SOURCES) +libharfbuzz_fuzzing_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(FUZZING_CPPFLAGS) +libharfbuzz_fuzzing_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS) +libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD) +EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES) +CLEANFILES += libharfbuzz-fuzzing.la + +if HAVE_ICU +if HAVE_ICU_BUILTIN +HBCFLAGS += $(ICU_CFLAGS) +HBLIBS += $(ICU_LIBS) +HBSOURCES += $(HB_ICU_sources) +HBHEADERS += $(HB_ICU_headers) +else +lib_LTLIBRARIES += libharfbuzz-icu.la +libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources) +libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS) +libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la +pkginclude_HEADERS += $(HB_ICU_headers) +pkgconfig_DATA += harfbuzz-icu.pc +endif +endif +EXTRA_DIST += harfbuzz-icu.pc.in + +if HAVE_GOBJECT +lib_LTLIBRARIES += libharfbuzz-gobject.la +libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources) +nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources) +libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS) +libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la +pkginclude_HEADERS += $(HB_GOBJECT_headers) +nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers) +pkgconfig_DATA += harfbuzz-gobject.pc + +BUILT_SOURCES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +DISTCLEANFILES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS) + $(AM_V_GEN) $(GLIB_MKENUMS) \ + --identifier-prefix hb_ --symbol-prefix hb_gobject \ + --template $^ | \ + sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \ + || ($(RM) "$@"; false) +endif +EXTRA_DIST += \ + harfbuzz-gobject.pc.in \ + hb-gobject-enums.cc.tmpl \ + hb-gobject-enums.h.tmpl \ + $(NULL) + + +%.pc: %.pc.in $(top_builddir)/config.status + $(AM_V_GEN) \ + $(SED) -e 's@%prefix%@$(prefix)@g' \ + -e 's@%exec_prefix%@$(exec_prefix)@g' \ + -e 's@%libdir%@$(libdir)@g' \ + -e 's@%includedir%@$(includedir)@g' \ + -e 's@%libs_private%@$(HBNONPCLIBS)@g' \ + -e 's@%requires_private%@$(HBDEPS)@g' \ + -e 's@%VERSION%@$(VERSION)@g' \ + "$<" > "$@" \ + || ($(RM) "$@"; false) + +CLEANFILES += $(pkgconfig_DATA) + + +CLEANFILES += harfbuzz.def +harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS) + $(AM_V_GEN) (echo EXPORTS; \ + (cat $^ || echo 'hb_ERROR ()' ) | \ + $(EGREP) '^hb_.* \(' | \ + sed -e 's/ (.*//' | \ + LANG=C sort; \ + echo LIBRARY libharfbuzz-0.dll; \ + ) >"$@" + @ ! grep -q hb_ERROR "$@" \ + || ($(RM) "$@"; false) + + +GENERATORS = \ + gen-arabic-table.py \ + gen-indic-table.py \ + gen-use-table.py \ + $(NULL) +EXTRA_DIST += $(GENERATORS) + +unicode-tables: arabic-table indic-table use-table + +arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh \ + || ($(RM) hb-ot-shape-complex-arabic-table.hh; false) + +indic-table: gen-indic-table.py IndicSyllabicCategory-7.0.0.txt IndicMatraCategory-7.0.0.txt Blocks.txt + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \ + || ($(RM) hb-ot-shape-complex-indic-table.cc; false) + +use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-use-table.cc \ + || ($(RM) hb-ot-shape-complex-use-table.cc; false) + +built-sources: $(BUILT_SOURCES) + +.PHONY: unicode-tables arabic-table indic-table use-table built-sources + +RAGEL_GENERATED = \ + $(srcdir)/hb-buffer-deserialize-json.hh \ + $(srcdir)/hb-buffer-deserialize-text.hh \ + $(srcdir)/hb-ot-shape-complex-indic-machine.hh \ + $(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \ + $(srcdir)/hb-ot-shape-complex-use-machine.hh \ + $(NULL) +BUILT_SOURCES += $(RAGEL_GENERATED) +EXTRA_DIST += \ + hb-buffer-deserialize-json.rl \ + hb-buffer-deserialize-text.rl \ + hb-ot-shape-complex-indic-machine.rl \ + hb-ot-shape-complex-myanmar-machine.rl \ + hb-ot-shape-complex-use-machine.rl \ + $(NULL) +MAINTAINERCLEANFILES += $(RAGEL_GENERATED) +$(srcdir)/%.hh: $(srcdir)/%.rl + $(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \ + || ($(RM) "$@"; false) + +noinst_PROGRAMS = \ + main \ + test \ + test-buffer-serialize \ + test-size-params \ + test-would-substitute \ + $(NULL) +bin_PROGRAMS = + +main_SOURCES = main.cc +main_CPPFLAGS = $(HBCFLAGS) +main_LDADD = libharfbuzz.la $(HBLIBS) + +test_SOURCES = test.cc +test_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +test_would_substitute_SOURCES = test-would-substitute.cc +test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +test_size_params_SOURCES = test-size-params.cc +test_size_params_CPPFLAGS = $(HBCFLAGS) +test_size_params_LDADD = libharfbuzz.la $(HBLIBS) + +test_buffer_serialize_SOURCES = test-buffer-serialize.cc +test_buffer_serialize_CPPFLAGS = $(HBCFLAGS) +test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS) + +dist_check_SCRIPTS = \ + check-c-linkage-decls.sh \ + check-defs.sh \ + check-header-guards.sh \ + check-includes.sh \ + check-libstdc++.sh \ + check-static-inits.sh \ + check-symbols.sh \ + $(NULL) + +check_PROGRAMS = \ + test-ot-tag \ + $(NULL) +test_ot_tag_SOURCES = hb-ot-tag.cc +test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN +test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS) + +TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS) +TESTS_ENVIRONMENT = \ + srcdir="$(srcdir)" \ + MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ + HBSOURCES="$(HBSOURCES)" \ + HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \ + $(NULL) + +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?! +INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_ --warn-all +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) +INTROSPECTION_SCANNER_ENV = CC="$(CC)" + +HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la +HarfBuzz_0_0_gir_INCLUDES = GObject-2.0 +HarfBuzz_0_0_gir_CFLAGS = \ + $(INCLUDES) \ + $(HBCFLAGS) \ + -DHB_H \ + -DHB_H_IN \ + -DHB_OT_H \ + -DHB_OT_H_IN \ + -DHB_GOBJECT_H \ + -DHB_GOBJECT_H_IN \ + -DHB_EXTERN= \ + $(NULL) +HarfBuzz_0_0_gir_LIBS = \ + libharfbuzz.la \ + libharfbuzz-gobject.la \ + $(NULL) +HarfBuzz_0_0_gir_FILES = \ + $(HBHEADERS) \ + $(HBNODISTHEADERS) \ + $(HBSOURCES) \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(HB_GOBJECT_sources) \ + $(HB_GOBJECT_STRUCTS_headers) \ + $(NULL) + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) + +endif + +-include $(top_srcdir)/git.mk diff --git a/gfx/harfbuzz/src/check-c-linkage-decls.sh b/gfx/harfbuzz/src/check-c-linkage-decls.sh new file mode 100755 index 000000000..b10310f53 --- /dev/null +++ b/gfx/harfbuzz/src/check-c-linkage-decls.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + +test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` +test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` + + +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 have HB_BEGIN_DECLS / HB_END_DECLS, but it should" + stat=1 + fi +done +for x in $HBSOURCES; 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 has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn't" + stat=1 + fi +done + +exit $stat diff --git a/gfx/harfbuzz/src/check-defs.sh b/gfx/harfbuzz/src/check-defs.sh new file mode 100755 index 000000000..65a24670e --- /dev/null +++ b/gfx/harfbuzz/src/check-defs.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +test -z "$MAKE" && MAKE=make +stat=0 + +if which nm 2>/dev/null >/dev/null; then + : +else + echo "check-defs.sh: 'nm' not found; skipping test" + exit 77 +fi + +defs="harfbuzz.def" +$MAKE $defs > /dev/null +tested=false +for def in $defs; do + lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'` + so=.libs/lib${lib}.so + + EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`" + + if test -f "$so"; then + + echo "Checking that $so has the same symbol list as $def" + { + echo EXPORTS + echo "$EXPORTED_SYMBOLS" + # cheat: copy the last line from the def file! + tail -n1 "$def" + } | diff "$def" - >&2 || stat=1 + + tested=true + fi +done +if ! $tested; then + echo "check-defs.sh: libharfbuzz shared library not found; skipping test" + exit 77 +fi + +exit $stat diff --git a/gfx/harfbuzz/src/check-header-guards.sh b/gfx/harfbuzz/src/check-header-guards.sh new file mode 100755 index 000000000..09c5ea8b2 --- /dev/null +++ b/gfx/harfbuzz/src/check-header-guards.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + +test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` +test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` + +for x in $HBHEADERS $HBSOURCES; do + test -f "$srcdir/$x" && x="$srcdir/$x" + echo "$x" | grep -q '[^h]$' && continue; + xx=`echo "$x" | sed 's@.*/@@'` + tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'` + lines=`grep -w "$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 + fi +done + +exit $stat diff --git a/gfx/harfbuzz/src/check-includes.sh b/gfx/harfbuzz/src/check-includes.sh new file mode 100755 index 000000000..902f2357e --- /dev/null +++ b/gfx/harfbuzz/src/check-includes.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + +test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` +test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` + + +echo 'Checking that public header files #include "hb-common.h" or "hb.h" first (or none)' + +for x in $HBHEADERS; do + test -f "$srcdir/$x" && x="$srcdir/$x" + grep '#.*\<include\>' "$x" /dev/null | head -n 1 +done | +grep -v '"hb-common[.]h"' | +grep -v '"hb[.]h"' | +grep -v 'hb-common[.]h:' | +grep -v 'hb[.]h:' | +grep . >&2 && stat=1 + + +echo 'Checking that source files #include "hb-*private.hh" first (or none)' + +for x in $HBSOURCES; do + test -f "$srcdir/$x" && x="$srcdir/$x" + grep '#.*\<include\>' "$x" /dev/null | grep -v 'include _' | head -n 1 +done | +grep -v '"hb-.*private[.]hh"' | +grep -v 'hb-private[.]hh:' | +grep . >&2 && stat=1 + + +echo 'Checking that there is no #include <hb.*.h>' +for x in $HBHEADERS $HBSOURCES; do + test -f "$srcdir/$x" && x="$srcdir/$x" + grep '#.*\<include\>.*<.*hb' "$x" /dev/null >&2 && stat=1 +done + + +exit $stat diff --git a/gfx/harfbuzz/src/check-libstdc++.sh b/gfx/harfbuzz/src/check-libstdc++.sh new file mode 100755 index 000000000..b541828bc --- /dev/null +++ b/gfx/harfbuzz/src/check-libstdc++.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + + +if which ldd 2>/dev/null >/dev/null; then + : +else + echo "check-libstdc++.sh: 'ldd' not found; skipping test" + exit 77 +fi + +tested=false +for suffix in so dylib; do + so=.libs/libharfbuzz.$suffix + if ! test -f "$so"; then continue; fi + + echo "Checking that we are not linking to libstdc++ or libc++" + if ldd $so | grep 'libstdc[+][+]\|libc[+][+]'; then + echo "Ouch, linked to libstdc++ or libc++" + stat=1 + fi + tested=true +done +if ! $tested; then + echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test" + exit 77 +fi + +exit $stat diff --git a/gfx/harfbuzz/src/check-static-inits.sh b/gfx/harfbuzz/src/check-static-inits.sh new file mode 100755 index 000000000..1446fa734 --- /dev/null +++ b/gfx/harfbuzz/src/check-static-inits.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + + +if which objdump 2>/dev/null >/dev/null; then + : +else + echo "check-static-inits.sh: 'objdump' not found; skipping test" + exit 77 +fi + +OBJS=.libs/*.o +if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then + echo "check-static-inits.sh: object files not found; skipping test" + exit 77 +fi + +echo "Checking that no object file has static initializers" +for obj in $OBJS; do + if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then + echo "Ouch, $obj has static initializers/finalizers" + stat=1 + fi +done + +echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff" +for obj in $OBJS; do + if objdump -t "$obj" | grep '__cxa_'; then + echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff" + stat=1 + fi +done + +exit $stat diff --git a/gfx/harfbuzz/src/check-symbols.sh b/gfx/harfbuzz/src/check-symbols.sh new file mode 100755 index 000000000..ba09ba1cc --- /dev/null +++ b/gfx/harfbuzz/src/check-symbols.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + + +if which nm 2>/dev/null >/dev/null; then + : +else + echo "check-symbols.sh: 'nm' not found; skipping test" + exit 77 +fi + +echo "Checking that we are not exposing internal symbols" +tested=false +for suffix in so dylib; do + so=.libs/libharfbuzz.$suffix + if ! test -f "$so"; then continue; fi + + EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| ___gcov_flush\>\| llvm_\| _llvm_' | cut -d' ' -f3`" + + prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'` + + # On mac, C symbols are prefixed with _ + if test $suffix = dylib; then prefix="_$prefix"; fi + + echo "Processing $so" + if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then + echo "Ouch, internal symbols exposed" + stat=1 + fi + + tested=true +done +if ! $tested; then + echo "check-symbols.sh: no shared library found; skipping test" + exit 77 +fi + +exit $stat diff --git a/gfx/harfbuzz/src/gen-arabic-table.py b/gfx/harfbuzz/src/gen-arabic-table.py new file mode 100755 index 000000000..308435f99 --- /dev/null +++ b/gfx/harfbuzz/src/gen-arabic-table.py @@ -0,0 +1,269 @@ +#!/usr/bin/python + +import sys +import os.path + +if len (sys.argv) != 4: + print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt" + sys.exit (1) + +files = [file (x) for x in sys.argv[1:]] + +headers = [[files[0].readline (), files[0].readline ()], [files[2].readline (), files[2].readline ()]] +headers.append (["UnicodeData.txt does not have a header."]) +while files[0].readline ().find ('##################') < 0: + pass + +blocks = {} +def read_blocks(f): + global blocks + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + blocks[u] = t + +def print_joining_table(f): + + values = {} + for line in f: + + if line[0] == '#': + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + u = int (fields[0], 16) + + if fields[3] in ["ALAPH", "DALATH RISH"]: + value = "JOINING_GROUP_" + fields[3].replace(' ', '_') + else: + value = "JOINING_TYPE_" + fields[2] + values[u] = value + + short_value = {} + for value in set([v for v in values.values()] + ['JOINING_TYPE_X']): + short = ''.join(x[0] for x in value.split('_')[2:]) + assert short not in short_value.values() + short_value[value] = short + + print + for value,short in short_value.items(): + print "#define %s %s" % (short, value) + + uu = sorted(values.keys()) + num = len(values) + all_blocks = set([blocks[u] for u in uu]) + + last = -100000 + ranges = [] + for u in uu: + if u - last <= 1+16*5: + ranges[-1][-1] = u + else: + ranges.append([u,u]) + last = u + + print + print "static const uint8_t joining_table[] =" + print "{" + last_block = None + offset = 0 + for start,end in ranges: + + print + print "#define joining_offset_0x%04xu %d" % (start, offset) + + for u in range(start, end+1): + + block = blocks.get(u, last_block) + value = values.get(u, "JOINING_TYPE_X") + + if block != last_block or u == start: + if u != start: + print + if block in all_blocks: + print "\n /* %s */" % block + else: + print "\n /* FILLER */" + last_block = block + if u % 32 != 0: + print + print " /* %04X */" % (u//32*32), " " * (u % 32), + + if u % 32 == 0: + print + print " /* %04X */ " % u, + sys.stdout.write("%s," % short_value[value]) + print + + offset += end - start + 1 + print + occupancy = num * 100. / offset + print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy) + print + + page_bits = 12; + print + print "static unsigned int" + print "joining_type (hb_codepoint_t u)" + print "{" + print " switch (u >> %d)" % page_bits + print " {" + pages = set([u>>page_bits for u in [s for s,e in ranges]+[e for s,e in ranges]]) + for p in sorted(pages): + print " case 0x%0Xu:" % p + for (start,end) in ranges: + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "joining_offset_0x%04xu" % start + print " if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset) + print " break;" + print "" + print " default:" + print " break;" + print " }" + print " return X;" + print "}" + print + for value,short in short_value.items(): + print "#undef %s" % (short) + print + +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 0 + for shape in ['initial', 'medial', 'final', 'isolated']] + value = ', '.join ("0x%04Xu" % 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%04Xu" % min_u + print "#define SHAPING_TABLE_LAST 0x%04Xu" % 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 ligature_set_t {" + print " uint16_t first;" + print " struct ligature_pairs_t {" + 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%04Xu, {" % (first) + for liga in ligas[first]: + print " { 0x%04Xu, 0x%04Xu }, /* %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 UnicodeData.txt Blocks.txt" +print " *" +print " * on files with these headers:" +print " *" +for h in headers: + for l in h: + print " * %s" % (l.strip()) +print " */" +print +print "#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH" +print "#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH" +print + +read_blocks (files[2]) +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 == */" + diff --git a/gfx/harfbuzz/src/gen-indic-table.py b/gfx/harfbuzz/src/gen-indic-table.py new file mode 100755 index 000000000..3016cd07b --- /dev/null +++ b/gfx/harfbuzz/src/gen-indic-table.py @@ -0,0 +1,260 @@ +#!/usr/bin/python + +import sys + +if len (sys.argv) != 4: + print >>sys.stderr, "usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt" + sys.exit (1) + +ALLOWED_SINGLES = [0x00A0, 0x25CC] +ALLOWED_BLOCKS = [ + 'Basic Latin', + 'Latin-1 Supplement', + 'Devanagari', + 'Bengali', + 'Gurmukhi', + 'Gujarati', + 'Oriya', + 'Tamil', + 'Telugu', + 'Kannada', + 'Malayalam', + 'Sinhala', + 'Myanmar', + 'Khmer', + 'Vedic Extensions', + 'General Punctuation', + 'Superscripts and Subscripts', + 'Devanagari Extended', + 'Myanmar Extended-B', + 'Myanmar Extended-A', +] + +files = [file (x) for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for f in files] + +data = [{} for f in files] +values = [{} for f in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + data[i][u] = t + values[i][t] = values[i].get (t, 0) + end - start + 1 + +# Merge data into one dict: +defaults = ('Other', 'Not_Applicable', 'No_Block') +for i,v in enumerate (defaults): + values[i][v] = values[i].get (v, 0) + 1 +combined = {} +for i,d in enumerate (data): + for u,v in d.items (): + if i == 2 and not u in combined: + continue + if not u in combined: + combined[u] = list (defaults) + combined[u][i] = v +combined = {k:v for k,v in combined.items() if k in ALLOWED_SINGLES or v[2] in ALLOWED_BLOCKS} +data = combined +del combined +num = len (data) + +for u in [0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D3]: + if data[u][0] == 'Other': + data[u][0] = "Vowel_Dependent" + +# Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out +singles = {} +for u in ALLOWED_SINGLES: + singles[u] = data[u] + del data[u] + +print "/* == Start of generated table == */" +print "/*" +print " * The following table is generated by running:" +print " *" +print " * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt" +print " *" +print " * on files with these headers:" +print " *" +for h in headers: + for l in h: + print " * %s" % (l.strip()) +print " */" +print +print '#include "hb-ot-shape-complex-indic-private.hh"' +print + +# Shorten values +short = [{ + "Bindu": 'Bi', + "Cantillation_Mark": 'Ca', + "Joiner": 'ZWJ', + "Non_Joiner": 'ZWNJ', + "Number": 'Nd', + "Visarga": 'Vs', + "Vowel": 'Vo', + "Vowel_Dependent": 'M', + "Consonant_Prefixed": 'CPrf', + "Other": 'x', +},{ + "Not_Applicable": 'x', +}] +all_shorts = [{},{}] + +# Add some of the values, to make them more readable, and to avoid duplicates + + +for i in range (2): + for v,s in short[i].items (): + all_shorts[i][s] = v + +what = ["INDIC_SYLLABIC_CATEGORY", "INDIC_MATRA_CATEGORY"] +what_short = ["ISC", "IMC"] +for i in range (2): + print + vv = values[i].keys () + vv.sort () + for v in vv: + v_no_and = v.replace ('_And_', '_') + if v in short[i]: + s = short[i][v] + else: + s = ''.join ([c for c in v_no_and if ord ('A') <= ord (c) <= ord ('Z')]) + if s in all_shorts[i]: + raise Exception ("Duplicate short value alias", v, all_shorts[i][s]) + all_shorts[i][s] = v + short[i][v] = s + print "#define %s_%s %s_%s %s/* %3d chars; %s */" % \ + (what_short[i], s, what[i], v.upper (), \ + ' '* ((48-1 - len (what[i]) - 1 - len (v)) / 8), \ + values[i][v], v) +print +print "#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)" +print +print + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, data): + global total, used, last_block + if block and block != last_block: + print + print + print " /* %s */" % block + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 8 == 0: + print + print " /* %04X */" % u, + if u in data: + num += 1 + d = data.get (u, defaults) + sys.stdout.write ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]]))) + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = data.keys () +uu.sort () + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +print "static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = {" +for u in uu: + if u <= last: + continue + block = data[u][2] + + start = u//8*8 + end = start+1 + while end in uu and block == data[end][2]: + end += 1 + end = (end-1)//8*8 + 7 + + if start != last + 1: + if start - last <= 1+16*3: + print_block (None, last+1, start-1, data) + last = start-1 + else: + if last >= 0: + ends.append (last + 1) + offset += ends[-1] - starts[-1] + print + print + print "#define indic_offset_0x%04xu %d" % (start, offset) + starts.append (start) + + print_block (block, start, end, data) + last = end +ends.append (last + 1) +offset += ends[-1] - starts[-1] +print +print +occupancy = used * 100. / total +page_bits = 12 +print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy) +print +print "INDIC_TABLE_ELEMENT_TYPE" +print "hb_indic_get_categories (hb_codepoint_t u)" +print "{" +print " switch (u >> %d)" % page_bits +print " {" +pages = set([u>>page_bits for u in starts+ends+singles.keys()]) +for p in sorted(pages): + print " case 0x%0Xu:" % p + for (start,end) in zip (starts, ends): + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "indic_offset_0x%04xu" % start + print " if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset) + for u,d in singles.items (): + if p != u>>page_bits: continue + print " if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]]) + print " break;" + print "" +print " default:" +print " break;" +print " }" +print " return _(x,x);" +print "}" +print +print "#undef _" +for i in range (2): + print + vv = values[i].keys () + vv.sort () + for v in vv: + print "#undef %s_%s" % \ + (what_short[i], short[i][v]) +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/gfx/harfbuzz/src/gen-use-table.py b/gfx/harfbuzz/src/gen-use-table.py new file mode 100755 index 000000000..a922c92fa --- /dev/null +++ b/gfx/harfbuzz/src/gen-use-table.py @@ -0,0 +1,477 @@ +#!/usr/bin/python + +import sys + +if len (sys.argv) != 5: + print >>sys.stderr, "usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt" + sys.exit (1) + +BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"] + +files = [file (x) for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 2] +headers.append (["UnicodeData.txt does not have a header."]) + +data = [{} for f in files] +values = [{} for f in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1 if i != 2 else 2] + + for u in range (start, end + 1): + data[i][u] = t + values[i][t] = values[i].get (t, 0) + end - start + 1 + +defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block') + +# TODO Characters that are not in Unicode Indic files, but used in USE +data[0][0x034F] = defaults[0] +data[0][0x2060] = defaults[0] +for u in range (0xFE00, 0xFE0F + 1): + data[0][u] = defaults[0] + +# Merge data into one dict: +for i,v in enumerate (defaults): + values[i][v] = values[i].get (v, 0) + 1 +combined = {} +for i,d in enumerate (data): + for u,v in d.items (): + if i >= 2 and not u in combined: + continue + if not u in combined: + combined[u] = list (defaults) + combined[u][i] = v +combined = {k:v for k,v in combined.items() if v[3] not in BLACKLISTED_BLOCKS} +data = combined +del combined +num = len (data) + + +property_names = [ + # General_Category + 'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', + 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', + 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs', + # Indic_Syllabic_Category + 'Other', + 'Bindu', + 'Visarga', + 'Avagraha', + 'Nukta', + 'Virama', + 'Pure_Killer', + 'Invisible_Stacker', + 'Vowel_Independent', + 'Vowel_Dependent', + 'Vowel', + 'Consonant_Placeholder', + 'Consonant', + 'Consonant_Dead', + 'Consonant_With_Stacker', + 'Consonant_Prefixed', + 'Consonant_Preceding_Repha', + 'Consonant_Succeeding_Repha', + 'Consonant_Subjoined', + 'Consonant_Medial', + 'Consonant_Final', + 'Consonant_Head_Letter', + 'Modifying_Letter', + 'Tone_Letter', + 'Tone_Mark', + 'Gemination_Mark', + 'Cantillation_Mark', + 'Register_Shifter', + 'Syllable_Modifier', + 'Consonant_Killer', + 'Non_Joiner', + 'Joiner', + 'Number_Joiner', + 'Number', + 'Brahmi_Joining_Number', + # Indic_Positional_Category + 'Not_Applicable', + 'Right', + 'Left', + 'Visual_Order_Left', + 'Left_And_Right', + 'Top', + 'Bottom', + 'Top_And_Bottom', + 'Top_And_Right', + 'Top_And_Left', + 'Top_And_Left_And_Right', + 'Bottom_And_Right', + 'Top_And_Bottom_And_Right', + 'Overstruck', +] + +class PropertyValue(object): + def __init__(self, name_): + self.name = name_ + def __str__(self): + return self.name + def __eq__(self, other): + return self.name == (other if isinstance(other, basestring) else other.name) + def __ne__(self, other): + return not (self == other) + +property_values = {} + +for name in property_names: + value = PropertyValue(name) + assert value not in property_values + assert value not in globals() + property_values[name] = value +globals().update(property_values) + + +def is_BASE(U, UISC, UGC): + return (UISC in [Number, Consonant, Consonant_Head_Letter, + #SPEC-DRAFT Consonant_Placeholder, + Tone_Letter, + Vowel_Independent #SPEC-DRAFT + ] or + (UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial, + Consonant_Subjoined, Vowel, Vowel_Dependent])) +def is_BASE_IND(U, UISC, UGC): + #SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po) + return (UISC in [Consonant_Dead, Modifying_Letter] or + (UGC == Po and not U in [0x104E, 0x2022]) or + False # SPEC-DRAFT-OUTDATED! U == 0x002D + ) +def is_BASE_NUM(U, UISC, UGC): + return UISC == Brahmi_Joining_Number +def is_BASE_OTHER(U, UISC, UGC): + if UISC == Consonant_Placeholder: return True #SPEC-DRAFT + #SPEC-DRAFT return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC, 0x25FB, 0x25FC, 0x25FD, 0x25FE] + return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE] +def is_CGJ(U, UISC, UGC): + return U == 0x034F +def is_CONS_FINAL(U, UISC, UGC): + return ((UISC == Consonant_Final and UGC != Lo) or + UISC == Consonant_Succeeding_Repha) +def is_CONS_FINAL_MOD(U, UISC, UGC): + #SPEC-DRAFT return UISC in [Consonant_Final_Modifier, Syllable_Modifier] + return UISC == Syllable_Modifier +def is_CONS_MED(U, UISC, UGC): + return UISC == Consonant_Medial and UGC != Lo +def is_CONS_MOD(U, UISC, UGC): + return UISC in [Nukta, Gemination_Mark, Consonant_Killer] +def is_CONS_SUB(U, UISC, UGC): + #SPEC-DRAFT return UISC == Consonant_Subjoined + return UISC == Consonant_Subjoined and UGC != Lo +def is_HALANT(U, UISC, UGC): + return UISC in [Virama, Invisible_Stacker] +def is_HALANT_NUM(U, UISC, UGC): + return UISC == Number_Joiner +def is_ZWNJ(U, UISC, UGC): + return UISC == Non_Joiner +def is_ZWJ(U, UISC, UGC): + return UISC == Joiner +def is_Word_Joiner(U, UISC, UGC): + return U == 0x2060 +def is_OTHER(U, UISC, UGC): + #SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters + return (UISC == Other + and not is_SYM_MOD(U, UISC, UGC) + and not is_CGJ(U, UISC, UGC) + and not is_Word_Joiner(U, UISC, UGC) + and not is_VARIATION_SELECTOR(U, UISC, UGC) + ) +def is_Reserved(U, UISC, UGC): + return UGC == 'Cn' +def is_REPHA(U, UISC, UGC): + #return UISC == Consonant_Preceding_Repha + #SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed + return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed] +def is_SYM(U, UISC, UGC): + if U == 0x25CC: return False #SPEC-DRAFT + #SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter + return UGC in [So, Sc] +def is_SYM_MOD(U, UISC, UGC): + return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73] +def is_VARIATION_SELECTOR(U, UISC, UGC): + return 0xFE00 <= U <= 0xFE0F +def is_VOWEL(U, UISC, UGC): + return (UISC == Pure_Killer or + (UGC != Lo and UISC in [Vowel, Vowel_Dependent])) +def is_VOWEL_MOD(U, UISC, UGC): + return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or + (UGC != Lo and UISC == Bindu)) + +use_mapping = { + 'B': is_BASE, + 'IND': is_BASE_IND, + 'N': is_BASE_NUM, + 'GB': is_BASE_OTHER, + 'CGJ': is_CGJ, + 'F': is_CONS_FINAL, + 'FM': is_CONS_FINAL_MOD, + 'M': is_CONS_MED, + 'CM': is_CONS_MOD, + 'SUB': is_CONS_SUB, + 'H': is_HALANT, + 'HN': is_HALANT_NUM, + 'ZWNJ': is_ZWNJ, + 'ZWJ': is_ZWJ, + 'WJ': is_Word_Joiner, + 'O': is_OTHER, + 'Rsv': is_Reserved, + 'R': is_REPHA, + 'S': is_SYM, + 'SM': is_SYM_MOD, + 'VS': is_VARIATION_SELECTOR, + 'V': is_VOWEL, + 'VM': is_VOWEL_MOD, +} + +use_positions = { + 'F': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Right], + }, + 'M': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Right], + 'Pre': [Left], + }, + 'CM': { + 'Abv': [Top], + 'Blw': [Bottom], + }, + 'V': { + 'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right], + 'Blw': [Bottom, Overstruck, Bottom_And_Right], + 'Pst': [Right], + 'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right], + }, + 'VM': { + 'Abv': [Top], + 'Blw': [Bottom, Overstruck], + 'Pst': [Right], + 'Pre': [Left], + }, + 'SM': { + 'Abv': [Top], + 'Blw': [Bottom], + }, + 'H': None, + 'B': None, + 'FM': None, + 'SUB': None, +} + +def map_to_use(data): + out = {} + items = use_mapping.items() + for U,(UISC,UIPC,UGC,UBlock) in data.items(): + + # Resolve Indic_Syllabic_Category + + # TODO: These don't have UISC assigned in Unicode 8.0, but + # have UIPC + if U == 0x17DD: UISC = Vowel_Dependent + if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark + + # TODO: U+1CED should only be allowed after some of + # the nasalization marks, maybe only for U+1CE9..U+1CF1. + if U == 0x1CED: UISC = Tone_Mark + + evals = [(k, v(U,UISC,UGC)) for k,v in items] + values = [k for k,v in evals if v] + assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values) + USE = values[0] + + # Resolve Indic_Positional_Category + + # TODO: Not in Unicode 8.0 yet, but in spec. + if U == 0x1B6C: UIPC = Bottom + + # TODO: These should die, but have UIPC in Unicode 8.0 + if U in [0x953, 0x954]: UIPC = Not_Applicable + + # TODO: In USE's override list but not in Unicode 8.0 + if U == 0x103C: UIPC = Left + + # TODO: These are not in USE's override list that we have, nor are they in Unicode 8.0 + if 0xA926 <= U <= 0xA92A: UIPC = Top + if U == 0x111CA: UIPC = Bottom + if U == 0x11300: UIPC = Top + if U == 0x1133C: UIPC = Bottom + if U == 0x1171E: UIPC = Left # Correct?! + if 0x1CF2 <= U <= 0x1CF3: UIPC = Right + if 0x1CF8 <= U <= 0x1CF9: UIPC = Top + + assert (UIPC in [Not_Applicable, Visual_Order_Left] or + USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC) + + pos_mapping = use_positions.get(USE, None) + if pos_mapping: + values = [k for k,v in pos_mapping.items() if v and UIPC in v] + assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values) + USE = USE + values[0] + + out[U] = (USE, UBlock) + return out + +defaults = ('O', 'No_Block') +data = map_to_use(data) + +# Remove the outliers +singles = {} +for u in [0x034F, 0x25CC, 0x1107F]: + singles[u] = data[u] + del data[u] + +print "/* == Start of generated table == */" +print "/*" +print " * The following table is generated by running:" +print " *" +print " * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt" +print " *" +print " * on files with these headers:" +print " *" +for h in headers: + for l in h: + print " * %s" % (l.strip()) +print " */" +print +print '#include "hb-ot-shape-complex-use-private.hh"' +print + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, data): + global total, used, last_block + if block and block != last_block: + print + print + print " /* %s */" % block + if start % 16: + print ' ' * (20 + (start % 16 * 6)), + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 16 == 0: + print + print " /* %04X */" % u, + if u in data: + num += 1 + d = data.get (u, defaults) + sys.stdout.write ("%6s," % d[0]) + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = data.keys () +uu.sort () + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +for k,v in sorted(use_mapping.items()): + if k in use_positions and use_positions[k]: continue + print "#define %s USE_%s /* %s */" % (k, k, v.__name__[3:]) +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print "#define %s USE_%s" % (tag, tag) +print "" +print "static const USE_TABLE_ELEMENT_TYPE use_table[] = {" +for u in uu: + if u <= last: + continue + block = data[u][1] + + start = u//8*8 + end = start+1 + while end in uu and block == data[end][1]: + end += 1 + end = (end-1)//8*8 + 7 + + if start != last + 1: + if start - last <= 1+16*3: + print_block (None, last+1, start-1, data) + last = start-1 + else: + if last >= 0: + ends.append (last + 1) + offset += ends[-1] - starts[-1] + print + print + print "#define use_offset_0x%04xu %d" % (start, offset) + starts.append (start) + + print_block (block, start, end, data) + last = end +ends.append (last + 1) +offset += ends[-1] - starts[-1] +print +print +occupancy = used * 100. / total +page_bits = 12 +print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy) +print +print "USE_TABLE_ELEMENT_TYPE" +print "hb_use_get_categories (hb_codepoint_t u)" +print "{" +print " switch (u >> %d)" % page_bits +print " {" +pages = set([u>>page_bits for u in starts+ends+singles.keys()]) +for p in sorted(pages): + print " case 0x%0Xu:" % p + for (start,end) in zip (starts, ends): + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "use_offset_0x%04xu" % start + print " if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset) + for u,d in singles.items (): + if p != u>>page_bits: continue + print " if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0]) + print " break;" + print "" +print " default:" +print " break;" +print " }" +print " return USE_O;" +print "}" +print +for k in sorted(use_mapping.keys()): + if k in use_positions and use_positions[k]: continue + print "#undef %s" % k +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print "#undef %s" % tag +print +print "/* == End of generated table == */" + +# Maintain at least 50% occupancy in the table */ +if occupancy < 50: + raise Exception ("Table too sparse, please investigate: ", occupancy) diff --git a/gfx/harfbuzz/src/harfbuzz-gobject.pc.in b/gfx/harfbuzz/src/harfbuzz-gobject.pc.in new file mode 100644 index 000000000..700836019 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-gobject.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library GObject integration +Version: %VERSION% + +Requires: harfbuzz gobject-2.0 glib-2.0 +Libs: -L${libdir} -lharfbuzz-gobject +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz-icu.pc b/gfx/harfbuzz/src/harfbuzz-icu.pc new file mode 100644 index 000000000..7b304421d --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-icu.pc @@ -0,0 +1,13 @@ +prefix=/usr/local +exec_prefix=/usr/local +libdir=/usr/local/lib +includedir=/usr/local/include + +Name: harfbuzz +Description: HarfBuzz text shaping library ICU integration +Version: 1.4.1 + +Requires: harfbuzz +Requires.private: icu-uc +Libs: -L${libdir} -lharfbuzz-icu +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz-icu.pc.in b/gfx/harfbuzz/src/harfbuzz-icu.pc.in new file mode 100644 index 000000000..949869a35 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-icu.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library ICU integration +Version: %VERSION% + +Requires: harfbuzz +Requires.private: icu-uc +Libs: -L${libdir} -lharfbuzz-icu +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz.pc b/gfx/harfbuzz/src/harfbuzz.pc new file mode 100644 index 000000000..0c79d3dac --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz.pc @@ -0,0 +1,13 @@ +prefix=/usr/local +exec_prefix=/usr/local +libdir=/usr/local/lib +includedir=/usr/local/include + +Name: harfbuzz +Description: HarfBuzz text shaping library +Version: 1.4.1 + +Libs: -L${libdir} -lharfbuzz +Libs.private: +Requires.private: glib-2.0 >= 2.19.1 +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz.pc.in b/gfx/harfbuzz/src/harfbuzz.pc.in new file mode 100644 index 000000000..b3e124aa8 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library +Version: %VERSION% + +Libs: -L${libdir} -lharfbuzz +Libs.private: %libs_private% +Requires.private: %requires_private% +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/hb-atomic-private.hh b/gfx/harfbuzz/src/hb-atomic-private.hh new file mode 100644 index 000000000..100ba539e --- /dev/null +++ b/gfx/harfbuzz/src/hb-atomic-private.hh @@ -0,0 +1,189 @@ +/* + * 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 defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) + +#include <windows.h> + +/* MinGW has a convoluted history of supporting MemoryBarrier + * properly. As such, define a function to wrap the whole + * thing. */ +static inline void _HBMemoryBarrier (void) { +#if !defined(MemoryBarrier) + long dummy = 0; + InterlockedExchange (&dummy, 1); +#else + MemoryBarrier (); +#endif +} + +typedef LONG hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd (&(AI), (V)) + +#define hb_atomic_ptr_impl_get(P) (_HBMemoryBarrier (), (void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) + + +#elif !defined(HB_NO_MT) && defined(__APPLE__) + +#include <libkern/OSAtomic.h> +#ifdef __MAC_OS_X_MIN_REQUIRED +#include <AvailabilityMacros.h> +#elif defined(__IPHONE_OS_MIN_REQUIRED) +#include <Availability.h> +#endif + + +typedef int32_t hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) + +#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) +#else +#if __ppc64__ || __x86_64__ || __aarch64__ +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P)) +#else +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P)) +#endif +#endif + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) + +typedef int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add (&(AI), (V)) + +#define hb_atomic_ptr_impl_get(P) (void *) (__sync_synchronize (), *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) + + +#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) + +#include <atomic.h> +#include <mbarrier.h> + +typedef unsigned int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) ( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ( ({__machine_rw_barrier ();}), (void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) ( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false) + + +#elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__) + +#include <builtins.h> + + +static inline int hb_fetch_and_add(volatile int* AI, unsigned int V) { + __lwsync(); + int result = __fetch_and_add(AI, V); + __isync(); + return result; +} +static inline int hb_compare_and_swaplp(volatile long* P, long O, long N) { + __sync(); + int result = __compare_and_swaplp (P, &O, N); + __sync(); + return result; +} + +typedef int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) hb_fetch_and_add (&(AI), (V)) + +#define hb_atomic_ptr_impl_get(P) (__sync(), (void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(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_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) +#define hb_atomic_ptr_impl_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_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + + +#endif + + +#define HB_ATOMIC_INT_INIT(V) {HB_ATOMIC_INT_IMPL_INIT(V)} + +struct hb_atomic_int_t +{ + hb_atomic_int_impl_t v; + + inline void set_unsafe (int v_) { v = v_; } + inline int get_unsafe (void) const { return v; } + inline int inc (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v), 1); } + inline int dec (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v), -1); } +}; + + +#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P) +#define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N)) + + +#endif /* HB_ATOMIC_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-blob.cc b/gfx/harfbuzz/src/hb-blob.cc new file mode 100644 index 000000000..fb48f03ca --- /dev/null +++ b/gfx/harfbuzz/src/hb-blob.cc @@ -0,0 +1,478 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif + +#include "hb-private.hh" + +#include "hb-object-private.hh" + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <sys/mman.h> +#endif /* HAVE_SYS_MMAN_H */ + +#include <stdio.h> +#include <errno.h> + + + +#ifndef HB_DEBUG_BLOB +#define HB_DEBUG_BLOB (HB_DEBUG+0) +#endif + + +struct hb_blob_t { + hb_object_header_t header; + ASSERT_POD (); + + bool immutable; + + const char *data; + unsigned int length; + hb_memory_mode_t mode; + + void *user_data; + hb_destroy_func_t destroy; +}; + + +static bool _try_writable (hb_blob_t *blob); + +static void +_hb_blob_destroy_user_data (hb_blob_t *blob) +{ + if (blob->destroy) { + blob->destroy (blob->user_data); + blob->user_data = NULL; + blob->destroy = NULL; + } +} + +/** + * hb_blob_create: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Return value: New blob, or the empty blob if something failed or if @length is + * zero. Destroy with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (!length || + length >= 1u << 31 || + !(blob = hb_object_create<hb_blob_t> ())) { + if (destroy) + destroy (user_data); + return hb_blob_get_empty (); + } + + blob->data = data; + blob->length = length; + blob->mode = mode; + + blob->user_data = user_data; + blob->destroy = destroy; + + if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { + blob->mode = HB_MEMORY_MODE_READONLY; + if (!_try_writable (blob)) { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + return blob; +} + +/** + * hb_blob_create_sub_blob: + * @parent: Parent blob. + * @offset: Start offset of sub-blob within @parent, in bytes. + * @length: Length of sub-blob. + * + * Returns a blob that represents a range of bytes in @parent. The new + * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it + * will never modify data in the parent blob. The parent data is not + * expected to be modified, and will result in undefined behavior if it + * is. + * + * Makes @parent immutable. + * + * Return value: New blob, or the empty blob if something failed or if + * @length is zero or @offset is beyond the end of @parent's data. Destroy + * with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length) +{ + hb_blob_t *blob; + + if (!length || offset >= parent->length) + return hb_blob_get_empty (); + + hb_blob_make_immutable (parent); + + blob = hb_blob_create (parent->data + offset, + MIN (length, parent->length - offset), + HB_MEMORY_MODE_READONLY, + hb_blob_reference (parent), + (hb_destroy_func_t) hb_blob_destroy); + + return blob; +} + +/** + * hb_blob_get_empty: + * + * Returns the singleton empty blob. + * + * See TODO:link object types for more information. + * + * Return value: (transfer full): the empty blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_get_empty (void) +{ + 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_reference: (skip) + * @blob: a blob. + * + * Increases the reference count on @blob. + * + * See TODO:link object types for more information. + * + * Return value: @blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_reference (hb_blob_t *blob) +{ + return hb_object_reference (blob); +} + +/** + * hb_blob_destroy: (skip) + * @blob: a blob. + * + * Descreases the reference count on @blob, and if it reaches zero, destroys + * @blob, freeing all memory, possibly calling the destroy-callback the blob + * was created for if it has not been called already. + * + * See TODO:link object types for more information. + * + * Since: 0.9.2 + **/ +void +hb_blob_destroy (hb_blob_t *blob) +{ + if (!hb_object_destroy (blob)) return; + + _hb_blob_destroy_user_data (blob); + + free (blob); +} + +/** + * hb_blob_set_user_data: (skip) + * @blob: a blob. + * @key: key for data to set. + * @data: data to set. + * @destroy: callback to call when @data is not needed anymore. + * @replace: whether to replace an existing data with the same key. + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (blob, key, data, destroy, replace); +} + +/** + * hb_blob_get_user_data: (skip) + * @blob: a blob. + * @key: key for data to get. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (blob, key); +} + + +/** + * hb_blob_make_immutable: + * @blob: a blob. + * + * + * + * Since: 0.9.2 + **/ +void +hb_blob_make_immutable (hb_blob_t *blob) +{ + if (hb_object_is_inert (blob)) + return; + + blob->immutable = true; +} + +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * + * + * Return value: TODO + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob) +{ + return blob->immutable; +} + + +/** + * hb_blob_get_length: + * @blob: a blob. + * + * + * + * Return value: the length of blob data in bytes. + * + * Since: 0.9.2 + **/ +unsigned int +hb_blob_get_length (hb_blob_t *blob) +{ + return blob->length; +} + +/** + * hb_blob_get_data: + * @blob: a blob. + * @length: (out): + * + * + * + * Returns: (transfer none) (array length=length): + * + * Since: 0.9.2 + **/ +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length) +{ + if (length) + *length = blob->length; + + return blob->data; +} + +/** + * hb_blob_get_data_writable: + * @blob: a blob. + * @length: (out): output length of the writable data. + * + * Tries to make blob data writable (possibly copying it) and + * return pointer to data. + * + * Fails if blob has been made immutable, or if memory allocation + * fails. + * + * Returns: (transfer none) (array length=length): Writable blob data, + * or %NULL if failed. + * + * Since: 0.9.2 + **/ +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) +{ + if (!_try_writable (blob)) { + if (length) + *length = 0; + + return NULL; + } + + if (length) + *length = blob->length; + + return const_cast<char *> (blob->data); +} + + +static hb_bool_t +_try_make_writable_inplace_unix (hb_blob_t *blob) +{ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + uintptr_t pagesize = -1, mask, length; + const char *addr; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + if ((uintptr_t) -1L == pagesize) { + DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); + return false; + } + DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); + + mask = ~(pagesize-1); + addr = (const char *) (((uintptr_t) blob->data) & mask); + length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, blob, + "calling mprotect on [%p..%p] (%lu bytes)", + 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; + } + + blob->mode = HB_MEMORY_MODE_WRITABLE; + + DEBUG_MSG_FUNC (BLOB, blob, + "successfully made [%p..%p] (%lu bytes) writable\n", + addr, addr+length, (unsigned long) length); + return true; +#else + return false; +#endif +} + +static bool +_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; + + DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); + + /* Failed to make writable inplace, mark that */ + blob->mode = HB_MEMORY_MODE_READONLY; + return false; +} + +static bool +_try_writable (hb_blob_t *blob) +{ + if (blob->immutable) + return false; + + if (blob->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) + return true; + + if (blob->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + + 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; + + DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); + + memcpy (new_data, blob->data, blob->length); + _hb_blob_destroy_user_data (blob); + blob->mode = HB_MEMORY_MODE_WRITABLE; + blob->data = new_data; + blob->user_data = new_data; + blob->destroy = free; + + return true; +} diff --git a/gfx/harfbuzz/src/hb-blob.h b/gfx/harfbuzz/src/hb-blob.h new file mode 100644 index 000000000..ef3fc98c0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-blob.h @@ -0,0 +1,126 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_BLOB_H +#define HB_BLOB_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/* + * Note re various memory-modes: + * + * - In no case shall the HarfBuzz client modify memory + * that is passed to HarfBuzz in a blob. If there is + * any such possibility, MODE_DUPLICATE should be used + * such that HarfBuzz makes a copy immediately, + * + * - Use MODE_READONLY otherse, unless you really really + * really know what you are doing, + * + * - MODE_WRITABLE is appropriate if you really made a + * copy of data solely for the purpose of passing to + * HarfBuzz and doing that just once (no reuse!), + * + * - If the font is mmap()ed, it's ok to use + * READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use MODE_READONLY instead. + */ +typedef enum { + HB_MEMORY_MODE_DUPLICATE, + HB_MEMORY_MODE_READONLY, + HB_MEMORY_MODE_WRITABLE, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE +} hb_memory_mode_t; + +typedef struct hb_blob_t hb_blob_t; + +HB_EXTERN hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +/* Always creates with MEMORY_MODE_READONLY. + * Even if the parent blob is writable, we don't + * want the user of the sub-blob to be able to + * modify the parent data as that data may be + * shared among multiple sub-blobs. + */ +HB_EXTERN hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length); + +HB_EXTERN hb_blob_t * +hb_blob_get_empty (void); + +HB_EXTERN hb_blob_t * +hb_blob_reference (hb_blob_t *blob); + +HB_EXTERN void +hb_blob_destroy (hb_blob_t *blob); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_blob_make_immutable (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob); + + +HB_EXTERN unsigned int +hb_blob_get_length (hb_blob_t *blob); + +HB_EXTERN const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length); + +HB_EXTERN char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); + + +HB_END_DECLS + +#endif /* HB_BLOB_H */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh b/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh new file mode 100644 index 000000000..3f626bda4 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -0,0 +1,643 @@ + +#line 1 "hb-buffer-deserialize-json.rl" +/* + * Copyright © 2013 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_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb-private.hh" + + +#line 36 "hb-buffer-deserialize-json.hh" +static const unsigned char _deserialize_json_trans_keys[] = { + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 +}; + +static const char _deserialize_json_key_spans[] = { + 0, 115, 26, 7, 2, 1, 50, 49, + 10, 117, 117, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 50, 49, + 58, 89, 117, 117, 85, 115, 0 +}; + +static const short _deserialize_json_index_offsets[] = { + 0, 0, 116, 143, 151, 154, 156, 207, + 257, 268, 386, 504, 622, 624, 675, 725, + 736, 854, 972, 974, 976, 1027, 1077, 1195, + 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, + 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, + 2119, 2178, 2268, 2386, 2504, 2590, 2706 +}; + +static const char _deserialize_json_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, + 5, 1, 6, 7, 1, 1, 8, 1, + 9, 10, 1, 11, 1, 11, 11, 11, + 11, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 12, 12, 12, 12, 12, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 12, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 1, 16, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 1, 18, 18, 18, + 18, 18, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 20, 1, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 18, 18, 18, 18, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 20, 1, 23, + 1, 23, 23, 23, 23, 23, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 1, 24, 24, 24, 24, + 24, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 24, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 25, 1, 1, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 1, 28, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 1, 30, 30, 30, 30, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 31, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 32, 1, 30, + 30, 30, 30, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 31, 1, 1, 1, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 32, 1, 33, 1, 34, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 35, 35, 35, 35, + 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 38, 38, 38, 38, + 38, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 40, 1, 42, 43, 1, 44, 1, 44, + 44, 44, 44, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 44, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 45, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 1, 49, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 51, + 51, 51, 51, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 51, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 51, 51, 51, + 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 54, 1, 54, 54, 54, + 54, 54, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 54, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 1, 1, 57, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 1, 59, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 61, 61, 61, + 61, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 62, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 63, 1, 61, 61, 61, 61, 61, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 61, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 62, 1, + 1, 1, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 63, + 1, 64, 1, 64, 64, 64, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 64, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 65, 1, 65, 65, + 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 65, 1, 66, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 67, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 1, 1, 1, 1, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 70, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 71, 71, + 1, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 1, 1, 1, 1, 1, + 1, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 1, 1, 1, + 71, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 72, 72, 72, + 72, 72, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 72, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 73, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 74, 1, 72, 72, 72, 72, 72, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 72, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 73, 1, + 1, 1, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 74, + 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 77, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 78, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 0 +}; + +static const char _deserialize_json_trans_targs[] = { + 1, 0, 2, 2, 3, 4, 18, 24, + 37, 5, 12, 6, 7, 8, 9, 11, + 9, 11, 10, 2, 44, 10, 44, 13, + 14, 15, 16, 17, 16, 17, 10, 2, + 44, 19, 20, 21, 22, 23, 10, 2, + 44, 23, 25, 31, 26, 27, 28, 29, + 30, 29, 30, 10, 2, 44, 32, 33, + 34, 35, 36, 35, 36, 10, 2, 44, + 38, 39, 40, 42, 43, 41, 10, 41, + 10, 2, 44, 43, 44, 45, 46 +}; + +static const char _deserialize_json_trans_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, + 0, 0, 3, 3, 4, 0, 5, 0, + 0, 2, 2, 2, 0, 0, 6, 6, + 7, 0, 0, 0, 2, 2, 8, 8, + 9, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 10, 10, 11, 0, 0, + 2, 2, 2, 0, 0, 12, 12, 13, + 0, 0, 0, 2, 2, 2, 14, 0, + 15, 15, 16, 0, 0, 0, 0 +}; + +static const int deserialize_json_start = 1; +static const int deserialize_json_first_final = 44; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 97 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 466 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 471 "hb-buffer-deserialize-json.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_json_trans_keys + (cs<<1); + _inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs]; + + _slen = _deserialize_json_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_json_trans_targs[_trans]; + + if ( _deserialize_json_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_json_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-json.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + break; + case 5: +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 2: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} + break; + case 14: +#line 55 "hb-buffer-deserialize-json.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 15: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 16: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 624 "hb-buffer-deserialize-json.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 125 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl b/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl new file mode 100644 index 000000000..91b350f5a --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl @@ -0,0 +1,132 @@ +/* + * Copyright © 2013 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_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb-private.hh" + +%%{ + +machine deserialize_json; +alphtype unsigned char; +write data; + +action clear_item { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action parse_glyph { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + +action parse_gid { if (!parse_uint (tok, p, &info.codepoint)) return false; } +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +comma = space* ',' space*; +colon = space* ':' space*; + +glyph_id = unum; +glyph_name = alpha (alnum|'_'|'.'|'-')*; + +glyph_string = '"' (glyph_name >tok %parse_glyph) '"'; +glyph_number = (glyph_id >tok %parse_gid); + +glyph = "\"g\"" colon (glyph_string | glyph_number); +cluster = "\"cl\"" colon (unum >tok %parse_cluster); +xoffset = "\"dx\"" colon (num >tok %parse_x_offset); +yoffset = "\"dy\"" colon (num >tok %parse_y_offset); +xadvance= "\"ax\"" colon (num >tok %parse_x_advance); +yadvance= "\"ay\"" colon (num >tok %parse_y_advance); + +element = glyph | cluster | xoffset | yoffset | xadvance | yadvance; +item = + ( '{' space* element (comma element)* space* '}') + >clear_item + @add_item + ; + +main := space* item (comma item)* space* (','|']')?; + +}%% + +static hb_bool_t +_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-text.hh b/gfx/harfbuzz/src/hb-buffer-deserialize-text.hh new file mode 100644 index 000000000..d2d8daae7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text.hh @@ -0,0 +1,571 @@ + +#line 1 "hb-buffer-deserialize-text.rl" +/* + * Copyright © 2013 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_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb-private.hh" + + +#line 36 "hb-buffer-deserialize-text.hh" +static const unsigned char _deserialize_text_trans_keys[] = { + 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, + 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_key_spans[] = { + 0, 114, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 116, 116, 0, + 114, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_index_offsets[] = { + 0, 0, 115, 129, 140, 154, 165, 176, + 190, 201, 203, 217, 228, 243, 360, 477, + 478, 593, 710, 827, 944, 1061, 1178, 1295, + 1412, 1529, 1646 +}; + +static const char _deserialize_text_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 5, 1, 1, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 1, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 1, 10, 1, 1, + 11, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 13, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 1, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 1, 17, 1, 1, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 20, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 1, 22, 1, 23, 1, 1, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 1, 26, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 1, 22, 1, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 1, 28, 28, 28, 28, + 28, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 31, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 32, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 33, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 35, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 36, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 28, 28, 28, 28, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 28, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 29, 1, 1, 1, + 1, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 1, 1, 1, 30, 1, + 1, 31, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 32, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 33, 1, 38, + 38, 38, 38, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 39, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 40, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 42, 42, 42, 42, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 44, + 1, 42, 42, 42, 42, 42, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 1, 45, 45, 45, 45, 45, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 46, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 48, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, + 50, 50, 50, 50, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 52, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 51, + 1, 1, 1, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 52, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 53, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 46, 1, 1, 1, + 1, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 1, 1, 1, 1, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 48, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 49, 1, 28, + 28, 28, 28, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 29, 1, 55, 55, 1, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 1, 1, 1, 30, 1, 1, 31, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 1, 32, 1, 55, 1, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 33, 1, 0 +}; + +static const char _deserialize_text_trans_targs[] = { + 1, 0, 13, 17, 26, 3, 18, 21, + 18, 21, 5, 19, 20, 19, 20, 22, + 25, 8, 9, 12, 9, 12, 10, 11, + 23, 24, 23, 24, 14, 2, 6, 7, + 15, 16, 14, 15, 16, 17, 14, 4, + 15, 16, 14, 15, 16, 14, 2, 7, + 15, 16, 14, 2, 15, 16, 25, 26 +}; + +static const char _deserialize_text_trans_actions[] = { + 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 2, 2, 2, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 3, 2, + 2, 2, 0, 0, 4, 5, 5, 5, + 4, 4, 0, 0, 0, 0, 6, 7, + 6, 6, 8, 8, 8, 9, 10, 10, + 9, 9, 11, 12, 11, 11, 0, 0 +}; + +static const char _deserialize_text_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, + 0, 4, 6, 8, 8, 6, 9, 11, + 11, 9, 4 +}; + +static const int deserialize_text_start = 1; +static const int deserialize_text_first_final = 13; +static const int deserialize_text_error = 0; + +static const int deserialize_text_en_main = 1; + + +#line 91 "hb-buffer-deserialize-text.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + { + *end_ptr = ++p; + } + + const char *eof = pe, *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 343 "hb-buffer-deserialize-text.hh" + { + cs = deserialize_text_start; + } + +#line 348 "hb-buffer-deserialize-text.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_trans_keys + (cs<<1); + _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; + + _slen = _deserialize_text_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_trans_targs[_trans]; + + if ( _deserialize_text_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_trans_actions[_trans] ) { + case 2: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 5: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 10: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 3: +#line 63 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 7: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 1: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 480 "hb-buffer-deserialize-text.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_eof_actions[cs] ) { + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 557 "hb-buffer-deserialize-text.hh" + } + } + + _out: {} + } + +#line 119 "hb-buffer-deserialize-text.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-text.rl b/gfx/harfbuzz/src/hb-buffer-deserialize-text.rl new file mode 100644 index 000000000..8a682f737 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text.rl @@ -0,0 +1,126 @@ +/* + * Copyright © 2013 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_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb-private.hh" + +%%{ + +machine deserialize_text; +alphtype unsigned char; +write data; + +action clear_item { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action parse_glyph { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +glyph_id = unum; +glyph_name = alpha (alnum|'_'|'.'|'-')*; + +glyph = (glyph_id | glyph_name) >tok %parse_glyph; +cluster = '=' (unum >tok %parse_cluster); +offsets = '@' (num >tok %parse_x_offset) ',' (num >tok %parse_y_offset ); +advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?; +item = + ( + glyph + cluster? + offsets? + advances? + ) + >clear_item + %add_item + ; + +main := space* item (space* '|' space* item)* space* ('|'|']')?; + +}%% + +static hb_bool_t +_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + { + *end_ptr = ++p; + } + + const char *eof = pe, *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-private.hh b/gfx/harfbuzz/src/hb-buffer-private.hh new file mode 100644 index 000000000..bca308da2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-private.hh @@ -0,0 +1,296 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_PRIVATE_HH +#define HB_BUFFER_PRIVATE_HH + +#include "hb-private.hh" +#include "hb-object-private.hh" +#include "hb-unicode-private.hh" + + +#ifndef HB_BUFFER_MAX_EXPANSION_FACTOR +#define HB_BUFFER_MAX_EXPANSION_FACTOR 32 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 8192 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); +ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)); + +HB_MARK_AS_FLAG_T (hb_buffer_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); + +enum hb_buffer_scratch_flags_t { + HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, + HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, + HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, + /* Reserved for complex shapers' internal use. */ + HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX2 = 0x04000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX3 = 0x08000000u, +}; +HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t { + hb_object_header_t header; + ASSERT_POD (); + + /* Information about how the text in the buffer should be treated */ + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + hb_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */ + unsigned int max_len; /* Maximum allowed len. */ + + /* Buffer contents */ + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool in_error; /* Allocation failed */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + 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 ? out_len - 1 : 0]; } + inline hb_glyph_info_t prev (void) const { return out_info[out_len ? out_len - 1 : 0]; } + + inline bool has_separate_output (void) const { return info != out_info; } + + unsigned int serial; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static const unsigned int CONTEXT_LENGTH = 5; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + /* Debugging API */ + hb_buffer_message_func_t message_func; + void *message_data; + hb_destroy_func_t message_destroy; + + /* Internal debugging. */ + /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ +#ifndef HB_NDEBUG + uint8_t allocated_var_bits; +#endif + inline void allocate_var (unsigned int start, unsigned int count) + { +#ifndef HB_NDEBUG + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<<end) - (1u<<start); + assert (0 == (allocated_var_bits & bits)); + allocated_var_bits |= bits; +#endif + } + inline void deallocate_var (unsigned int start, unsigned int count) + { +#ifndef HB_NDEBUG + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<<end) - (1u<<start); + assert (bits == (allocated_var_bits & bits)); + allocated_var_bits &= ~bits; +#endif + } + inline void assert_var (unsigned int start, unsigned int count) + { +#ifndef HB_NDEBUG + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<<end) - (1u<<start); + assert (bits == (allocated_var_bits & bits)); +#endif + } + inline void deallocate_var_all (void) + { +#ifndef HB_NDEBUG + allocated_var_bits = 0; +#endif + } + + + /* Methods */ + + HB_INTERNAL void reset (void); + HB_INTERNAL void clear (void); + + inline unsigned int backtrack_len (void) const + { return have_output? out_len : idx; } + inline unsigned int lookahead_len (void) const + { return len - idx; } + inline unsigned int next_serial (void) { return serial++; } + + HB_INTERNAL void add (hb_codepoint_t codepoint, + unsigned int cluster); + HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info); + + 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_segment_properties (void); + + HB_INTERNAL void swap_buffers (void); + HB_INTERNAL void remove_output (void); + HB_INTERNAL void clear_output (void); + HB_INTERNAL void clear_positions (void); + + 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); + HB_INTERNAL void output_info (const hb_glyph_info_t &glyph_info); + /* Copies glyph at idx to output but doesn't advance idx */ + HB_INTERNAL void copy_glyph (void); + HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */ + /* Copies glyph at idx to output and advance idx. + * If there's no output, just advance idx. */ + inline void + next_glyph (void) + { + if (have_output) + { + if (unlikely (out_info != info || out_len != idx)) { + if (unlikely (!make_room_for (1, 1))) return; + out_info[out_len] = info[idx]; + } + out_len++; + } + + idx++; + } + + /* Advance idx without copying to output. */ + inline void skip_glyph (void) { idx++; } + + inline void reset_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask = mask; + } + inline void add_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask |= mask; + } + HB_INTERNAL void set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end); + + HB_INTERNAL void merge_clusters (unsigned int start, + unsigned int end) + { + if (end - start < 2) + return; + merge_clusters_impl (start, end); + } + HB_INTERNAL void merge_clusters_impl (unsigned int start, + unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, + unsigned int end); + /* Merge clusters for deleting current glyph, and skip it. */ + HB_INTERNAL void delete_glyph (void); + + /* Internal methods */ + HB_INTERNAL bool enlarge (unsigned int size); + + inline bool ensure (unsigned int size) + { return likely (!size || size < allocated) ? true : enlarge (size); } + + inline bool ensure_inplace (unsigned int size) + { return likely (!size || size < allocated); } + + HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + HB_INTERNAL bool shift_forward (unsigned int count); + + typedef long scratch_buffer_t; + HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); + + inline void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); + + inline bool messaging (void) { return unlikely (message_func); } + inline bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) + { + if (!messaging ()) + return true; + va_list ap; + va_start (ap, fmt); + bool ret = message_impl (font, fmt, ap); + va_end (ap); + return ret; + } + HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); +}; + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var)) +#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) +#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) + + +#endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-serialize.cc b/gfx/harfbuzz/src/hb-buffer-serialize.cc new file mode 100644 index 000000000..63a0f3466 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc @@ -0,0 +1,454 @@ +/* + * Copyright © 2012,2013 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-buffer-private.hh" + + +static const char *serialize_formats[] = { + "text", + "json", + NULL +}; + +/** + * hb_buffer_serialize_list_formats: + * + * Returns a list of supported buffer serialization formats. + * + * Return value: (transfer none): + * A string array of buffer serialization formats. Should not be freed. + * + * Since: 0.9.7 + **/ +const char ** +hb_buffer_serialize_list_formats (void) +{ + return serialize_formats; +} + +/** + * hb_buffer_serialize_format_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is %NULL terminated + * + * Parses a string into an #hb_buffer_serialize_format_t. Does not check if + * @str is a valid buffer serialization format, use + * hb_buffer_serialize_list_formats() to get the list of supported formats. + * + * Return value: + * The parsed #hb_buffer_serialize_format_t. + * + * Since: 0.9.7 + **/ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len) +{ + /* Upper-case it. */ + return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); +} + +/** + * hb_buffer_serialize_format_to_string: + * @format: an #hb_buffer_serialize_format_t to convert. + * + * Converts @format to the string corresponding it, or %NULL if it is not a valid + * #hb_buffer_serialize_format_t. + * + * Return value: (transfer none): + * A %NULL terminated string corresponding to @format. Should not be freed. + * + * Since: 0.9.7 + **/ +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) +{ + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return NULL; + } +} + +static unsigned int +_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + +#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END + + if (i) + *p++ = ','; + + *p++ = '{'; + + APPEND ("\"g\":"); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + char g[128]; + hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); + *p++ = '"'; + for (char *q = g; *q; q++) { + if (*q == '"') + *p++ = '\\'; + *p++ = *q; + } + *p++ = '"'; + } + else + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + pos[i].x_offset, pos[i].y_offset); + p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } + + *p++ = '}'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + + if (i) + *p++ = '|'; + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + hb_font_glyph_to_string (font, info[i].codepoint, p, 128); + p += strlen (p); + } + else + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + if (pos[i].x_offset || pos[i].y_offset) + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); + + *p++ = '+'; + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } + + return end - start; +} + +/** + * hb_buffer_serialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf. + * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If %NULL, and empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its glyph content, + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized glyphs will look something like: + * + * ``` + * [uni0651=0@518,0+0|uni0628=0+1897] + * ``` + * - The serialized glyphs are delimited with `[` and `]`. + * - Glyphs are separated with `|` + * - Each glyph starts with glyph name, or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: + * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, + * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the + * #hb_glyph_extents_t in the format + * `<x_bearing,y_bearing,width,height>` + * + * ## json + * TODO. + * + * Return value: + * The number of serialized items. + * + * Since: 0.9.7 + **/ +unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (start <= end && end <= buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + + assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || + buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!buffer->have_positions) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; + + if (unlikely (start == end)) + return 0; + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_glyphs_text (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_glyphs_json (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + + +static hb_bool_t +parse_uint (const char *pp, const char *end, uint32_t *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); + strncpy (buf, pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + uint32_t v; + + errno = 0; + v = strtol (p, &pend, 10); + if (errno || p == pend || pend - p != end - pp) + return false; + + *pv = v; + return true; +} + +static hb_bool_t +parse_int (const char *pp, const char *end, int32_t *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); + strncpy (buf, pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + int32_t v; + + errno = 0; + v = strtol (p, &pend, 10); + if (errno || p == pend || pend - p != end - pp) + return false; + + *pv = v; + return true; +} + +#include "hb-buffer-deserialize-json.hh" +#include "hb-buffer-deserialize-text.hh" + +/** + * hb_buffer_deserialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): + * @buf_len: + * @end_ptr: (out): + * @font: + * @format: + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || + buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_glyphs_text (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_glyphs_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} diff --git a/gfx/harfbuzz/src/hb-buffer.cc b/gfx/harfbuzz/src/hb-buffer.cc new file mode 100644 index 000000000..3940a3dbf --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer.cc @@ -0,0 +1,1818 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-buffer-private.hh" +#include "hb-utf-private.hh" + + +#ifndef HB_DEBUG_BUFFER +#define HB_DEBUG_BUFFER (HB_DEBUG+0) +#endif + +/** + * SECTION: hb-buffer + * @title: Buffers + * @short_description: Input and output buffers + * @include: hb.h + * + * Buffers serve dual role in HarfBuzz; they hold the input characters that are + * passed hb_shape(), and after shaping they hold the output glyphs. + **/ + +/** + * hb_segment_properties_equal: + * @a: first #hb_segment_properties_t to compare. + * @b: second #hb_segment_properties_t to compare. + * + * Checks the equality of two #hb_segment_properties_t's. + * + * Return value: + * %true if all properties of @a equal those of @b, false otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language && + a->reserved1 == b->reserved1 && + a->reserved2 == b->reserved2; + +} + +/** + * hb_segment_properties_hash: + * @p: #hb_segment_properties_t to hash. + * + * Creates a hash representing @p. + * + * Return value: + * A hash of @p. + * + * Since: 0.9.7 + **/ +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + return (unsigned int) p->direction ^ + (unsigned int) p->script ^ + (intptr_t) (p->language); +} + + + +/* Here is how the buffer works internally: + * + * There are two info pointers: info and out_info. They always have + * the same allocated size, but different lengths. + * + * As an optimization, both info and out_info may point to the + * same piece of memory, which is owned by info. This remains the + * case as long as out_len doesn't exceed i at any time. + * In that case, swap_buffers() is no-op and the glyph operations operate + * mostly in-place. + * + * As soon as out_info gets longer than info, out_info is moved over + * to an alternate buffer (which we reuse the pos buffer for!), and its + * current contents (out_len entries) are copied to the new place. + * This should all remain transparent to the user. swap_buffers() then + * switches info and out_info. + */ + + + +/* Internal API */ + +bool +hb_buffer_t::enlarge (unsigned int size) +{ + if (unlikely (in_error)) + return false; + if (unlikely (size > max_len)) + { + in_error = true; + return false; + } + + unsigned int new_allocated = allocated; + hb_glyph_position_t *new_pos = NULL; + hb_glyph_info_t *new_info = NULL; + bool separate_out = out_info != info; + + if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) + goto done; + + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 32; + + ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); + if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) + goto done; + + new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); + new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); + +done: + if (unlikely (!new_pos || !new_info)) + in_error = true; + + if (likely (new_pos)) + pos = new_pos; + + if (likely (new_info)) + info = new_info; + + out_info = separate_out ? (hb_glyph_info_t *) pos : info; + if (likely (!in_error)) + allocated = new_allocated; + + return likely (!in_error); +} + +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 (out_info == info && + out_len + num_out > idx + num_in) + { + assert (have_output); + + out_info = (hb_glyph_info_t *) pos; + memcpy (out_info, info, out_len * sizeof (out_info[0])); + } + + return true; +} + +bool +hb_buffer_t::shift_forward (unsigned int count) +{ + assert (have_output); + if (unlikely (!ensure (len + count))) return false; + + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); + if (idx + count > len) + { + /* Under memory failure we might expose this area. At least + * clean it up. Oh well... */ + memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + } + len += count; + idx += count; + + return true; +} + +hb_buffer_t::scratch_buffer_t * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; + + assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); + *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); + return (scratch_buffer_t *) (void *) pos; +} + + + +/* HarfBuzz-Internal API */ + +void +hb_buffer_t::reset (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_get_default (); + flags = HB_BUFFER_FLAG_DEFAULT; + replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + clear (); +} + +void +hb_buffer_t::clear (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; + props = default_props; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + in_error = false; + have_output = false; + have_positions = false; + + idx = 0; + len = 0; + out_len = 0; + out_info = info; + + serial = 0; + + memset (context, 0, sizeof context); + memset (context_len, 0, sizeof context_len); + + deallocate_var_all (); +} + +void +hb_buffer_t::add (hb_codepoint_t codepoint, + unsigned int cluster) +{ + hb_glyph_info_t *glyph; + + if (unlikely (!ensure (len + 1))) return; + + glyph = &info[len]; + + memset (glyph, 0, sizeof (*glyph)); + glyph->codepoint = codepoint; + glyph->mask = 1; + glyph->cluster = cluster; + + len++; +} + +void +hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + + len++; +} + + +void +hb_buffer_t::remove_output (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_output (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = true; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_positions (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = false; + have_positions = true; + + out_len = 0; + out_info = info; + + memset (pos, 0, sizeof (pos[0]) * len); +} + +void +hb_buffer_t::swap_buffers (void) +{ + if (unlikely (in_error)) return; + + assert (have_output); + have_output = false; + + if (out_info != info) + { + hb_glyph_info_t *tmp_string; + tmp_string = info; + info = out_info; + out_info = tmp_string; + pos = (hb_glyph_position_t *) out_info; + } + + unsigned int tmp; + tmp = len; + len = out_len; + out_len = tmp; + + idx = 0; +} + + +void +hb_buffer_t::replace_glyphs (unsigned int num_in, + unsigned int num_out, + const uint32_t *glyph_data) +{ + if (unlikely (!make_room_for (num_in, num_out))) return; + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t orig_info = info[idx]; + 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 (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = info[idx]; + out_info[out_len].codepoint = glyph_index; + + out_len++; +} + +void +hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = glyph_info; + + out_len++; +} + +void +hb_buffer_t::copy_glyph (void) +{ + if (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = info[idx]; + + out_len++; +} + +bool +hb_buffer_t::move_to (unsigned int i) +{ + if (!have_output) + { + assert (i <= len); + idx = i; + return true; + } + if (unlikely (in_error)) + return false; + + assert (i <= out_len + (len - idx)); + + if (out_len < i) + { + unsigned int count = i - out_len; + if (unlikely (!make_room_for (count, count))) return false; + + memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); + idx += count; + out_len += count; + } + else if (out_len > i) + { + /* Tricky part: rewinding... */ + unsigned int count = out_len - i; + + /* This will blow in our face if memory allocation fails later + * in this same lookup... */ + if (unlikely (idx < count && !shift_forward (count + 32))) return false; + + assert (idx >= count); + + idx -= count; + out_len -= count; + memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); + } + + return true; +} + +void +hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) +{ + if (unlikely (out_info != info || out_len != idx)) { + if (unlikely (!make_room_for (1, 1))) return; + out_info[out_len] = info[idx]; + } + out_info[out_len].codepoint = glyph_index; + + idx++; + out_len++; +} + + +void +hb_buffer_t::set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end) +{ + hb_mask_t not_mask = ~mask; + value &= mask; + + if (!mask) + return; + + if (cluster_start == 0 && cluster_end == (unsigned int)-1) { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + info[i].mask = (info[i].mask & not_mask) | value; + return; + } + + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) + info[i].mask = (info[i].mask & not_mask) | value; +} + +void +hb_buffer_t::reverse_range (unsigned int start, + unsigned int end) +{ + unsigned int i, j; + + if (end - start < 2) + return; + + for (i = start, j = end - 1; i < j; i++, j--) { + hb_glyph_info_t t; + + t = info[i]; + info[i] = info[j]; + info[j] = t; + } + + if (have_positions) { + for (i = start, j = end - 1; i < j; i++, j--) { + hb_glyph_position_t t; + + t = pos[i]; + pos[i] = pos[j]; + pos[j] = t; + } + } +} + +void +hb_buffer_t::reverse (void) +{ + if (unlikely (!len)) + return; + + reverse_range (0, len); +} + +void +hb_buffer_t::reverse_clusters (void) +{ + unsigned int i, start, count, last_cluster; + + if (unlikely (!len)) + return; + + reverse (); + + count = len; + start = 0; + last_cluster = info[0].cluster; + for (i = 1; i < count; i++) { + if (last_cluster != info[i].cluster) { + reverse_range (start, i); + start = i; + last_cluster = info[i].cluster; + } + } + reverse_range (start, i); +} + +void +hb_buffer_t::merge_clusters_impl (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + unsigned int cluster = info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, info[i].cluster); + + /* Extend end */ + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; + + /* Extend start */ + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; + + /* If we hit the start of buffer, continue in out-buffer. */ + if (idx == start) + for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) + out_info[i - 1].cluster = cluster; + + for (unsigned int i = start; i < end; i++) + info[i].cluster = cluster; +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, out_info[i].cluster); + + /* Extend start */ + while (start && out_info[start - 1].cluster == out_info[start].cluster) + start--; + + /* Extend end */ + while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) + end++; + + /* If we hit the end of out-buffer, continue in buffer. */ + if (end == out_len) + for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) + info[i].cluster = cluster; + + for (unsigned int i = start; i < end; i++) + out_info[i].cluster = cluster; +} +void +hb_buffer_t::delete_glyph () +{ + unsigned int cluster = info[idx].cluster; + if (idx + 1 < len && cluster == info[idx + 1].cluster) + { + /* Cluster survives; do nothing. */ + goto done; + } + + if (out_len) + { + /* Merge cluster backward. */ + if (cluster < out_info[out_len - 1].cluster) + { + unsigned int old_cluster = out_info[out_len - 1].cluster; + for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) + out_info[i - 1].cluster = cluster; + } + goto done; + } + + if (idx + 1 < len) + { + /* Merge cluster forward. */ + merge_clusters (idx, idx + 2); + goto done; + } + +done: + skip_glyph (); +} + +void +hb_buffer_t::guess_segment_properties (void) +{ + assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || + (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + /* 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 = unicode->script (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 (); + } +} + + +/* Public API */ + +/** + * hb_buffer_create: (Xconstructor) + * + * Creates a new #hb_buffer_t with all properties to defaults. + * + * Return value: (transfer full): + * A newly allocated #hb_buffer_t with a reference count of 1. The initial + * reference count should be released with hb_buffer_destroy() when you are done + * using the #hb_buffer_t. This function never returns %NULL. If memory cannot + * be allocated, a special #hb_buffer_t object will be returned on which + * hb_buffer_allocation_successful() returns %false. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_create (void) +{ + hb_buffer_t *buffer; + + if (!(buffer = hb_object_create<hb_buffer_t> ())) + return hb_buffer_get_empty (); + + buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + + buffer->reset (); + + return buffer; +} + +/** + * hb_buffer_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_get_empty (void) +{ + static const hb_buffer_t _hb_buffer_nil = { + HB_OBJECT_HEADER_STATIC, + + const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + HB_BUFFER_SCRATCH_FLAG_DEFAULT, + HB_BUFFER_MAX_LEN_DEFAULT, + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + true, /* in_error */ + true, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ + }; + + return const_cast<hb_buffer_t *> (&_hb_buffer_nil); +} + +/** + * hb_buffer_reference: (skip) + * @buffer: an #hb_buffer_t. + * + * Increases the reference count on @buffer by one. This prevents @buffer from + * being destroyed until a matching call to hb_buffer_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_buffer_t. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer) +{ + return hb_object_reference (buffer); +} + +/** + * hb_buffer_destroy: (skip) + * @buffer: an #hb_buffer_t. + * + * Deallocate the @buffer. + * Decreases the reference count on @buffer by one. If the result is zero, then + * @buffer and all associated resources are freed. See hb_buffer_reference(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_destroy (hb_buffer_t *buffer) +{ + if (!hb_object_destroy (buffer)) return; + + hb_unicode_funcs_destroy (buffer->unicode); + + free (buffer->info); + free (buffer->pos); + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); + + free (buffer); +} + +/** + * hb_buffer_set_user_data: (skip) + * @buffer: an #hb_buffer_t. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (buffer, key, data, destroy, replace); +} + +/** + * hb_buffer_get_user_data: (skip) + * @buffer: an #hb_buffer_t. + * @key: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (buffer, key); +} + + +/** + * hb_buffer_set_content_type: + * @buffer: an #hb_buffer_t. + * @content_type: the type of buffer contents to set + * + * Sets the type of @buffer contents, buffers are either empty, contain + * characters (before shaping) or glyphs (the result of shaping). + * + * Since: 0.9.5 + **/ +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type) +{ + buffer->content_type = content_type; +} + +/** + * hb_buffer_get_content_type: + * @buffer: an #hb_buffer_t. + * + * see hb_buffer_set_content_type(). + * + * Return value: + * The type of @buffer contents. + * + * Since: 0.9.5 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_unicode_funcs: + * @buffer: an #hb_buffer_t. + * @unicode_funcs: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + if (!unicode_funcs) + unicode_funcs = hb_unicode_funcs_get_default (); + + + hb_unicode_funcs_reference (unicode_funcs); + hb_unicode_funcs_destroy (buffer->unicode); + buffer->unicode = unicode_funcs; +} + +/** + * hb_buffer_get_unicode_funcs: + * @buffer: an #hb_buffer_t. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) +{ + return buffer->unicode; +} + +/** + * hb_buffer_set_direction: + * @buffer: an #hb_buffer_t. + * @direction: the #hb_direction_t of the @buffer + * + * Set the text flow direction of the buffer. No shaping can happen without + * setting @buffer direction, and it controls the visual direction for the + * output glyphs; for RTL direction the glyphs will be reversed. Many layout + * features depend on the proper setting of the direction, for example, + * reversing RTL text before shaping, then shaping with LTR direction is not + * the same as keeping the text in logical order and shaping with RTL + * direction. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction) + +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.direction = direction; +} + +/** + * hb_buffer_get_direction: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_direction() + * + * Return value: + * The direction of the @buffer. + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer) +{ + return buffer->props.direction; +} + +/** + * hb_buffer_set_script: + * @buffer: an #hb_buffer_t. + * @script: an #hb_script_t to set. + * + * Sets the script of @buffer to @script. + * + * Script is crucial for choosing the proper shaping behaviour for scripts that + * require it (e.g. Arabic) and the which OpenType features defined in the font + * to be applied. + * + * You can pass one of the predefined #hb_script_t values, or use + * hb_script_from_string() or hb_script_from_iso15924_tag() to get the + * corresponding script from an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.script = script; +} + +/** + * hb_buffer_get_script: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_script(). + * + * Return value: + * The #hb_script_t of the @buffer. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer) +{ + return buffer->props.script; +} + +/** + * hb_buffer_set_language: + * @buffer: an #hb_buffer_t. + * @language: an hb_language_t to set. + * + * Sets the language of @buffer to @language. + * + * Languages are crucial for selecting which OpenType feature to apply to the + * buffer which can result in applying language-specific behaviour. Languages + * are orthogonal to the scripts, and though they are related, they are + * different concepts and should not be confused with each other. + * + * Use hb_language_from_string() to convert from ISO 639 language codes to + * #hb_language_t. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.language = language; +} + +/** + * hb_buffer_get_language: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_language(). + * + * Return value: (transfer none): + * The #hb_language_t of the buffer. Must not be freed by the caller. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer) +{ + return buffer->props.language; +} + +/** + * hb_buffer_set_segment_properties: + * @buffer: an #hb_buffer_t. + * @props: an #hb_segment_properties_t to use. + * + * Sets the segment properties of the buffer, a shortcut for calling + * hb_buffer_set_direction(), hb_buffer_set_script() and + * hb_buffer_set_language() individually. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props = *props; +} + +/** + * hb_buffer_get_segment_properties: + * @buffer: an #hb_buffer_t. + * @props: (out): the output #hb_segment_properties_t. + * + * Sets @props to the #hb_segment_properties_t of @buffer. + * + * Since: 0.9.7 + **/ +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: an #hb_buffer_t. + * @flags: the buffer flags to set. + * + * Sets @buffer flags to @flags. See #hb_buffer_flags_t. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->flags = flags; +} + +/** + * hb_buffer_get_flags: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_flags(). + * + * Return value: + * The @buffer flags. + * + * Since: 0.9.7 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer) +{ + return buffer->flags; +} + +/** + * hb_buffer_set_cluster_level: + * @buffer: an #hb_buffer_t. + * @cluster_level: + * + * + * + * Since: 0.9.42 + **/ +void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->cluster_level = cluster_level; +} + +/** + * hb_buffer_get_cluster_level: + * @buffer: an #hb_buffer_t. + * + * + * + * Return value: + * + * Since: 0.9.42 + **/ +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer) +{ + return buffer->cluster_level; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @buffer: an #hb_buffer_t. + * @replacement: the replacement #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. + * + * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. + * + * Since: 0.9.31 + **/ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->replacement = replacement; +} + +/** + * hb_buffer_get_replacement_codepoint: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_replacement_codepoint(). + * + * Return value: + * The @buffer replacement #hb_codepoint_t. + * + * Since: 0.9.31 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +{ + return buffer->replacement; +} + + +/** + * hb_buffer_reset: + * @buffer: an #hb_buffer_t. + * + * Resets the buffer to its initial status, as if it was just newly created + * with hb_buffer_create(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + buffer->reset (); +} + +/** + * hb_buffer_clear_contents: + * @buffer: an #hb_buffer_t. + * + * Similar to hb_buffer_reset(), but does not clear the Unicode functions and + * the replacement code point. + * + * Since: 0.9.11 + **/ +void +hb_buffer_clear_contents (hb_buffer_t *buffer) +{ + buffer->clear (); +} + +/** + * hb_buffer_pre_allocate: + * @buffer: an #hb_buffer_t. + * @size: number of items to pre allocate. + * + * Pre allocates memory for @buffer to fit at least @size number of items. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) +{ + return buffer->ensure (size); +} + +/** + * hb_buffer_allocation_successful: + * @buffer: an #hb_buffer_t. + * + * Check if allocating memory for the buffer succeeded. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer) +{ + return !buffer->in_error; +} + +/** + * hb_buffer_add: + * @buffer: an #hb_buffer_t. + * @codepoint: a Unicode code point. + * @cluster: the cluster value of @codepoint. + * + * Appends a character with the Unicode value of @codepoint to @buffer, and + * gives it the initial cluster value of @cluster. Clusters can be any thing + * the client wants, they are usually used to refer to the index of the + * character in the input text stream and are output in + * #hb_glyph_info_t.cluster field. + * + * This function does not check the validity of @codepoint, it is up to the + * caller to ensure it is a valid Unicode code point. + * + * Since: 0.9.7 + **/ +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster) +{ + buffer->add (codepoint, cluster); + buffer->clear_context (1); +} + +/** + * hb_buffer_set_length: + * @buffer: an #hb_buffer_t. + * @length: the new length of @buffer. + * + * Similar to hb_buffer_pre_allocate(), but clears any new items added at the + * end. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +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; + + /* Wipe the new space */ + if (length > buffer->len) { + memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + if (buffer->have_positions) + memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + } + + buffer->len = length; + + if (!length) + { + buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + buffer->clear_context (0); + } + buffer->clear_context (1); + + return true; +} + +/** + * hb_buffer_get_length: + * @buffer: an #hb_buffer_t. + * + * Returns the number of items in the buffer. + * + * Return value: + * The @buffer length. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_get_length (hb_buffer_t *buffer) +{ + return buffer->len; +} + +/** + * hb_buffer_get_glyph_infos: + * @buffer: an #hb_buffer_t. + * @length: (out): output array length. + * + * Returns @buffer glyph information array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph information array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + return (hb_glyph_info_t *) buffer->info; +} + +/** + * hb_buffer_get_glyph_positions: + * @buffer: an #hb_buffer_t. + * @length: (out): output length. + * + * Returns @buffer glyph position array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph position array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length) +{ + if (!buffer->have_positions) + buffer->clear_positions (); + + if (length) + *length = buffer->len; + + return (hb_glyph_position_t *) buffer->pos; +} + +/** + * hb_buffer_reverse: + * @buffer: an #hb_buffer_t. + * + * Reverses buffer contents. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse (hb_buffer_t *buffer) +{ + buffer->reverse (); +} + +/** + * hb_buffer_reverse_range: + * @buffer: an #hb_buffer_t. + * @start: start index. + * @end: end index. + * + * Reverses buffer contents between start to end. + * + * Since: 0.9.41 + **/ +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + buffer->reverse_range (start, end); +} + +/** + * hb_buffer_reverse_clusters: + * @buffer: an #hb_buffer_t. + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer) +{ + buffer->reverse_clusters (); +} + +/** + * hb_buffer_guess_segment_properties: + * @buffer: an #hb_buffer_t. + * + * Sets unset buffer segment properties based on buffer Unicode + * contents. If buffer is not empty, it must have content type + * %HB_BUFFER_CONTENT_TYPE_UNICODE. + * + * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it + * will be set to the Unicode script of the first character in + * the buffer that has a script other than %HB_SCRIPT_COMMON, + * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. + * + * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), + * it will be set to the natural horizontal direction of the + * buffer script as returned by hb_script_get_horizontal_direction(). + * + * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), + * it will be set to the process's default language as returned by + * hb_language_get_default(). This may change in the future by + * taking buffer script into consideration when choosing a language. + * + * Since: 0.9.7 + **/ +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer) +{ + buffer->guess_segment_properties (); +} + +template <typename utf_t> +static inline void +hb_buffer_add_utf (hb_buffer_t *buffer, + const typename utf_t::codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + typedef typename utf_t::codepoint_t T; + const hb_codepoint_t replacement = buffer->replacement; + + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || + (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + if (unlikely (hb_object_is_inert (buffer))) + return; + + if (text_length == -1) + text_length = utf_t::strlen (text); + + if (item_length == -1) + item_length = text_length - item_offset; + + buffer->ensure (buffer->len + item_length * sizeof (T) / 4); + + /* If buffer is empty and pre-context provided, install it. + * This check is written this way, to make sure people can + * provide pre-context in one add_utf() call, then provide + * text in a follow-up call. See: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 + */ + if (!buffer->len && item_offset > 0) + { + /* Add pre-context */ + buffer->clear_context (0); + const T *prev = text + item_offset; + const T *start = text; + while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + prev = utf_t::prev (prev, start, &u, replacement); + buffer->context[0][buffer->context_len[0]++] = u; + } + } + + const T *next = text + item_offset; + const T *end = next + item_length; + while (next < end) + { + hb_codepoint_t u; + const T *old_next = next; + next = utf_t::next (next, end, &u, replacement); + buffer->add (u, old_next - (const T *) text); + } + + /* Add post-context */ + buffer->clear_context (1); + end = text + text_length; + while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + next = utf_t::next (next, end, &u, replacement); + buffer->context[1][buffer->context_len[1]++] = u; + } + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; +} + +/** + * hb_buffer_add_utf8: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-8 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf8_t> (buffer, (const uint8_t *) text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf16: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length): an array of UTF-16 characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-16 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf16_t> (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf32: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length): an array of UTF-32 characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-32 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf32_t<> > (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 + * Unicode code points that can fit in 8-bit strings. + * + * <note>Has nothing to do with non-Unicode Latin-1 encoding.</note> + * + * Since: 0.9.39 + **/ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_latin1_t> (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_codepoints: + * @buffer: a #hb_buffer_t to append characters to. + * @text: (array length=text_length): an array of Unicode code points to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first code point to add to the @buffer. + * @item_length: the number of code points to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * Appends characters from @text array to @buffer. The @item_offset is the + * position of the first character from @text that will be appended, and + * @item_length is the number of character. When shaping part of a larger text + * (e.g. a run of text from a paragraph), instead of passing just the substring + * corresponding to the run, it is preferable to pass the whole + * paragraph and specify the run start and length as @item_offset and + * @item_length, respectively, to give HarfBuzz the full context to be able, + * for example, to do cross-run Arabic shaping or properly handle combining + * marks at stat of run. + * + * This function does not check the validity of @text, it is up to the caller + * to ensure it contains a valid Unicode code points. + * + * Since: 0.9.31 + **/ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf32_t<false> > (buffer, text, text_length, item_offset, item_length); +} + + +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @buffer: an #hb_buffer_t. + * + * Reorders a glyph buffer to have canonical in-cluster glyph order / position. + * The resulting clusters should behave identical to pre-reordering clusters. + * + * <note>This has nothing to do with Unicode normalization.</note> + * + * Since: 0.9.2 + **/ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + unsigned int count = buffer->len; + if (unlikely (!count)) return; + hb_glyph_info_t *info = buffer->info; + + unsigned int start = 0; + unsigned int end; + for (end = start + 1; end < count; end++) + if (info[start].cluster != info[end].cluster) { + normalize_glyphs_cluster (buffer, start, end, backward); + start = end; + } + normalize_glyphs_cluster (buffer, start, end, backward); +} + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +} + +/* + * Debugging. + */ + +/** + * hb_buffer_set_message_func: + * @buffer: an #hb_buffer_t. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.3 + **/ +void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy) +{ + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); + + if (func) { + buffer->message_func = func; + buffer->message_data = user_data; + buffer->message_destroy = destroy; + } else { + buffer->message_func = NULL; + buffer->message_data = NULL; + buffer->message_destroy = NULL; + } +} + +bool +hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) +{ + char buf[100]; + vsnprintf (buf, sizeof (buf), fmt, ap); + return (bool) this->message_func (this, font, buf, this->message_data); +} diff --git a/gfx/harfbuzz/src/hb-buffer.h b/gfx/harfbuzz/src/hb-buffer.h new file mode 100644 index 000000000..bf289c19b --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer.h @@ -0,0 +1,472 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009 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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_BUFFER_H +#define HB_BUFFER_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_glyph_info_t: + * @codepoint: either a Unicode code point (before shaping) or a glyph index + * (after shaping). + * @mask: + * @cluster: the index of the character in the original text that corresponds + * to this #hb_glyph_info_t, or whatever the client passes to + * hb_buffer_add(). More than one #hb_glyph_info_t can have the same + * @cluster value, if they resulted from the same character (e.g. one + * to many glyph substitution), and when more than one character gets + * merged in the same glyph (e.g. many to one glyph substitution) the + * #hb_glyph_info_t will have the smallest cluster value of them. + * By default some characters are merged into the same cluster + * (e.g. combining marks have the same cluster as their bases) + * even if they are separate glyphs, hb_buffer_set_cluster_level() + * allow selecting more fine-grained cluster handling. + * + * The #hb_glyph_info_t is the structure that holds information about the + * glyphs and their relation to input text. + * + */ +typedef struct hb_glyph_info_t { + hb_codepoint_t codepoint; + hb_mask_t mask; + uint32_t cluster; + + /*< private >*/ + hb_var_int_t var1; + hb_var_int_t var2; +} hb_glyph_info_t; + +/** + * hb_glyph_position_t: + * @x_advance: how much the line advances after drawing this glyph when setting + * text in horizontal direction. + * @y_advance: how much the line advances after drawing this glyph when setting + * text in vertical direction. + * @x_offset: how much the glyph moves on the X-axis before drawing it, this + * should not affect how much the line advances. + * @y_offset: how much the glyph moves on the Y-axis before drawing it, this + * should not affect how much the line advances. + * + * The #hb_glyph_position_t is the structure that holds the positions of the + * glyph in both horizontal and vertical directions. All positions in + * #hb_glyph_position_t are relative to the current point. + * + */ +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + + /*< private >*/ + hb_var_int_t var; +} hb_glyph_position_t; + +/** + * hb_segment_properties_t: + * @direction: the #hb_direction_t of the buffer, see hb_buffer_set_direction(). + * @script: the #hb_script_t of the buffer, see hb_buffer_set_script(). + * @language: the #hb_language_t of the buffer, see hb_buffer_set_language(). + * + * The structure that holds various text properties of an #hb_buffer_t. Can be + * set and retrieved using hb_buffer_set_segment_properties() and + * hb_buffer_get_segment_properties(), respectively. + */ +typedef struct hb_segment_properties_t { + hb_direction_t direction; + hb_script_t script; + hb_language_t language; + /*< private >*/ + void *reserved1; + void *reserved2; +} hb_segment_properties_t; + +#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ + HB_SCRIPT_INVALID, \ + HB_LANGUAGE_INVALID, \ + NULL, \ + NULL} + +HB_EXTERN hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +HB_EXTERN unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p); + + + +/** + * hb_buffer_t: + * + * The main structure holding the input text and its properties before shaping, + * and output glyphs and their information after shaping. + */ + +typedef struct hb_buffer_t hb_buffer_t; + +HB_EXTERN hb_buffer_t * +hb_buffer_create (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_get_empty (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_destroy (hb_buffer_t *buffer); + +HB_EXTERN 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_bool_t replace); + +HB_EXTERN void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key); + +/** + * hb_buffer_content_type_t: + * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer. + * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping). + * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping). + */ +typedef enum { + HB_BUFFER_CONTENT_TYPE_INVALID = 0, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +HB_EXTERN void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type); + +HB_EXTERN hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs); + +HB_EXTERN hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction); + +HB_EXTERN hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script); + +HB_EXTERN hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language); + + +HB_EXTERN hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +/** + * hb_buffer_flags_t: + * @HB_BUFFER_FLAG_DEFAULT: the default buffer flag. + * @HB_BUFFER_FLAG_BOT: flag indicating that special handling of the beginning + * of text paragraph can be applied to this buffer. Should usually + * be set, unless you are passing to the buffer only part + * of the text without the full context. + * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text + * paragraph can be applied to this buffer, similar to + * @HB_BUFFER_FLAG_EOT. + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should use the corresponding glyph + * from the font, instead of hiding them (currently done + * by replacing them with the space glyph and zeroing the + * advance width.) + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ + HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u +} hb_buffer_flags_t; + +HB_EXTERN void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +HB_EXTERN hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer); + +/* + * Since: 0.9.42 + */ +typedef enum { + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES +} hb_buffer_cluster_level_t; + +HB_EXTERN void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level); + +HB_EXTERN hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer); + +/** + * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: + * + * The default code point for replacing invalid characters in a given encoding. + * Set to U+FFFD REPLACEMENT CHARACTER. + * + * Since: 0.9.31 + */ +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +HB_EXTERN void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_reset (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_clear_contents (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, + unsigned int size); + + +HB_EXTERN hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end); + +HB_EXTERN void +hb_buffer_reverse_clusters (hb_buffer_t *buffer); + + +/* Filling the buffer in */ + +HB_EXTERN void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster); + +HB_EXTERN void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + + +HB_EXTERN hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length); + +HB_EXTERN unsigned int +hb_buffer_get_length (hb_buffer_t *buffer); + +/* Getting glyphs out of the buffer */ + +HB_EXTERN hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length); + +HB_EXTERN hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length); + + +HB_EXTERN void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +/** + * hb_buffer_serialize_flags_t: + * @HB_BUFFER_SERIALIZE_FLAG_DEFAULT: serialize glyph names, clusters and positions. + * @HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS: do not serialize glyph cluster. + * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information. + * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. + * + * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, + HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, + HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u +} hb_buffer_serialize_flags_t; + +/** + * hb_buffer_serialize_format_t: + * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format. + * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format. + * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format. + * + * The buffer serialization and de-serialization format used in + * hb_buffer_serialize_glyphs() and hb_buffer_deserialize_glyphs(). + * + * Since: 0.9.2 + */ +typedef enum { + HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), + HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), + HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE +} hb_buffer_serialize_format_t; + +HB_EXTERN hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +HB_EXTERN const char ** +hb_buffer_serialize_list_formats (void); + +HB_EXTERN unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_font_t *font, + hb_buffer_serialize_format_t format); + + +/* + * Debugging. + */ + +typedef hb_bool_t (*hb_buffer_message_func_t) (hb_buffer_t *buffer, + hb_font_t *font, + const char *message, + void *user_data); + +HB_EXTERN void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_END_DECLS + +#endif /* HB_BUFFER_H */ diff --git a/gfx/harfbuzz/src/hb-cache-private.hh b/gfx/harfbuzz/src/hb-cache-private.hh new file mode 100644 index 000000000..24957e1e9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-cache-private.hh @@ -0,0 +1,74 @@ +/* + * 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 & ((1u<<cache_bits)-1); + unsigned int v = values[k]; + if ((v >> value_bits) != (key >> cache_bits)) + return false; + *value = v & ((1u<<value_bits)-1); + return true; + } + + inline bool set (unsigned int key, unsigned int value) + { + if (unlikely ((key >> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1u<<cache_bits)-1); + unsigned int v = ((key>>cache_bits)<<value_bits) | value; + values[k] = v; + return true; + } + + private: + unsigned int values[1u<<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 */ diff --git a/gfx/harfbuzz/src/hb-common.cc b/gfx/harfbuzz/src/hb-common.cc new file mode 100644 index 000000000..3564e4355 --- /dev/null +++ b/gfx/harfbuzz/src/hb-common.cc @@ -0,0 +1,607 @@ +/* + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-mutex-private.hh" +#include "hb-object-private.hh" + +#include <locale.h> + + +/* hb_options_t */ + +hb_options_union_t _hb_options; + +void +_hb_options_init (void) +{ + hb_options_union_t u; + u.i = 0; + u.opts.initialized = 1; + + char *c = getenv ("HB_OPTIONS"); + u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); + + /* This is idempotent and threadsafe. */ + _hb_options = u; +} + + +/* hb_tag_t */ + +/** + * hb_tag_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_tag_from_string (const char *str, int len) +{ + char tag[4]; + unsigned int i; + + if (!str || !len || !*str) + return HB_TAG_NONE; + + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && str[i]; i++) + tag[i] = str[i]; + for (; i < 4; i++) + tag[i] = ' '; + + return HB_TAG_CHAR4 (tag); +} + +/** + * hb_tag_to_string: + * @tag: + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): + * + * + * + * Since: 0.9.5 + **/ +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + + +/* hb_direction_t */ + +const char direction_strings[][4] = { + "ltr", + "rtl", + "ttb", + "btt" +}; + +/** + * hb_direction_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_direction_from_string (const char *str, int len) +{ + if (unlikely (!str || !len || !*str)) + return HB_DIRECTION_INVALID; + + /* Lets match loosely: just match the first letter, such that + * all of "ltr", "left-to-right", etc work! + */ + 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) (HB_DIRECTION_LTR + i); + + return HB_DIRECTION_INVALID; +} + +/** + * hb_direction_to_string: + * @direction: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +const char * +hb_direction_to_string (hb_direction_t direction) +{ + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; + + return "invalid"; +} + + +/* hb_language_t */ + +struct hb_language_impl_t { + const char s[1]; +}; + +static const char canon_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, + '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 +}; + +static bool +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 (*p1 && *p1 == canon_map[*p2]) + p1++, p2++; + + return *p1 == canon_map[*p2]; +} + +#if 0 +static unsigned int +lang_hash (const void *key) +{ + const unsigned char *p = key; + unsigned int h = 0; + while (canon_map[*p]) + { + h = (h << 5) - h + canon_map[*p]; + p++; + } + + return h; +} +#endif + + +struct hb_language_item_t { + + struct hb_language_item_t *next; + hb_language_t lang; + + inline bool operator == (const char *s) const { + return lang_equal (lang, s); + } + + inline hb_language_item_t & operator = (const char *s) { + lang = (hb_language_t) strdup (s); + for (unsigned char *p = (unsigned char *) lang; *p; p++) + *p = canon_map[*p]; + + return *this; + } + + void finish (void) { free ((void *) lang); } +}; + + +/* Thread-safe lock-free language list */ + +static hb_language_item_t *langs; + +#ifdef HB_USE_ATEXIT +static +void free_langs (void) +{ + while (langs) { + hb_language_item_t *next = langs->next; + langs->finish (); + free (langs); + langs = next; + } +} +#endif + +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)) { + lang->finish (); + free (lang); + goto retry; + } + +#ifdef HB_USE_ATEXIT + if (!first_lang) + atexit (free_langs); /* First person registers atexit() callback. */ +#endif + + return lang; +} + + +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing + * ISO 639 language code + * @len: length of the @str, or -1 if it is %NULL-terminated. + * + * Converts @str representing an ISO 639 language code to the corresponding + * #hb_language_t. + * + * Return value: (transfer none): + * The #hb_language_t corresponding to the ISO 639 language code. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_from_string (const char *str, int len) +{ + if (!str || !len || !*str) + return HB_LANGUAGE_INVALID; + + hb_language_item_t *item = NULL; + if (len >= 0) + { + /* NUL-terminate it. */ + char strbuf[64]; + len = MIN (len, (int) sizeof (strbuf) - 1); + memcpy (strbuf, str, len); + strbuf[len] = '\0'; + item = lang_find_or_insert (strbuf); + } + else + item = lang_find_or_insert (str); + + return likely (item) ? item->lang : HB_LANGUAGE_INVALID; +} + +/** + * hb_language_to_string: + * @language: an #hb_language_t to convert. + * + * See hb_language_from_string(). + * + * Return value: (transfer none): + * A %NULL-terminated string representing the @language. Must not be freed by + * the caller. + * + * Since: 0.9.2 + **/ +const char * +hb_language_to_string (hb_language_t language) +{ + /* This is actually NULL-safe! */ + return language->s; +} + +/** + * hb_language_get_default: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_get_default (void) +{ + static hb_language_t default_language = HB_LANGUAGE_INVALID; + + hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); + if (unlikely (language == HB_LANGUAGE_INVALID)) { + language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); + (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); + } + + return default_language; +} + + +/* hb_script_t */ + +/** + * hb_script_from_iso15924_tag: + * @tag: an #hb_tag_t representing an ISO 15924 tag. + * + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return HB_SCRIPT_INVALID; + + /* Be lenient, adjust case (one capital letter followed by three small letters) */ + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; + + switch (tag) { + + /* These graduated from the 'Q' private-area codes, but + * the old code is still aliased by Unicode, and the Qaai + * one in use by ICU. */ + case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; + case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; + + /* Script variants from http://unicode.org/iso15924/ */ + case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; + case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; + case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; + } + + /* If it looks right, just use the tag as a script */ + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) + return (hb_script_t) tag; + + /* Otherwise, return unknown */ + return HB_SCRIPT_UNKNOWN; +} + +/** + * hb_script_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing an + * ISO 15924 tag. + * @len: length of the @str, or -1 if it is %NULL-terminated. + * + * Converts a string @str representing an ISO 15924 script tag to a + * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then + * hb_script_from_iso15924_tag(). + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_string (const char *str, int len) +{ + return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); +} + +/** + * hb_script_to_iso15924_tag: + * @script: an #hb_script_ to convert. + * + * See hb_script_from_iso15924_tag(). + * + * Return value: + * An #hb_tag_t representing an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script) +{ + return (hb_tag_t) script; +} + +/** + * hb_script_get_horizontal_direction: + * @script: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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: + case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: + case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: + case HB_SCRIPT_OLD_SOUTH_ARABIAN: + case HB_SCRIPT_OLD_TURKIC: + case HB_SCRIPT_SAMARITAN: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MENDE_KIKAKUI: + case HB_SCRIPT_NABATAEAN: + case HB_SCRIPT_OLD_NORTH_ARABIAN: + case HB_SCRIPT_PALMYRENE: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_OLD_HUNGARIAN: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + + return HB_DIRECTION_RTL; + } + + return HB_DIRECTION_LTR; +} + + +/* hb_user_data_array_t */ + +bool +hb_user_data_array_t::set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (!key) + return false; + + 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, lock, (bool) replace); + + return ret; +} + +void * +hb_user_data_array_t::get (hb_user_data_key_t *key) +{ + hb_user_data_item_t item = {NULL, NULL, NULL}; + + return items.find (key, &item, lock) ? item.data : NULL; +} + + +/* hb_version */ + +/** + * hb_version: + * @major: (out): Library major version component. + * @minor: (out): Library minor version component. + * @micro: (out): Library micro version component. + * + * Returns library version as three integer components. + * + * Since: 0.9.2 + **/ +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro) +{ + *major = HB_VERSION_MAJOR; + *minor = HB_VERSION_MINOR; + *micro = HB_VERSION_MICRO; +} + +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: library version string. + * + * Since: 0.9.2 + **/ +const char * +hb_version_string (void) +{ + return HB_VERSION_STRING; +} + +/** + * hb_version_atleast: + * @major: + * @minor: + * @micro: + * + * + * + * Return value: + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) +{ + return HB_VERSION_ATLEAST (major, minor, micro); +} diff --git a/gfx/harfbuzz/src/hb-common.h b/gfx/harfbuzz/src/hb-common.h new file mode 100644 index 000000000..2cbee76a8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-common.h @@ -0,0 +1,367 @@ +/* + * Copyright © 2007,2008,2009 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_COMMON_H +#define HB_COMMON_H + +#ifndef HB_BEGIN_DECLS +# ifdef __cplusplus +# define HB_BEGIN_DECLS extern "C" { +# define HB_END_DECLS } +# else /* !__cplusplus */ +# define HB_BEGIN_DECLS +# define HB_END_DECLS +# endif /* !__cplusplus */ +#endif + +#if !defined (HB_DONT_DEFINE_STDINT) + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ + defined (_sgi) || defined (__sun) || defined (sun) || \ + defined (__digital__) || defined (__HP_cc) +# include <inttypes.h> +#elif defined (_AIX) +# include <sys/inttypes.h> +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +#elif defined (_MSC_VER) && _MSC_VER < 1600 +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +# include <stdint.h> +#endif + +#endif + +HB_BEGIN_DECLS + + +typedef int hb_bool_t; + +typedef uint32_t hb_codepoint_t; +typedef int32_t hb_position_t; +typedef uint32_t hb_mask_t; + +typedef union _hb_var_int_t { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_int_t; + + +/* hb_tag_t */ + +typedef uint32_t hb_tag_t; + +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4)))) +#define HB_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag)) + +#define HB_TAG_NONE HB_TAG(0,0,0,0) +#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +#define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) + +/* len=-1 means str is NUL-terminated. */ +HB_EXTERN hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +HB_EXTERN void +hb_tag_to_string (hb_tag_t tag, char *buf); + + +/** + * hb_direction_t: + * @HB_DIRECTION_INVALID: Initial, unset direction. + * @HB_DIRECTION_LTR: Text is set horizontally from left to right. + * @HB_DIRECTION_RTL: Text is set horizontally from right to left. + * @HB_DIRECTION_TTB: Text is set vertically from top to bottom. + * @HB_DIRECTION_BTT: Text is set vertically from bottom to top. + */ +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_EXTERN hb_direction_t +hb_direction_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_direction_to_string (hb_direction_t direction); + +#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +/* Direction must be valid for the following */ +#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) + + +/* hb_language_t */ + +typedef const struct hb_language_impl_t *hb_language_t; + +HB_EXTERN hb_language_t +hb_language_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_language_to_string (hb_language_t language); + +#define HB_LANGUAGE_INVALID ((hb_language_t) NULL) + +HB_EXTERN hb_language_t +hb_language_get_default (void); + + +/* hb_script_t */ + +/* http://unicode.org/iso15924/ */ +/* http://goo.gl/x9ilM */ +/* Unicode Character Database property: Script (sc) */ +typedef enum +{ + /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), + /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), + /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), + + /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), + /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), + /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), + /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), + /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), + /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), + /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), + /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), + /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), + /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), + /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), + /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), + /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), + /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), + /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), + /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), + /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), + /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), + /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), + /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), + /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), + /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + + /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + + /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), + /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), + /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), + /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), + /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), + /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), + /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), + /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), + /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), + /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), + /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), + /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), + /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), + /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + + /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), + /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), + /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + + /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), + /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), + /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), + /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + + /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), + /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), + /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), + /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), + /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), + /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), + /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + + /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), + /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), + /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), + /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), + /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), + /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), + /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), + /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), + + /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), + /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), + /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), + /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), + /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + + /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), + /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), + /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), + /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), + /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), + /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), + /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), + /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), + /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), + /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), + /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), + + /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), + /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), + /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), + /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), + /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), + /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), + /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), + /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), + /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), + /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), + /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), + /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), + /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + + /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), + /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), + /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + + /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), + /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), + /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), + /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), + /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), + /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), + /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), + + /* + * Since: 0.9.30 + */ + /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), + /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), + /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), + /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), + /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), + /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), + /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), + /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), + /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), + /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), + /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), + /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), + /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), + /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), + /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), + /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), + /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), + /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), + /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), + /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), + /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), + /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), + /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), + + /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), + /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), + /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), + /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), + /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), + /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), + + /* + * Since 1.3.0 + */ + /*9.0*/ HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), + /*9.0*/ HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), + /*9.0*/ HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), + /*9.0*/ HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), + /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), + /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. Include both a signed and unsigned max, + * since technically enums are int, and indeed, hb_script_t ends up being signed. + * See this thread for technicalities: + * + * http://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +/* Script functions */ + +HB_EXTERN hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag); + +HB_EXTERN hb_script_t +hb_script_from_string (const char *str, int len); + +HB_EXTERN hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script); + +HB_EXTERN hb_direction_t +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; + +typedef void (*hb_destroy_func_t) (void *user_data); + + +HB_END_DECLS + +#endif /* HB_COMMON_H */ diff --git a/gfx/harfbuzz/src/hb-coretext.cc b/gfx/harfbuzz/src/hb-coretext.cc new file mode 100644 index 000000000..e857dfae0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-coretext.cc @@ -0,0 +1,1310 @@ +/* + * Copyright © 2012,2013 Mozilla Foundation. + * Copyright © 2012,2013 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#define HB_SHAPER coretext +#include "hb-shaper-impl-private.hh" + +#include "hb-coretext.h" + + +#ifndef HB_DEBUG_CORETEXT +#define HB_DEBUG_CORETEXT (HB_DEBUG+0) +#endif + + +static void +release_table_data (void *user_data) +{ + CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data); + CFRelease(cf_data); +} + +static hb_blob_t * +reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data); + CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); + if (unlikely (!cf_data)) + return NULL; + + const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data)); + const size_t length = CFDataGetLength (cf_data); + if (!data || !length) + return NULL; + + return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, + reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)), + release_table_data); +} + +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font) +{ + return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease); +} + + +HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) +HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) + + +/* + * shaper face data + */ + +static CTFontDescriptorRef +get_last_resort_font_desc (void) +{ + // TODO Handle allocation failures? + CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); + CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, + (const void **) &last_resort, + 1, + &kCFTypeArrayCallBacks); + CFRelease (last_resort); + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontCascadeListAttribute, + (const void **) &cascade_list, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cascade_list); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + return font_desc; +} + +static void +release_data (void *info, const void *data, size_t size) +{ + assert (hb_blob_get_length ((hb_blob_t *) info) == size && + hb_blob_get_data ((hb_blob_t *) info, NULL) == data); + + hb_blob_destroy ((hb_blob_t *) info); +} + +static CGFontRef +create_cg_font (hb_face_t *face) +{ + CGFontRef cg_font = NULL; + if (face->destroy == (hb_destroy_func_t) CGFontRelease) + { + cg_font = CGFontRetain ((CGFontRef) face->user_data); + } + else + { + 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 (CORETEXT, face, "Face has empty blob"); + + CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); + if (likely (provider)) + { + cg_font = CGFontCreateWithDataProvider (provider); + if (unlikely (!cg_font)) + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + CGDataProviderRelease (provider); + } + } + return cg_font; +} + +static CTFontRef +create_ct_font (CGFontRef cg_font, CGFloat font_size) +{ + CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL); + if (unlikely (!ct_font)) { + DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); + return NULL; + } + + /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter + * bug indicate that the cascade list reconfiguration occasionally causes + * crashes in CoreText on OS X 10.9, thus let's skip this step on older + * operating system versions. Except for the emoji font, where _not_ + * reconfiguring the cascade list causes CoreText crashes. For details, see + * crbug.com/549610 */ + // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h + if (&CTGetCoreTextVersion != NULL && CTGetCoreTextVersion() < 0x00070000) { + CFStringRef fontName = CTFontCopyPostScriptName (ct_font); + bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; + CFRelease (fontName); + if (!isEmojiFont) + return ct_font; + } + + CFURLRef original_url = (CFURLRef)CTFontCopyAttribute(ct_font, kCTFontURLAttribute); + + /* Create font copy with cascade list that has LastResort first; this speeds up CoreText + * font fallback which we don't need anyway. */ + { + CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc); + CFRelease (last_resort_font_desc); + if (new_ct_font) + { + /* The CTFontCreateCopyWithAttributes call fails to stay on the same font + * when reconfiguring the cascade list and may switch to a different font + * when there are fonts that go by the same name, since the descriptor is + * just name and size. + * + * Avoid reconfiguring the cascade lists if the new font is outside the + * system locations that we cannot access from the sandboxed renderer + * process in Blink. This can be detected by the new file URL location + * that the newly found font points to. */ + CFURLRef new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); + // Keep reconfigured font if URL cannot be retrieved (seems to be the case + // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 + if (!original_url || !new_url || CFEqual (original_url, new_url)) { + CFRelease (ct_font); + ct_font = new_ct_font; + } else { + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + } + if (new_url) + CFRelease (new_url); + } + else + DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); + } + + if (original_url) + CFRelease (original_url); + return ct_font; +} + +struct hb_coretext_shaper_face_data_t { + CGFontRef cg_font; + CTFontRef ct_font; +}; + +hb_coretext_shaper_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); + if (unlikely (!data)) + return NULL; + + data->cg_font = create_cg_font (face); + if (unlikely (!data->cg_font)) + { + DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); + free (data); + return NULL; + } + + /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table, + * which can make the font too tight at large sizes. 36pt should be a good semi-neutral + * size. + * + * Since we always create CTFont at a fixed size, our CTFont lives in face_data + * instead of font_data. Which is good, because when people change scale on + * hb_font_t, we won't need to update our CTFont. */ + data->ct_font = create_ct_font (data->cg_font, 36.); + if (unlikely (!data->ct_font)) + { + DEBUG_MSG (CORETEXT, face, "CTFont creation failed."); + CFRelease (data->cg_font); + free (data); + return NULL; + } + + return data; +} + +void +_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) +{ + CFRelease (data->ct_font); + CFRelease (data->cg_font); + free (data); +} + +/* + * Since: 0.9.10 + */ +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face) +{ + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + return face_data->cg_font; +} + + +/* + * shaper font data + */ + +struct hb_coretext_shaper_font_data_t {}; + +hb_coretext_shaper_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) +{ +} + + +/* + * shaper shape_plan data + */ + +struct hb_coretext_shaper_shape_plan_data_t {}; + +hb_coretext_shaper_shape_plan_data_t * +_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font) +{ + hb_face_t *face = font->face; + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + return face_data->ct_font; +} + + +/* + * shaper + */ + +struct feature_record_t { + unsigned int feature; + unsigned int setting; +}; + +struct active_feature_t { + feature_record_t rec; + unsigned int order; + + static int cmp (const active_feature_t *a, const active_feature_t *b) { + return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : + 0; + } + bool operator== (const active_feature_t *f) { + return cmp (this, f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + static int cmp (const feature_event_t *a, const feature_event_t *b) { + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + CTFontRef font; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + +/* The following enum members are added in OS X 10.8. */ +#define kAltHalfWidthTextSelector 6 +#define kAltProportionalTextSelector 5 +#define kAlternateHorizKanaOffSelector 1 +#define kAlternateHorizKanaOnSelector 0 +#define kAlternateKanaType 34 +#define kAlternateVertKanaOffSelector 3 +#define kAlternateVertKanaOnSelector 2 +#define kCaseSensitiveLayoutOffSelector 1 +#define kCaseSensitiveLayoutOnSelector 0 +#define kCaseSensitiveLayoutType 33 +#define kCaseSensitiveSpacingOffSelector 3 +#define kCaseSensitiveSpacingOnSelector 2 +#define kContextualAlternatesOffSelector 1 +#define kContextualAlternatesOnSelector 0 +#define kContextualAlternatesType 36 +#define kContextualLigaturesOffSelector 19 +#define kContextualLigaturesOnSelector 18 +#define kContextualSwashAlternatesOffSelector 5 +#define kContextualSwashAlternatesOnSelector 4 +#define kDefaultLowerCaseSelector 0 +#define kDefaultUpperCaseSelector 0 +#define kHistoricalLigaturesOffSelector 21 +#define kHistoricalLigaturesOnSelector 20 +#define kHojoCharactersSelector 12 +#define kJIS2004CharactersSelector 11 +#define kLowerCasePetiteCapsSelector 2 +#define kLowerCaseSmallCapsSelector 1 +#define kLowerCaseType 37 +#define kMathematicalGreekOffSelector 11 +#define kMathematicalGreekOnSelector 10 +#define kNLCCharactersSelector 13 +#define kQuarterWidthTextSelector 4 +#define kScientificInferiorsSelector 4 +#define kStylisticAltEightOffSelector 17 +#define kStylisticAltEightOnSelector 16 +#define kStylisticAltEighteenOffSelector 37 +#define kStylisticAltEighteenOnSelector 36 +#define kStylisticAltElevenOffSelector 23 +#define kStylisticAltElevenOnSelector 22 +#define kStylisticAltFifteenOffSelector 31 +#define kStylisticAltFifteenOnSelector 30 +#define kStylisticAltFiveOffSelector 11 +#define kStylisticAltFiveOnSelector 10 +#define kStylisticAltFourOffSelector 9 +#define kStylisticAltFourOnSelector 8 +#define kStylisticAltFourteenOffSelector 29 +#define kStylisticAltFourteenOnSelector 28 +#define kStylisticAltNineOffSelector 19 +#define kStylisticAltNineOnSelector 18 +#define kStylisticAltNineteenOffSelector 39 +#define kStylisticAltNineteenOnSelector 38 +#define kStylisticAltOneOffSelector 3 +#define kStylisticAltOneOnSelector 2 +#define kStylisticAltSevenOffSelector 15 +#define kStylisticAltSevenOnSelector 14 +#define kStylisticAltSeventeenOffSelector 35 +#define kStylisticAltSeventeenOnSelector 34 +#define kStylisticAltSixOffSelector 13 +#define kStylisticAltSixOnSelector 12 +#define kStylisticAltSixteenOffSelector 33 +#define kStylisticAltSixteenOnSelector 32 +#define kStylisticAltTenOffSelector 21 +#define kStylisticAltTenOnSelector 20 +#define kStylisticAltThirteenOffSelector 27 +#define kStylisticAltThirteenOnSelector 26 +#define kStylisticAltThreeOffSelector 7 +#define kStylisticAltThreeOnSelector 6 +#define kStylisticAltTwelveOffSelector 25 +#define kStylisticAltTwelveOnSelector 24 +#define kStylisticAltTwentyOffSelector 41 +#define kStylisticAltTwentyOnSelector 40 +#define kStylisticAltTwoOffSelector 5 +#define kStylisticAltTwoOnSelector 4 +#define kStylisticAlternativesType 35 +#define kSwashAlternatesOffSelector 3 +#define kSwashAlternatesOnSelector 2 +#define kThirdWidthTextSelector 3 +#define kTraditionalNamesCharactersSelector 14 +#define kUpperCasePetiteCapsSelector 2 +#define kUpperCaseSmallCapsSelector 1 +#define kUpperCaseType 38 + +/* Table data courtesy of Apple. */ +static const struct feature_mapping_t { + FourCharCode otFeatureTag; + uint16_t aatFeatureType; + uint16_t selectorToEnable; + uint16_t selectorToDisable; +} feature_mappings[] = { + { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, + { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, + { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, + { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector }, + { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector }, + { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector }, + { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector }, + { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector }, + { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 }, + { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector }, + { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 }, + { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, + { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, + { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, }, + { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, + { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector }, + { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 }, + { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 }, + { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector }, + { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 }, + { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 }, + { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 }, + { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 }, + { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector }, + { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 }, + { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector }, + { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 }, + { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 }, + { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector }, + { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector }, + { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 }, + { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 }, + { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 }, + { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 }, + { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector }, + { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector }, + { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector }, + { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 }, + { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector }, + { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector }, + { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector }, + { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector }, + { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector }, + { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector }, + { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector }, + { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector }, + { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector }, + { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector }, + { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector }, + { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector }, + { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector }, + { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector }, + { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector }, + { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector }, + { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector }, + { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector }, + { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector }, + { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector }, + { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector }, + { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector }, + { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector }, + { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector }, + { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 }, + { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 }, + { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 }, + { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 }, + { 'unic', kLetterCaseType, 14, 15 }, + { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, + { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, + { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector }, + { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, + { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector }, +}; + +static int +_hb_feature_mapping_cmp (const void *key_, const void *entry_) +{ + unsigned int key = * (unsigned int *) key_; + const feature_mapping_t * entry = (const feature_mapping_t *) entry_; + return key < entry->otFeatureTag ? -1 : + key > entry->otFeatureTag ? 1 : + 0; +} + +hb_bool_t +_hb_coretext_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + + CGFloat ct_font_size = CTFontGetSize (face_data->ct_font); + CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; + CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; + + /* Attach marks to their bases, to match the 'ot' shaper. + * Adapted from hb-ot-shape:hb_form_clusters(). + * Note that this only makes us be closer to the 'ot' shaper, + * but by no means the same. For example, if there's + * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will + * continue pointing to B2 even though B2 was merged into B1's + * cluster... */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) + buffer->merge_clusters (i - 1, i + 1); + } + + hb_auto_array_t<feature_record_t> feature_records; + hb_auto_array_t<range_record_t> range_records; + + /* + * Set up features. + * (copied + modified from code from hb-uniscribe.cc) + */ + if (num_features) + { + /* Sort features by start/end events. */ + hb_auto_array_t<feature_event_t> feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag, + feature_mappings, + ARRAY_LENGTH (feature_mappings), + sizeof (feature_mappings[0]), + _hb_feature_mapping_cmp); + if (!mapping) + continue; + + active_feature_t feature; + feature.rec.feature = mapping->aatFeatureType; + feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.rec.feature = HB_TAG_NONE; + feature.rec.setting = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_auto_array_t<active_feature_t> active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.len; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + if (unlikely (!range)) + goto fail_features; + + if (active_features.len) + { + CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + /* TODO sort and resolve conflicting features? */ + /* active_features.qsort (); */ + for (unsigned int j = 0; j < active_features.len; j++) + { + CFStringRef keys[2] = { + kCTFontFeatureTypeIdentifierKey, + kCTFontFeatureSelectorIdentifierKey + }; + CFNumberRef values[2] = { + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; + CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (values[0]); + CFRelease (values[1]); + + CFArrayAppendValue (features_array, dict); + CFRelease (dict); + + } + + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontFeatureSettingsAttribute, + (const void **) &features_array, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (features_array); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + + range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc); + CFRelease (font_desc); + } + else + { + range->font = NULL; + } + + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) { + active_feature_t *feature = active_features.push (); + if (unlikely (!feature)) + goto fail_features; + *feature = event->feature; + } else { + active_feature_t *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.array); + } + } + + if (!range_records.len) /* No active feature found. */ + goto fail_features; + } + else + { + fail_features: + num_features = 0; + } + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ + Type *name = (Type *) scratch; \ + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + if (unlikely (_consumed > scratch_size)) \ + { \ + on_no_room; \ + assert (0); \ + } \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } + + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) { + hb_codepoint_t c = buffer->info[i].codepoint; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ + ret = false; \ + goto fail; \ + } HB_STMT_END; + + bool ret = true; + CFStringRef string_ref = NULL; + CTLineRef line = NULL; + + if (0) + { +resize_and_retry: + DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); + /* string_ref uses the scratch-buffer for backing store, and line references + * string_ref (via attr_string). We must release those before resizing buffer. */ + assert (string_ref); + assert (line); + CFRelease (string_ref); + CFRelease (line); + string_ref = NULL; + line = NULL; + + /* Get previous start-of-scratch-area, that we use later for readjusting + * our existing scratch arrays. */ + unsigned int old_scratch_used; + hb_buffer_t::scratch_buffer_t *old_scratch; + old_scratch = buffer->get_scratch_buffer (&old_scratch_used); + old_scratch_used = scratch - old_scratch; + + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + + /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the + * cleanest way to do without completely restructuring the rest of this shaper. */ + scratch = buffer->get_scratch_buffer (&scratch_size); + pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch))); + log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); + scratch += old_scratch_used; + scratch_size -= old_scratch_used; + } + { + string_ref = CFStringCreateWithCharactersNoCopy (NULL, + pchars, chars_len, + kCFAllocatorNull); + if (unlikely (!string_ref)) + FAIL ("CFStringCreateWithCharactersNoCopy failed"); + + /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ + { + CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, + chars_len); + if (unlikely (!attr_string)) + FAIL ("CFAttributedStringCreateMutable failed"); + CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + { + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTVerticalFormsAttributeName, kCFBooleanTrue); + } + + if (buffer->props.language) + { +/* What's the iOS equivalent of this check? + * The symbols was introduced in iOS 7.0. + * At any rate, our fallback is safe and works fine. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +# define kCTLanguageAttributeName CFSTR ("NSLanguage") +#endif + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + hb_language_to_string (buffer->props.language), + kCFStringEncodingUTF8, + kCFAllocatorNull); + if (unlikely (!lang)) + FAIL ("CFStringCreateWithCStringNoCopy failed"); + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTLanguageAttributeName, lang); + CFRelease (lang); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTFontAttributeName, face_data->ct_font); + + if (num_features) + { + unsigned int start = 0; + range_record_t *last_range = &range_records[0]; + for (unsigned int k = 0; k < chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (range != last_range) + { + if (last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), + kCTFontAttributeName, last_range->font); + + start = k; + } + + last_range = range; + } + if (start != chars_len && last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), + kCTFontAttributeName, last_range->font); + } + + int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); + CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, + (const void **) &level_number, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (unlikely (!options)) + FAIL ("CFDictionaryCreate failed"); + + CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); + CFRelease (options); + CFRelease (attr_string); + if (unlikely (!typesetter)) + FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); + + line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); + CFRelease (typesetter); + if (unlikely (!line)) + FAIL ("CTTypesetterCreateLine failed"); + } + + CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); + unsigned int num_runs = CFArrayGetCount (glyph_runs); + DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs); + + buffer->len = 0; + uint32_t status_and = ~0, status_or = 0; + double advances_so_far = 0; + /* For right-to-left runs, CoreText returns the glyphs positioned such that + * any trailing whitespace is to the left of (0,0). Adjust coordinate system + * to fix for that. Test with any RTL string with trailing spaces. + * https://code.google.com/p/chromium/issues/detail?id=469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } + + const CFRange range_all = CFRangeMake (0, 0); + + for (unsigned int i = 0; i < num_runs; i++) + { + CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); + CTRunStatus run_status = CTRunGetStatus (run); + status_or |= run_status; + status_and &= run_status; + DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); + double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + run_advance = -run_advance; + DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); + + /* CoreText does automatic font fallback (AKA "cascading") for characters + * not supported by the requested font, and provides no way to turn it off, + * so we must detect if the returned run uses a font other than the requested + * one and fill in the buffer with .notdef glyphs instead of random glyph + * indices from a different font. + */ + CFDictionaryRef attributes = CTRunGetAttributes (run); + CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); + if (!CFEqual (run_ct_font, face_data->ct_font)) + { + /* The run doesn't use our main font instance. We have to figure out + * whether font fallback happened, or this is just CoreText giving us + * another CTFont using the same underlying CGFont. CoreText seems + * to do that in a variety of situations, one of which being vertical + * text, but also perhaps for caching reasons. + * + * First, see if it uses any of our subfonts created to set font features... + * + * Next, compare the CGFont to the one we used to create our fonts. + * Even this doesn't work all the time. + * + * Finally, we compare PS names, which I don't think are unique... + * + * Looks like if we really want to be sure here we have to modify the + * font to change the name table, similar to what we do in the uniscribe + * backend. + * + * However, even that wouldn't work if we were passed in the CGFont to + * construct a hb_face to begin with. + * + * See: http://github.com/behdad/harfbuzz/pull/36 + * + * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 + */ + bool matched = false; + for (unsigned int i = 0; i < range_records.len; i++) + if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) + { + matched = true; + break; + } + if (!matched) + { + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); + if (run_cg_font) + { + matched = CFEqual (run_cg_font, face_data->cg_font); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey); + CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); + CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); + CFRelease (run_ps_name); + CFRelease (font_ps_name); + if (result == kCFCompareEqualTo) + matched = true; + } + if (!matched) + { + CFRange range = CTRunGetStringRange (run); + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + range.location, range.location + range.length); + if (!buffer->ensure_inplace (buffer->len + range.length)) + goto resize_and_retry; + hb_glyph_info_t *info = buffer->info + buffer->len; + + hb_codepoint_t notdef = 0; + hb_direction_t dir = buffer->props.direction; + hb_position_t x_advance, y_advance, x_offset, y_offset; + hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); + hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); + hb_position_t advance = x_advance + y_advance; + x_offset = -x_offset; + y_offset = -y_offset; + + unsigned int old_len = buffer->len; + for (CFIndex j = range.location; j < range.location + range.length; j++) + { + UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); + if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j) + { + ch = CFStringGetCharacterAtIndex (string_ref, j - 1); + if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) + /* This is the second of a surrogate pair. Don't need .notdef + * for this one. */ + continue; + } + if (buffer->unicode->is_default_ignorable (ch)) + continue; + + info->codepoint = notdef; + info->cluster = log_clusters[j]; + + info->mask = advance; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; + + info++; + buffer->len++; + } + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + buffer->reverse_range (old_len, buffer->len); + advances_so_far += run_advance; + continue; + } + } + + unsigned int num_glyphs = CTRunGetGlyphCount (run); + if (num_glyphs == 0) + continue; + + if (!buffer->ensure_inplace (buffer->len + num_glyphs)) + goto resize_and_retry; + + hb_glyph_info_t *run_info = buffer->info + buffer->len; + + /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always + * succeed, and so copying data to our own buffer will be rare. Reports + * have it that this changed in OS X 10.10 Yosemite, and NULL is returned + * frequently. At any rate, we can test that codepath by setting USE_PTR + * to false. */ + +#define USE_PTR true + +#define SCRATCH_SAVE() \ + unsigned int scratch_size_saved = scratch_size; \ + hb_buffer_t::scratch_buffer_t *scratch_saved = scratch + +#define SCRATCH_RESTORE() \ + scratch_size = scratch_size_saved; \ + scratch = scratch_saved; + + { /* Setup glyphs */ + SCRATCH_SAVE(); + const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL; + if (!glyphs) { + ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); + CTRunGetGlyphs (run, range_all, glyph_buf); + glyphs = glyph_buf; + } + const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL; + if (!string_indices) { + ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); + CTRunGetStringIndices (run, range_all, index_buf); + string_indices = index_buf; + } + hb_glyph_info_t *info = run_info; + for (unsigned int j = 0; j < num_glyphs; j++) + { + info->codepoint = glyphs[j]; + info->cluster = log_clusters[string_indices[j]]; + info++; + } + SCRATCH_RESTORE(); + } + { + /* Setup positions. + * Note that CoreText does not return advances for glyphs. As such, + * for all but last glyph, we use the delta position to next glyph as + * advance (in the advance direction only), and for last glyph we set + * whatever is needed to make the whole run's advance add up. */ + SCRATCH_SAVE(); + const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL; + if (!positions) { + ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); + CTRunGetPositions (run, range_all, position_buf); + positions = position_buf; + } + hb_glyph_info_t *info = run_info; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].x - positions[j].x; + else /* last glyph */ + advance = run_advance - (positions[j].x - positions[0].x); + info->mask = advance * x_mult; + info->var1.i32 = x_offset; + info->var2.i32 = positions[j].y * y_mult; + info++; + } + } + else + { + hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].y - positions[j].y; + else /* last glyph */ + advance = run_advance - (positions[j].y - positions[0].y); + info->mask = advance * y_mult; + info->var1.i32 = positions[j].x * x_mult; + info->var2.i32 = y_offset; + info++; + } + } + SCRATCH_RESTORE(); + advances_so_far += run_advance; + } +#undef SCRATCH_RESTORE +#undef SCRATCH_SAVE +#undef USE_PTR +#undef ALLOCATE_ARRAY + + buffer->len += num_glyphs; + } + + /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, + * or if it does, it doesn't resepct it. So we get runs with wrong + * directions. As such, disable the assert... It wouldn't crash, but + * cursoring will be off... + * + * http://crbug.com/419769 + */ + if (0) + { + /* Make sure all runs had the expected direction. */ + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + assert (bool (status_and & kCTRunStatusRightToLeft) == backward); + assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + } + + buffer->clear_positions (); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned int i = 0; i < count; i++) + { + pos->x_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + info++, pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + info++, pos++; + } + + /* Fix up clusters so that we never return out-of-order indices; + * if core text has reordered glyphs, we'll merge them to the + * beginning of the reordered cluster. CoreText is nice enough + * to tell us whenever it has produced nonmonotonic results... + * Note that we assume the input clusters were nonmonotonic to + * begin with. + * + * This does *not* mean we'll form the same clusters as Uniscribe + * or the native OT backend, only that the cluster indices will be + * monotonic in the output buffer. */ + if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) + { + hb_glyph_info_t *info = buffer->info; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + unsigned int cluster = info[count - 1].cluster; + for (unsigned int i = count - 1; i > 0; i--) + { + cluster = MIN (cluster, info[i - 1].cluster); + info[i - 1].cluster = cluster; + } + } + else + { + unsigned int cluster = info[0].cluster; + for (unsigned int i = 1; i < count; i++) + { + cluster = MIN (cluster, info[i].cluster); + info[i].cluster = cluster; + } + } + } + } + +#undef FAIL + +fail: + if (string_ref) + CFRelease (string_ref); + if (line) + CFRelease (line); + + for (unsigned int i = 0; i < range_records.len; i++) + if (range_records[i].font) + CFRelease (range_records[i].font); + + return ret; +} + + +/* + * AAT shaper + */ + +/* + * shaper face data + */ + +struct hb_coretext_aat_shaper_face_data_t {}; + +hb_coretext_aat_shaper_face_data_t * +_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (mort_blob)) + { + hb_blob_destroy (mort_blob); + mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX); + if (!hb_blob_get_length (mort_blob)) + { + hb_blob_destroy (mort_blob); + return NULL; + } + } + hb_blob_destroy (mort_blob); + + return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; +} + +void +_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_coretext_aat_shaper_font_data_t {}; + +hb_coretext_aat_shaper_font_data_t * +_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) +{ + return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; +} + +void +_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper shape_plan data + */ + +struct hb_coretext_aat_shaper_shape_plan_data_t {}; + +hb_coretext_aat_shaper_shape_plan_data_t * +_hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); +} diff --git a/gfx/harfbuzz/src/hb-coretext.h b/gfx/harfbuzz/src/hb-coretext.h new file mode 100644 index 000000000..82066e4e0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-coretext.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2012 Mozilla Foundation. + * + * 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. + * + * Mozilla Author(s): Jonathan Kew + */ + +#ifndef HB_CORETEXT_H +#define HB_CORETEXT_H + +#include "hb.h" + +#include <TargetConditionals.h> +#if TARGET_OS_IPHONE +# include <CoreText/CoreText.h> +# include <CoreGraphics/CoreGraphics.h> +#else +# include <ApplicationServices/ApplicationServices.h> +#endif + +HB_BEGIN_DECLS + + +#define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') +#define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + + +HB_EXTERN hb_face_t * +hb_coretext_face_create (CGFontRef cg_font); + + +HB_EXTERN CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +HB_EXTERN CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/gfx/harfbuzz/src/hb-deprecated.h b/gfx/harfbuzz/src/hb-deprecated.h new file mode 100644 index 000000000..0398dfae6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-deprecated.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2013 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_DEPRECATED_H +#define HB_DEPRECATED_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + +#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS + +#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT + +typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + +HB_EXTERN void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +#endif + +HB_END_DECLS + +#endif /* HB_DEPRECATED_H */ diff --git a/gfx/harfbuzz/src/hb-directwrite.cc b/gfx/harfbuzz/src/hb-directwrite.cc new file mode 100644 index 000000000..b5c1113b1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-directwrite.cc @@ -0,0 +1,934 @@ +/* + * Copyright © 2015-2016 Ebrahim Byagowi + * + * 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. + */ + +#define HB_SHAPER directwrite +#include "hb-shaper-impl-private.hh" + +#include <DWrite_1.h> + +#include "hb-directwrite.h" + + +#ifndef HB_DEBUG_DIRECTWRITE +#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) +#endif + +HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face) +HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, font) + + +/* + * DirectWrite font stream helpers + */ + +// This is a font loader which provides only one font (unlike its original design). +// For a better implementation which was also source of this +// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla +class DWriteFontFileLoader : public IDWriteFontFileLoader +{ +private: + IDWriteFontFileStream *mFontFileStream; +public: + DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) { + mFontFileStream = fontFileStream; + } + + // IUnknown interface + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; } + IFACEMETHOD_(ULONG, AddRef)() { return 1; } + IFACEMETHOD_(ULONG, Release)() { return 1; } + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } +}; + +class DWriteFontFileStream : public IDWriteFontFileStream +{ +private: + uint8_t *mData; + uint32_t mSize; +public: + DWriteFontFileStream(uint8_t *aData, uint32_t aSize) + { + mData = aData; + mSize = aSize; + } + + // IUnknown interface + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; } + IFACEMETHOD_(ULONG, AddRef)() { return 1; } + IFACEMETHOD_(ULONG, Release)() { return 1; } + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void** fragmentContext) + { + // We are required to do bounds checking. + if (fileOffset + fragmentSize > mSize) { + return E_FAIL; + } + + // truncate the 64 bit fileOffset to size_t sized index into mData + size_t index = static_cast<size_t> (fileOffset); + + // We should be alive for the duration of this. + *fragmentStart = &mData[index]; + *fragmentContext = nullptr; + return S_OK; + } + + virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext) { } + + virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize) + { + *fileSize = mSize; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime) + { + return E_NOTIMPL; + } +}; + + +/* +* shaper face data +*/ + +struct hb_directwrite_shaper_face_data_t { + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + IDWriteFontFileStream *fontFileStream; + IDWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; +}; + +hb_directwrite_shaper_face_data_t * +_hb_directwrite_shaper_face_data_create(hb_face_t *face) +{ + hb_directwrite_shaper_face_data_t *data = + (hb_directwrite_shaper_face_data_t *) malloc (sizeof (hb_directwrite_shaper_face_data_t)); + if (unlikely (!data)) + return NULL; + + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + DWriteCreateFactory ( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory + ); + + HRESULT hr; + hb_blob_t *blob = hb_face_reference_blob (face); + IDWriteFontFileStream *fontFileStream = new DWriteFontFileStream ( + (uint8_t*) hb_blob_get_data (blob, NULL), hb_blob_get_length (blob)); + + IDWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \ + return false; \ + } HB_STMT_END; + + if (FAILED (hr)) { + FAIL ("Failed to load font file from data!"); + return false; + } + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + UINT32 numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) { + FAIL ("Font file is not supported."); + return false; + } + +#undef FAIL + + IDWriteFontFace *fontFace; + dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, + DWRITE_FONT_SIMULATIONS_NONE, &fontFace); + + data->dwriteFactory = dwriteFactory; + data->fontFile = fontFile; + data->fontFileStream = fontFileStream; + data->fontFileLoader = fontFileLoader; + data->fontFace = fontFace; + data->faceBlob = blob; + + return data; +} + +void +_hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t *data) +{ + if (data->fontFace) + data->fontFace->Release (); + if (data->fontFile) + data->fontFile->Release (); + if (data->dwriteFactory) { + if (data->fontFileLoader) + data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); + data->dwriteFactory->Release (); + } + if (data->fontFileLoader) + delete data->fontFileLoader; + if (data->fontFileStream) + delete data->fontFileStream; + if (data->faceBlob) + hb_blob_destroy (data->faceBlob); + if (data) + free (data); +} + + +/* + * shaper font data + */ + +struct hb_directwrite_shaper_font_data_t { +}; + +hb_directwrite_shaper_font_data_t * +_hb_directwrite_shaper_font_data_create (hb_font_t *font) +{ + if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL; + + hb_directwrite_shaper_font_data_t *data = + (hb_directwrite_shaper_font_data_t *) malloc (sizeof (hb_directwrite_shaper_font_data_t)); + if (unlikely (!data)) + return NULL; + + return data; +} + +void +_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data) +{ + free (data); +} + + +/* + * shaper shape_plan data + */ + +struct hb_directwrite_shaper_shape_plan_data_t {}; + +hb_directwrite_shaper_shape_plan_data_t * +_hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + +// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project +// but now is relicensed to MIT for HarfBuzz use +class TextAnalysis + : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink +{ +public: + + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; } + IFACEMETHOD_(ULONG, AddRef)() { return 1; } + IFACEMETHOD_(ULONG, Release)() { return 1; } + + // A single contiguous run of characters containing the same analysis + // results. + struct Run + { + uint32_t mTextStart; // starting text position of this run + uint32_t mTextLength; // number of contiguous code units covered + uint32_t mGlyphStart; // starting glyph in the glyphs array + uint32_t mGlyphCount; // number of glyphs associated with this run of + // text + DWRITE_SCRIPT_ANALYSIS mScript; + uint8_t mBidiLevel; + bool mIsSideways; + + inline bool ContainsTextPosition(uint32_t aTextPosition) const + { + return aTextPosition >= mTextStart + && aTextPosition < mTextStart + mTextLength; + } + + Run *nextRun; + }; + +public: + TextAnalysis(const wchar_t* text, + uint32_t textLength, + const wchar_t* localeName, + DWRITE_READING_DIRECTION readingDirection) + : mText(text) + , mTextLength(textLength) + , mLocaleName(localeName) + , mReadingDirection(readingDirection) + , mCurrentRun(NULL) { }; + + ~TextAnalysis() { + // delete runs, except mRunHead which is part of the TextAnalysis object + for (Run *run = mRunHead.nextRun; run;) { + Run *origRun = run; + run = run->nextRun; + free (origRun); + } + } + + STDMETHODIMP GenerateResults(IDWriteTextAnalyzer* textAnalyzer, + Run **runHead) { + // Analyzes the text using the script analyzer and returns + // the result as a series of runs. + + HRESULT hr = S_OK; + + // Initially start out with one result that covers the entire range. + // This result will be subdivided by the analysis processes. + mRunHead.mTextStart = 0; + mRunHead.mTextLength = mTextLength; + mRunHead.mBidiLevel = + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + mRunHead.nextRun = NULL; + mCurrentRun = &mRunHead; + + // Call each of the analyzers in sequence, recording their results. + if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) { + *runHead = &mRunHead; + } + + return hr; + } + + // IDWriteTextAnalysisSource implementation + + IFACEMETHODIMP GetTextAtPosition(uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition >= mTextLength) { + // No text at this position, valid query though. + *textString = NULL; + *textLength = 0; + } + else { + *textString = mText + textPosition; + *textLength = mTextLength - textPosition; + } + return S_OK; + } + + IFACEMETHODIMP GetTextBeforePosition(uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition == 0 || textPosition > mTextLength) { + // Either there is no text before here (== 0), or this + // is an invalid position. The query is considered valid thouh. + *textString = NULL; + *textLength = 0; + } + else { + *textString = mText; + *textLength = textPosition; + } + return S_OK; + } + + IFACEMETHODIMP_(DWRITE_READING_DIRECTION) + GetParagraphReadingDirection() { return mReadingDirection; } + + IFACEMETHODIMP GetLocaleName(uint32_t textPosition, + uint32_t* textLength, + wchar_t const** localeName) + { + return S_OK; + } + + IFACEMETHODIMP + GetNumberSubstitution(uint32_t textPosition, + OUT uint32_t* textLength, + OUT IDWriteNumberSubstitution** numberSubstitution) + { + // We do not support number substitution. + *numberSubstitution = NULL; + *textLength = mTextLength - textPosition; + + return S_OK; + } + + // IDWriteTextAnalysisSink implementation + + IFACEMETHODIMP + SetScriptAnalysis(uint32_t textPosition, + uint32_t textLength, + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) + { + SetCurrentRun(textPosition); + SplitCurrentRun(textPosition); + while (textLength > 0) + { + Run *run = FetchNextRun(&textLength); + run->mScript = *scriptAnalysis; + } + + return S_OK; + } + + IFACEMETHODIMP + SetLineBreakpoints(uint32_t textPosition, + uint32_t textLength, + const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; } + + IFACEMETHODIMP SetBidiLevel(uint32_t textPosition, + uint32_t textLength, + uint8_t explicitLevel, + uint8_t resolvedLevel) { return S_OK; } + + IFACEMETHODIMP + SetNumberSubstitution(uint32_t textPosition, + uint32_t textLength, + IDWriteNumberSubstitution* numberSubstitution) { return S_OK; } + +protected: + Run *FetchNextRun(IN OUT uint32_t* textLength) + { + // Used by the sink setters, this returns a reference to the next run. + // Position and length are adjusted to now point after the current run + // being returned. + + Run *origRun = mCurrentRun; + // Split the tail if needed (the length remaining is less than the + // current run's size). + if (*textLength < mCurrentRun->mTextLength) + { + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); + } + else + { + // Just advance the current run. + mCurrentRun = mCurrentRun->nextRun; + } + *textLength -= origRun->mTextLength; + + // Return a reference to the run that was just current. + return origRun; + } + + void SetCurrentRun(uint32_t textPosition) + { + // Move the current run to the given position. + // Since the analyzers generally return results in a forward manner, + // this will usually just return early. If not, find the + // corresponding run for the text position. + + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + { + return; + } + + for (Run *run = &mRunHead; run; run = run->nextRun) { + if (run->ContainsTextPosition (textPosition)) + { + mCurrentRun = run; + return; + } + } + //NS_NOTREACHED("We should always be able to find the text position in one \ + // of our runs"); + } + + void SplitCurrentRun(uint32_t splitPosition) + { + if (!mCurrentRun) + { + //NS_ASSERTION(false, "SplitCurrentRun called without current run."); + // Shouldn't be calling this when no current run is set! + return; + } + // Split the current run. + if (splitPosition <= mCurrentRun->mTextStart) + { + // No need to split, already the start of a run + // or before it. Usually the first. + return; + } + Run *newRun = (Run*) malloc (sizeof (Run)); + + *newRun = *mCurrentRun; + + // Insert the new run in our linked list. + newRun->nextRun = mCurrentRun->nextRun; + mCurrentRun->nextRun = newRun; + + // Adjust runs' text positions and lengths. + uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; + newRun->mTextStart += splitPoint; + newRun->mTextLength -= splitPoint; + mCurrentRun->mTextLength = splitPoint; + mCurrentRun = newRun; + } + +protected: + // Input + // (weak references are fine here, since this class is a transient + // stack-based helper that doesn't need to copy data) + uint32_t mTextLength; + const wchar_t* mText; + const wchar_t* mLocaleName; + DWRITE_READING_DIRECTION mReadingDirection; + + // Current processing state. + Run *mCurrentRun; + + // Output is a list of runs starting here + Run mRunHead; +}; + +static inline uint16_t hb_uint16_swap (const uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline uint32_t hb_uint32_swap (const uint32_t v) +{ return (hb_uint16_swap(v) << 16) | hb_uint16_swap(v >> 16); } + +/* + * shaper + */ + +static hb_bool_t +_hb_directwrite_shape_full(hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + float lineWidth) +{ + hb_face_t *face = font->face; + hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; + + IDWriteTextAnalyzer* analyzer; + dwriteFactory->CreateTextAnalyzer(&analyzer); + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY(wchar_t, textString, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index() = chars_len; + if (likely(c <= 0xFFFFu)) + textString[chars_len++] = c; + else if (unlikely(c > 0x10FFFFu)) + textString[chars_len++] = 0xFFFDu; + else { + textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY(WORD, log_clusters, chars_len); + // if (num_features) + { + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range(c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + } + + // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES + + DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ? + DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + /* + * There's an internal 16-bit limit on some things inside the analyzer, + * but we never attempt to shape a word longer than 64K characters + * in a single gfxShapedWord, so we cannot exceed that limit. + */ + uint32_t textLength = buffer->len; + + TextAnalysis analysis(textString, textLength, NULL, readingDirection); + TextAnalysis::Run *runHead; + HRESULT hr; + hr = analysis.GenerateResults(analyzer, &runHead); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \ + return false; \ + } HB_STMT_END; + + if (FAILED (hr)) + { + FAIL ("Analyzer failed to generate results."); + return false; + } + + uint32_t maxGlyphCount = 3 * textLength / 2 + 16; + uint32_t glyphCount; + bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + const wchar_t localeName[20] = {0}; + if (buffer->props.language != NULL) + { + mbstowcs ((wchar_t*) localeName, + hb_language_to_string (buffer->props.language), 20); + } + + DWRITE_TYPOGRAPHIC_FEATURES singleFeatures; + singleFeatures.featureCount = num_features; + if (num_features) + { + DWRITE_FONT_FEATURE* dwfeatureArray = (DWRITE_FONT_FEATURE*) + malloc (sizeof (DWRITE_FONT_FEATURE) * num_features); + for (unsigned int i = 0; i < num_features; ++i) + { + dwfeatureArray[i].nameTag = (DWRITE_FONT_FEATURE_TAG) + hb_uint32_swap (features[i].tag); + dwfeatureArray[i].parameter = features[i].value; + } + singleFeatures.features = dwfeatureArray; + } + const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures = + (const DWRITE_TYPOGRAPHIC_FEATURES*) &singleFeatures; + const uint32_t featureRangeLengths[] = { textLength }; + + uint16_t* clusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t)); + DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*) + malloc (textLength * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES)); +retry_getglyphs: + uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); + DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*) + malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES)); + + hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE, + isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures, + featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices, + glyphProperties, &glyphCount); + + if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) + { + free (glyphIndices); + free (glyphProperties); + + maxGlyphCount *= 2; + + goto retry_getglyphs; + } + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get glyphs."); + return false; + } + + float* glyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); + DWRITE_GLYPH_OFFSET* glyphOffsets = (DWRITE_GLYPH_OFFSET*) + malloc(maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof(WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof(int) - 2) + / (sizeof(WORD) + + sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof(int) + + sizeof(DWRITE_GLYPH_OFFSET) + + sizeof(uint32_t)); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + +#undef ALLOCATE_ARRAY + + int fontEmSize = font->face->get_upem(); + if (fontEmSize < 0) + fontEmSize = -fontEmSize; + + if (fontEmSize < 0) + fontEmSize = -fontEmSize; + double x_mult = (double) font->x_scale / fontEmSize; + double y_mult = (double) font->y_scale / fontEmSize; + + hr = analyzer->GetGlyphPlacements (textString, + clusterMap, textProperties, textLength, glyphIndices, + glyphProperties, glyphCount, fontFace, fontEmSize, + FALSE, isRightToLeft, &runHead->mScript, localeName, + &dwFeatures, featureRangeLengths, 1, + glyphAdvances, glyphOffsets); + + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get glyph placements."); + return false; + } + + IDWriteTextAnalyzer1* analyzer1; + analyzer->QueryInterface (&analyzer1); + + if (analyzer1 && lineWidth) + { + + DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = + (DWRITE_JUSTIFICATION_OPPORTUNITY*) + malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY)); + hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, + runHead->mScript, textLength, glyphCount, textString, clusterMap, + glyphProperties, justificationOpportunities); + + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get justification opportunities."); + return false; + } + + float* justifiedGlyphAdvances = + (float*) malloc (maxGlyphCount * sizeof (float)); + DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) + malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET)); + hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, + glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets); + + if (FAILED (hr)) + { + FAIL("Analyzer failed to get justified glyph advances."); + return false; + } + + DWRITE_SCRIPT_PROPERTIES scriptProperties; + hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties); + if (FAILED (hr)) + { + FAIL("Analyzer failed to get script properties."); + return false; + } + uint32_t justificationCharacter = scriptProperties.justificationCharacter; + + // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs + if (justificationCharacter != 32) + { + uint16_t* modifiedClusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t)); + retry_getjustifiedglyphs: + uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); + float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); + DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) + malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); + uint32_t actualGlyphsCount; + hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, + textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices, + glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets, + glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices, + modifiedGlyphAdvances, modifiedGlyphOffsets); + + if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) + { + maxGlyphCount = actualGlyphsCount; + free (modifiedGlyphIndices); + free (modifiedGlyphAdvances); + free (modifiedGlyphOffsets); + + maxGlyphCount = actualGlyphsCount; + + goto retry_getjustifiedglyphs; + } + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get justified glyphs."); + return false; + } + + free (clusterMap); + free (glyphIndices); + free (glyphAdvances); + free (glyphOffsets); + + glyphCount = actualGlyphsCount; + clusterMap = modifiedClusterMap; + glyphIndices = modifiedGlyphIndices; + glyphAdvances = modifiedGlyphAdvances; + glyphOffsets = modifiedGlyphOffsets; + + free (justifiedGlyphAdvances); + free (justifiedGlyphOffsets); + } + else + { + free (glyphAdvances); + free (glyphOffsets); + + glyphAdvances = justifiedGlyphAdvances; + glyphOffsets = justifiedGlyphOffsets; + } + + free (justificationOpportunities); + + } + + /* 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 < glyphCount; 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); + } + for (unsigned int i = 1; i < glyphCount; i++) + if (vis_clusters[i] == -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphCount))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphIndices[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = glyphAdvances[i]; + info->var1.i32 = glyphOffsets[i].advanceOffset; + info->var2.i32 = glyphOffsets[i].ascenderOffset; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = + x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (isRightToLeft) + hb_buffer_reverse (buffer); + + free (clusterMap); + free (glyphIndices); + free (textProperties); + free (glyphProperties); + free (glyphAdvances); + free (glyphOffsets); + + if (num_features) + free (singleFeatures.features); + + /* Wow, done! */ + return true; +} + +hb_bool_t +_hb_directwrite_shape(hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + return _hb_directwrite_shape_full(shape_plan, font, buffer, + features, num_features, 0); +} + +/* + * Public [experimental] API + */ + +hb_bool_t +hb_shape_dwrite_experimental_width(hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + float width) +{ + static char *shapers = "directwrite"; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, + &buffer->props, features, num_features, &shapers); + hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer, + features, num_features, width); + + if (res) + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + + return res; +} diff --git a/gfx/harfbuzz/src/hb-directwrite.h b/gfx/harfbuzz/src/hb-directwrite.h new file mode 100644 index 000000000..0eb116f4c --- /dev/null +++ b/gfx/harfbuzz/src/hb-directwrite.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2015 Ebrahim Byagowi + * + * 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_DIRECTWRITE_H +#define HB_DIRECTWRITE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +HB_EXTERN hb_bool_t +hb_shape_dwrite_experimental_width(hb_font_t *font, hb_buffer_t *buffer, + const hb_feature_t *features, unsigned int num_features, float width); + +HB_END_DECLS + +#endif /* HB_DIRECTWRITE_H */ diff --git a/gfx/harfbuzz/src/hb-face-private.hh b/gfx/harfbuzz/src/hb-face-private.hh new file mode 100644 index 000000000..c4266fff4 --- /dev/null +++ b/gfx/harfbuzz/src/hb-face-private.hh @@ -0,0 +1,107 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FACE_PRIVATE_HH +#define HB_FACE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-object-private.hh" +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" + + +/* + * hb_face_t + */ + +struct hb_face_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t immutable; + + hb_reference_table_func_t reference_table_func; + void *user_data; + hb_destroy_func_t destroy; + + unsigned int index; + mutable unsigned int upem; + mutable unsigned int num_glyphs; + + struct hb_shaper_data_t shaper_data; + + struct plan_node_t { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + } *shape_plans; + + + inline hb_blob_t *reference_table (hb_tag_t tag) const + { + hb_blob_t *blob; + + if (unlikely (!reference_table_func)) + return hb_blob_get_empty (); + + blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data); + if (unlikely (!blob)) + return hb_blob_get_empty (); + + return blob; + } + + inline HB_PURE_FUNC unsigned int get_upem (void) const + { + if (unlikely (!upem)) + load_upem (); + return upem; + } + + inline unsigned int get_num_glyphs (void) const + { + if (unlikely (num_glyphs == (unsigned int) -1)) + load_num_glyphs (); + return num_glyphs; + } + + private: + HB_INTERNAL void load_upem (void) const; + HB_INTERNAL void load_num_glyphs (void) const; +}; + +extern HB_INTERNAL const hb_face_t _hb_face_nil; + +#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS + + +#endif /* HB_FACE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-face.cc b/gfx/harfbuzz/src/hb-face.cc new file mode 100644 index 000000000..6b563bc8f --- /dev/null +++ b/gfx/harfbuzz/src/hb-face.cc @@ -0,0 +1,479 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ot-layout-private.hh" + +#include "hb-font-private.hh" +#include "hb-open-file-private.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" + +#include <string.h> + + +/* + * hb_face_t + */ + +const hb_face_t _hb_face_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* reference_table_func */ + NULL, /* user_data */ + NULL, /* destroy */ + + 0, /* index */ + 1000, /* upem */ + 0, /* num_glyphs */ + + { +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + }, + + NULL, /* shape_plans */ +}; + + +/** + * hb_face_create_for_tables: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!reference_table_func || !(face = hb_object_create<hb_face_t> ())) { + if (destroy) + destroy (user_data); + return hb_face_get_empty (); + } + + face->reference_table_func = reference_table_func; + face->user_data = user_data; + face->destroy = destroy; + + face->upem = 0; + face->num_glyphs = (unsigned int) -1; + + return face; +} + + +typedef struct hb_face_for_data_closure_t { + hb_blob_t *blob; + unsigned int index; +} hb_face_for_data_closure_t; + +static hb_face_for_data_closure_t * +_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) +{ + hb_face_for_data_closure_t *closure; + + closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return NULL; + + closure->blob = blob; + closure->index = index; + + return closure; +} + +static void +_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) +{ + hb_blob_destroy (closure->blob); + free (closure); +} + +static hb_blob_t * +_hb_face_for_data_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 OT::OpenTypeFontFile &ot_file = *OT::Sanitizer<OT::OpenTypeFontFile>::lock_instance (data->blob); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); + + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length); + + return blob; +} + +/** + * hb_face_create: (Xconstructor) + * @blob: + * @index: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob)) + blob = hb_blob_get_empty (); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index); + + if (unlikely (!closure)) + return hb_face_get_empty (); + + 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_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_get_empty (void) +{ + return const_cast<hb_face_t *> (&_hb_face_nil); +} + + +/** + * hb_face_reference: (skip) + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_destroy (hb_face_t *face) +{ + if (!hb_object_destroy (face)) return; + + for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) + { + hb_face_t::plan_node_t *next = node->next; + hb_shape_plan_destroy (node->shape_plan); + free (node); + node = next; + } + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + if (face->destroy) + face->destroy (face->user_data); + + free (face); +} + +/** + * hb_face_set_user_data: (skip) + * @face: a face. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (face, key, data, destroy, replace); +} + +/** + * hb_face_get_user_data: (skip) + * @face: a face. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_face_get_user_data (hb_face_t *face, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (face, key); +} + +/** + * hb_face_make_immutable: + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_make_immutable (hb_face_t *face) +{ + if (unlikely (hb_object_is_inert (face))) + return; + + face->immutable = true; +} + +/** + * hb_face_is_immutable: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_is_immutable (hb_face_t *face) +{ + return face->immutable; +} + + +/** + * hb_face_reference_table: + * @face: a face. + * @tag: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_table (hb_face_t *face, + hb_tag_t tag) +{ + return face->reference_table (tag); +} + +/** + * hb_face_reference_blob: + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) +{ + return face->reference_table (HB_TAG_NONE); +} + +/** + * hb_face_set_index: + * @face: a face. + * @index: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (face->immutable) + return; + + face->index = index; +} + +/** + * hb_face_get_index: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_index (hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_upem: + * @face: a face. + * @upem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (face->immutable) + return; + + face->upem = upem; +} + +/** + * hb_face_get_upem: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_upem (hb_face_t *face) +{ + return face->get_upem (); +} + +void +hb_face_t::load_upem (void) const +{ + hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (reference_table (HB_OT_TAG_head)); + const OT::head *head_table = OT::Sanitizer<OT::head>::lock_instance (head_blob); + upem = head_table->get_upem (); + hb_blob_destroy (head_blob); +} + +/** + * hb_face_set_glyph_count: + * @face: a face. + * @glyph_count: + * + * + * + * Since: 0.9.7 + **/ +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count) +{ + if (face->immutable) + return; + + face->num_glyphs = glyph_count; +} + +/** + * hb_face_get_glyph_count: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +unsigned int +hb_face_get_glyph_count (hb_face_t *face) +{ + return face->get_num_glyphs (); +} + +void +hb_face_t::load_num_glyphs (void) const +{ + hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>::sanitize (reference_table (HB_OT_TAG_maxp)); + const OT::maxp *maxp_table = OT::Sanitizer<OT::maxp>::lock_instance (maxp_blob); + num_glyphs = maxp_table->get_num_glyphs (); + hb_blob_destroy (maxp_blob); +} + + diff --git a/gfx/harfbuzz/src/hb-face.h b/gfx/harfbuzz/src/hb-face.h new file mode 100644 index 000000000..91237b708 --- /dev/null +++ b/gfx/harfbuzz/src/hb-face.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_FACE_H +#define HB_FACE_H + +#include "hb-common.h" +#include "hb-blob.h" + +HB_BEGIN_DECLS + + +/* + * hb_face_t + */ + +typedef struct hb_face_t hb_face_t; + +HB_EXTERN hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index); + +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_EXTERN hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_face_t * +hb_face_get_empty (void); + +HB_EXTERN hb_face_t * +hb_face_reference (hb_face_t *face); + +HB_EXTERN void +hb_face_destroy (hb_face_t *face); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_face_get_user_data (hb_face_t *face, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_face_make_immutable (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_is_immutable (hb_face_t *face); + + +HB_EXTERN hb_blob_t * +hb_face_reference_table (hb_face_t *face, + hb_tag_t tag); + +HB_EXTERN hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +HB_EXTERN void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +HB_EXTERN unsigned int +hb_face_get_index (hb_face_t *face); + +HB_EXTERN void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + +HB_EXTERN unsigned int +hb_face_get_upem (hb_face_t *face); + +HB_EXTERN void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count); + +HB_EXTERN unsigned int +hb_face_get_glyph_count (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_FACE_H */ diff --git a/gfx/harfbuzz/src/hb-fallback-shape.cc b/gfx/harfbuzz/src/hb-fallback-shape.cc new file mode 100644 index 000000000..ac6d4b00f --- /dev/null +++ b/gfx/harfbuzz/src/hb-fallback-shape.cc @@ -0,0 +1,143 @@ +/* + * 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 HB_SHAPER fallback +#include "hb-shaper-impl-private.hh" + + +/* + * shaper face data + */ + +struct hb_fallback_shaper_face_data_t {}; + +hb_fallback_shaper_face_data_t * +_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) +{ + return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_fallback_shaper_font_data_t {}; + +hb_fallback_shaper_font_data_t * +_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper shape_plan data + */ + +struct hb_fallback_shaper_shape_plan_data_t {}; + +hb_fallback_shaper_shape_plan_data_t * +_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + /* TODO + * + * - Apply fallback kern. + * - Handle Variation Selectors? + * - Apply normalization? + * + * This will make the fallback shaper into a dumb "TrueType" + * shaper which many people unfortunately still request. + */ + + hb_codepoint_t space; + bool has_space = (bool) font->get_nominal_glyph (' ', &space); + + buffer->clear_positions (); + + hb_direction_t direction = buffer->props.direction; + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + { + if (has_space && unicode->is_default_ignorable (info[i].codepoint)) { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + continue; + } + font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint); + font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + hb_buffer_reverse (buffer); + + return true; +} diff --git a/gfx/harfbuzz/src/hb-font-private.hh b/gfx/harfbuzz/src/hb-font-private.hh new file mode 100644 index 000000000..53671d78d --- /dev/null +++ b/gfx/harfbuzz/src/hb-font-private.hh @@ -0,0 +1,553 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_PRIVATE_HH +#define HB_FONT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-object-private.hh" +#include "hb-face-private.hh" +#include "hb-shaper-private.hh" + + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ + 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; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } destroy; + + /* Don't access these directly. Call font->get_*() instead. */ + union get_t { + struct get_funcs_t { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } f; + void (*array[VAR]) (void); + } get; +}; + + + +/* + * hb_font_t + */ + +struct hb_font_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t immutable; + + hb_font_t *parent; + hb_face_t *face; + + int x_scale; + int y_scale; + + unsigned int x_ppem; + unsigned int y_ppem; + + /* Font variation coordinates. */ + unsigned int num_coords; + int *coords; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + struct hb_shaper_data_t shaper_data; + + + /* Convert from font-space to user-space */ + inline int dir_scale (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } + inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } + inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } + inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } + inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } + inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_scale (v, dir_scale (direction)); } + + /* Convert from parent-font user-space to our user-space */ + inline hb_position_t parent_scale_x_distance (hb_position_t v) { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + inline hb_position_t parent_scale_y_distance (hb_position_t v) { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + inline hb_position_t parent_scale_x_position (hb_position_t v) { + return parent_scale_x_distance (v); + } + inline hb_position_t parent_scale_y_position (hb_position_t v) { + return parent_scale_y_distance (v); + } + + inline void parent_scale_distance (hb_position_t *x, hb_position_t *y) { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + inline void parent_scale_position (hb_position_t *x, hb_position_t *y) { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + + /* Public getters */ + + HB_INTERNAL bool has_func (unsigned int i); + + /* has_* ... */ +#define HB_FONT_FUNC_IMPLEMENT(name) \ + bool \ + has_##name##_func (void) \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func (i); \ + } + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + inline hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_h_extents (this, user_data, + extents, + klass->user_data.font_h_extents); + } + inline hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_v_extents (this, user_data, + extents, + klass->user_data.font_v_extents); + } + + inline bool has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_nominal_glyph (unicode, &glyph); + } + + inline hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.f.nominal_glyph (this, user_data, + unicode, glyph, + klass->user_data.nominal_glyph); + } + + inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.f.variation_glyph (this, user_data, + unicode, variation_selector, glyph, + klass->user_data.variation_glyph); + } + + inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_h_advance (this, user_data, + glyph, + klass->user_data.glyph_h_advance); + } + + inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_v_advance (this, user_data, + glyph, + klass->user_data.glyph_v_advance); + } + + inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_h_origin); + } + + inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_v_origin); + } + + inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) + { + return klass->get.f.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + klass->user_data.glyph_h_kerning); + } + + inline hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) + { + return klass->get.f.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + klass->user_data.glyph_v_kerning); + } + + inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + klass->user_data.glyph_extents); + } + + inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + klass->user_data.glyph_contour_point); + } + + inline hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.f.glyph_name (this, user_data, + glyph, + name, size, + klass->user_data.glyph_name); + } + + inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.f.glyph_from_name (this, user_data, + name, len, + glyph, + klass->user_data.glyph_from_name); + } + + + /* A bit higher-level, and with fallback */ + + inline void get_h_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_h_extents (extents)) + { + extents->ascender = y_scale * .8; + extents->descender = extents->ascender - y_scale; + extents->line_gap = 0; + } + } + inline void get_v_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_v_extents (extents)) + { + extents->ascender = x_scale / 2; + extents->descender = extents->ascender - x_scale; + extents->line_gap = 0; + } + } + + inline void get_extents_for_direction (hb_direction_t direction, + hb_font_extents_t *extents) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); + } + + inline void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *x = get_glyph_h_advance (glyph); + *y = 0; + } else { + *x = 0; + *y = get_glyph_v_advance (glyph); + } + } + + inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + /* TODO cache this somehow?! */ + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + + inline void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_origin_with_fallback (glyph, x, y); + else + get_glyph_v_origin_with_fallback (glyph, x, y); + } + + inline void add_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + inline void add_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + inline void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + inline void subtract_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + inline void subtract_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *x = get_glyph_h_kerning (first_glyph, second_glyph); + *y = 0; + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + inline void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + inline hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_nominal_glyph (unichar, glyph)) + return true; + } + + return false; + } + + inline hb_position_t em_scale (int16_t v, int scale) + { + int upem = face->get_upem (); + int64_t scaled = v * (int64_t) scale; + scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ + return (hb_position_t) (scaled / upem); + } + inline hb_position_t em_scalef (float v, int scale) + { + return (hb_position_t) (v * scale / face->get_upem ()); + } +}; + +#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS + + +#endif /* HB_FONT_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-font.cc b/gfx/harfbuzz/src/hb-font.cc new file mode 100644 index 000000000..2935c4b4f --- /dev/null +++ b/gfx/harfbuzz/src/hb-font.cc @@ -0,0 +1,1698 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ot-layout-private.hh" + +#include "hb-font-private.hh" +#include "hb-open-file-private.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" + +#include <string.h> + + +/* + * hb_font_funcs_t + */ + +static hb_bool_t +hb_font_get_font_h_extents_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + memset (metrics, 0, sizeof (*metrics)); + return false; +} +static hb_bool_t +hb_font_get_font_h_extents_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_h_extents (metrics); + if (ret) { + metrics->ascender = font->parent_scale_y_distance (metrics->ascender); + metrics->descender = font->parent_scale_y_distance (metrics->descender); + metrics->line_gap = font->parent_scale_y_distance (metrics->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_font_v_extents_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + memset (metrics, 0, sizeof (*metrics)); + return false; +} +static hb_bool_t +hb_font_get_font_v_extents_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_v_extents (metrics); + if (ret) { + metrics->ascender = font->parent_scale_x_distance (metrics->ascender); + metrics->descender = font->parent_scale_x_distance (metrics->descender); + metrics->line_gap = font->parent_scale_x_distance (metrics->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_nominal_glyph_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_nominal_glyph (unicode, glyph); +} + +static hb_bool_t +hb_font_get_variation_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) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_variation_glyph_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_variation_glyph (unicode, variation_selector, glyph); +} + + +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) +{ + return font->x_scale; +} +static hb_position_t +hb_font_get_glyph_h_advance_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); +} + +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) +{ + /* TODO use font_extents.ascender+descender */ + return font->y_scale; +} +static hb_position_t +hb_font_get_glyph_v_advance_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); +} + +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) +{ + *x = *y = 0; + return true; +} +static hb_bool_t +hb_font_get_glyph_h_origin_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +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) +{ + *x = *y = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_v_origin_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +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) +{ + return 0; +} +static hb_position_t +hb_font_get_glyph_h_kerning_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); +} + +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) +{ + return 0; +} +static hb_position_t +hb_font_get_glyph_v_kerning_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); +} + +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) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} +static hb_bool_t +hb_font_get_glyph_extents_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + if (ret) { + font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); + font->parent_scale_distance (&extents->width, &extents->height); + } + return ret; +} + +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) +{ + *x = *y = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_contour_point_parent (hb_font_t *font, + 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) +{ + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_bool_t +hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + if (size) *name = '\0'; + return false; +} +static hb_bool_t +hb_font_get_glyph_name_parent (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + return font->parent->get_glyph_name (glyph, name, size); +} + +static hb_bool_t +hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_from_name_parent (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) +{ + return font->parent->get_glyph_from_name (name, len, glyph); +} + +static const hb_font_funcs_t _hb_font_funcs_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) NULL, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { +#define HB_FONT_FUNC_IMPLEMENT(name) NULL, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; +static const hb_font_funcs_t _hb_font_funcs_parent = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) NULL, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { +#define HB_FONT_FUNC_IMPLEMENT(name) NULL, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + + +/** + * hb_font_funcs_create: (Xconstructor) + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_create (void) +{ + hb_font_funcs_t *ffuncs; + + if (!(ffuncs = hb_object_create<hb_font_funcs_t> ())) + return hb_font_funcs_get_empty (); + + ffuncs->get = _hb_font_funcs_parent.get; + + return ffuncs; +} + +/** + * hb_font_funcs_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_get_empty (void) +{ + return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_parent); +} + +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs) +{ + return hb_object_reference (ffuncs); +} + +/** + * hb_font_funcs_destroy: (skip) + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) +{ + if (!hb_object_destroy (ffuncs)) return; + +#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + free (ffuncs); +} + +/** + * hb_font_funcs_set_user_data: (skip) + * @ffuncs: font functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); +} + +/** + * hb_font_funcs_get_user_data: (skip) + * @ffuncs: font functions. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ffuncs, key); +} + + +/** + * hb_font_funcs_make_immutable: + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) +{ + if (unlikely (hb_object_is_inert (ffuncs))) + return; + + ffuncs->immutable = true; +} + +/** + * hb_font_funcs_is_immutable: + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) +{ + return ffuncs->immutable; +} + + +#define HB_FONT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ + hb_font_get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (ffuncs->immutable) { \ + if (destroy) \ + destroy (user_data); \ + return; \ + } \ + \ + if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); \ + \ + if (func) { \ + ffuncs->get.f.name = func; \ + ffuncs->user_data.name = user_data; \ + ffuncs->destroy.name = destroy; \ + } else { \ + ffuncs->get.f.name = hb_font_get_##name##_parent; \ + ffuncs->user_data.name = NULL; \ + ffuncs->destroy.name = NULL; \ + } \ +} + +HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + +bool +hb_font_t::has_func (unsigned int i) +{ + if (parent && parent != hb_font_get_empty () && parent->has_func (i)) + return true; + return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i]; +} + +/* Public getters */ + +/** + * hb_font_get_h_extents: + * @font: a font. + * @extents: (out): + * + * + * + * Return value: + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_h_extents (extents); +} + +/** + * hb_font_get_v_extents: + * @font: a font. + * @extents: (out): + * + * + * + * Return value: + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_v_extents (extents); +} + +/** + * hb_font_get_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + if (unlikely (variation_selector)) + return font->get_variation_glyph (unicode, variation_selector, glyph); + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyph: + * @font: a font. + * @unicode: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph) +{ + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_variation_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + return font->get_variation_glyph (unicode, variation_selector, glyph); +} + +/** + * hb_font_get_glyph_h_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_h_advance (glyph); +} + +/** + * hb_font_get_glyph_v_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_v_advance (glyph); +} + +/** + * hb_font_get_glyph_h_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_h_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_v_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_v_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_h_kerning: + * @font: a font. + * @left_glyph: + * @right_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) +{ + return font->get_glyph_h_kerning (left_glyph, right_glyph); +} + +/** + * hb_font_get_glyph_v_kerning: + * @font: a font. + * @top_glyph: + * @bottom_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) +{ + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); +} + +/** + * hb_font_get_glyph_extents: + * @font: a font. + * @glyph: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents (glyph, extents); +} + +/** + * hb_font_get_glyph_contour_point: + * @font: a font. + * @glyph: + * @point_index: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +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) +{ + return font->get_glyph_contour_point (glyph, point_index, x, y); +} + +/** + * hb_font_get_glyph_name: + * @font: a font. + * @glyph: + * @name: (array length=size): + * @size: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size) +{ + return font->get_glyph_name (glyph, name, size); +} + +/** + * hb_font_get_glyph_from_name: + * @font: a font. + * @name: (array length=len): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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->get_glyph_from_name (name, len, glyph); +} + + +/* A bit higher-level, and with fallback */ + +/** + * hb_font_get_extents_for_direction: + * @font: a font. + * @direction: + * @extents: + * + * + * + * Since: 1.1.3 + **/ +void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents) +{ + return font->get_extents_for_direction (direction, extents); +} +/** + * hb_font_get_glyph_advance_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_advance_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_add_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->add_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_subtract_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_kerning_for_direction: + * @font: a font. + * @first_glyph: + * @second_glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_extents_for_origin: + * @font: a font. + * @glyph: + * @direction: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents_for_origin (glyph, direction, extents); +} + +/** + * hb_font_get_glyph_contour_point_for_origin: + * @font: a font. + * @glyph: + * @point_index: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); +} + +/* Generates gidDDD if glyph has no name. */ +/** + * hb_font_glyph_to_string: + * @font: a font. + * @glyph: + * @s: (array length=size): + * @size: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size) +{ + font->glyph_to_string (glyph, s, size); +} + +/* Parses gidDDD and uniUUUU strings automatically. */ +/** + * hb_font_glyph_from_string: + * @font: a font. + * @s: (array length=len) (element-type uint8_t): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->glyph_from_string (s, len, glyph); +} + + +/* + * hb_font_t + */ + +/** + * hb_font_create: (Xconstructor) + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font; + + if (unlikely (!face)) + face = hb_face_get_empty (); + if (!(font = hb_object_create<hb_font_t> ())) + return hb_font_get_empty (); + + hb_face_make_immutable (face); + font->parent = hb_font_get_empty (); + font->face = hb_face_reference (face); + font->klass = hb_font_funcs_get_empty (); + + font->x_scale = font->y_scale = hb_face_get_upem (face); + + return font; +} + +/** + * hb_font_create_sub_font: + * @parent: parent font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create_sub_font (hb_font_t *parent) +{ + if (unlikely (!parent)) + parent = hb_font_get_empty (); + + hb_font_t *font = hb_font_create (parent->face); + + if (unlikely (hb_object_is_inert (font))) + return font; + + font->parent = hb_font_reference (parent); + + font->x_scale = parent->x_scale; + font->y_scale = parent->y_scale; + font->x_ppem = parent->x_ppem; + font->y_ppem = parent->y_ppem; + + /* TODO: copy variation coordinates. */ + + return font; +} + +/** + * hb_font_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_empty (void) +{ + static const hb_font_t _hb_font_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* parent */ + const_cast<hb_face_t *> (&_hb_face_nil), + + 1000, /* x_scale */ + 1000, /* y_scale */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + + 0, /* num_coords */ + NULL, /* coords */ + + const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */ + NULL, /* user_data */ + NULL, /* destroy */ + + { +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + }; + + return const_cast<hb_font_t *> (&_hb_font_nil); +} + +/** + * hb_font_reference: (skip) + * @font: a font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_reference (hb_font_t *font) +{ + return hb_object_reference (font); +} + +/** + * hb_font_destroy: (skip) + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_destroy (hb_font_t *font) +{ + if (!hb_object_destroy (font)) return; + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + if (font->destroy) + font->destroy (font->user_data); + + hb_font_destroy (font->parent); + hb_face_destroy (font->face); + hb_font_funcs_destroy (font->klass); + + free (font->coords); + + free (font); +} + +/** + * hb_font_set_user_data: (skip) + * @font: a font. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (font, key, data, destroy, replace); +} + +/** + * hb_font_get_user_data: (skip) + * @font: a font. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (font, key); +} + +/** + * hb_font_make_immutable: + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_make_immutable (hb_font_t *font) +{ + if (unlikely (hb_object_is_inert (font))) + return; + + if (font->parent) + hb_font_make_immutable (font->parent); + + font->immutable = true; +} + +/** + * hb_font_is_immutable: + * @font: a font. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_is_immutable (hb_font_t *font) +{ + return font->immutable; +} + +/** + * hb_font_set_parent: + * @font: a font. + * @parent: new parent. + * + * Sets parent font of @font. + * + * Since: 1.0.5 + **/ +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent) +{ + if (font->immutable) + return; + + if (!parent) + parent = hb_font_get_empty (); + + hb_font_t *old = font->parent; + + font->parent = hb_font_reference (parent); + + hb_font_destroy (old); +} + +/** + * hb_font_get_parent: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_parent (hb_font_t *font) +{ + return font->parent; +} + +/** + * hb_font_get_face: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_font_get_face (hb_font_t *font) +{ + return font->face; +} + + +/** + * hb_font_set_funcs: + * @font: a font. + * @klass: (closure font_data) (destroy destroy) (scope notified): + * @font_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy) +{ + if (font->immutable) { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + if (!klass) + klass = hb_font_funcs_get_empty (); + + hb_font_funcs_reference (klass); + hb_font_funcs_destroy (font->klass); + font->klass = klass; + font->user_data = font_data; + font->destroy = destroy; +} + +/** + * hb_font_set_funcs_data: + * @font: a font. + * @font_data: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy) +{ + /* Destroy user_data? */ + if (font->immutable) { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = font_data; + font->destroy = destroy; +} + + +/** + * hb_font_set_scale: + * @font: a font. + * @x_scale: + * @y_scale: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale) +{ + if (font->immutable) + return; + + font->x_scale = x_scale; + font->y_scale = y_scale; +} + +/** + * hb_font_get_scale: + * @font: a font. + * @x_scale: (out): + * @y_scale: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale) +{ + if (x_scale) *x_scale = font->x_scale; + if (y_scale) *y_scale = font->y_scale; +} + +/** + * hb_font_set_ppem: + * @font: a font. + * @x_ppem: + * @y_ppem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) +{ + if (font->immutable) + return; + + font->x_ppem = x_ppem; + font->y_ppem = y_ppem; +} + +/** + * hb_font_get_ppem: + * @font: a font. + * @x_ppem: (out): + * @y_ppem: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem) +{ + if (x_ppem) *x_ppem = font->x_ppem; + if (y_ppem) *y_ppem = font->y_ppem; +} + + +void +hb_font_set_var_coords_normalized (hb_font_t *font, + int *coords, /* XXX 2.14 normalized */ + unsigned int coords_length) +{ + if (font->immutable) + return; + + /* Skip tail zero entries. */ + while (coords_length && !coords[coords_length - 1]) + coords_length--; + + int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : NULL; + if (unlikely (coords_length && !copy)) + return; + + free (font->coords); + + if (coords_length) + memcpy (copy, coords, coords_length * sizeof (coords[0])); + + font->coords = copy; + font->num_coords = coords_length; +} + + +#ifndef HB_DISABLE_DEPRECATED + +/* + * Deprecated get_glyph_func(): + */ + +struct hb_trampoline_closure_t +{ + void *user_data; + hb_destroy_func_t destroy; + unsigned int ref_count; +}; + +template <typename FuncType> +struct hb_trampoline_t +{ + hb_trampoline_closure_t closure; /* Must be first. */ + FuncType func; +}; + +template <typename FuncType> +static hb_trampoline_t<FuncType> * +trampoline_create (FuncType func, + void *user_data, + hb_destroy_func_t destroy) +{ + typedef hb_trampoline_t<FuncType> trampoline_t; + + trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); + + if (unlikely (!trampoline)) + return NULL; + + trampoline->closure.user_data = user_data; + trampoline->closure.destroy = destroy; + trampoline->closure.ref_count = 1; + trampoline->func = func; + + return trampoline; +} + +static void +trampoline_reference (hb_trampoline_closure_t *closure) +{ + closure->ref_count++; +} + +static void +trampoline_destroy (void *user_data) +{ + hb_trampoline_closure_t *closure = (hb_trampoline_closure_t *) user_data; + + if (--closure->ref_count) + return; + + if (closure->destroy) + closure->destroy (closure->user_data); + free (closure); +} + +typedef hb_trampoline_t<hb_font_get_glyph_func_t> hb_font_get_glyph_trampoline_t; + +static hb_bool_t +hb_font_get_nominal_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data); +} + +static hb_bool_t +hb_font_get_variation_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data); +} + +/** + * hb_font_funcs_set_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and + * hb_font_funcs_set_variation_glyph_func() instead. + * + * Since: 0.9.2 + * Deprecated: 1.2.3 + **/ +void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy) +{ + hb_font_get_glyph_trampoline_t *trampoline; + + trampoline = trampoline_create (func, user_data, destroy); + if (unlikely (!trampoline)) + { + if (destroy) + destroy (user_data); + return; + } + + hb_font_funcs_set_nominal_glyph_func (ffuncs, + hb_font_get_nominal_glyph_trampoline, + trampoline, + trampoline_destroy); + + trampoline_reference (&trampoline->closure); + hb_font_funcs_set_variation_glyph_func (ffuncs, + hb_font_get_variation_glyph_trampoline, + trampoline, + trampoline_destroy); +} + +#endif /* HB_DISABLE_DEPRECATED */ diff --git a/gfx/harfbuzz/src/hb-font.h b/gfx/harfbuzz/src/hb-font.h new file mode 100644 index 000000000..881328672 --- /dev/null +++ b/gfx/harfbuzz/src/hb-font.h @@ -0,0 +1,614 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_FONT_H +#define HB_FONT_H + +#include "hb-common.h" +#include "hb-face.h" + +HB_BEGIN_DECLS + + +typedef struct hb_font_t hb_font_t; + + +/* + * hb_font_funcs_t + */ + +typedef struct hb_font_funcs_t hb_font_funcs_t; + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_create (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_get_empty (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs); + +HB_EXTERN void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); + + +/* font and glyph extents */ + +/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */ +typedef struct hb_font_extents_t +{ + hb_position_t ascender; /* typographic ascender. */ + hb_position_t descender; /* typographic descender. */ + hb_position_t line_gap; /* suggested line spacing gap. */ + /*< private >*/ + hb_position_t reserved9; + hb_position_t reserved8; + hb_position_t reserved7; + hb_position_t reserved6; + hb_position_t reserved5; + hb_position_t reserved4; + hb_position_t reserved3; + hb_position_t reserved2; + hb_position_t reserved1; +} hb_font_extents_t; + +/* Note that height is negative in coordinate systems that grow up. */ +typedef struct hb_glyph_extents_t +{ + hb_position_t x_bearing; /* left side of glyph from origin. */ + hb_position_t y_bearing; /* top side of glyph from origin. */ + hb_position_t width; /* distance from left to right side. */ + hb_position_t height; /* distance from top to bottom side. */ +} hb_glyph_extents_t; + +/* func types */ + +typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data, + hb_font_extents_t *metrics, + void *user_data); +typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; +typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; + + +typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data); +typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + + +typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data); +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; + +typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data); +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; + +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + + +typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + 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 */ + +/** + * hb_font_funcs_set_font_h_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_h_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_font_v_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_v_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_variation_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_variation_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_contour_point_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +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); + +/** + * hb_font_funcs_set_glyph_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_from_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_from_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* func dispatch */ + +HB_EXTERN hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents); +HB_EXTERN hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph); +HB_EXTERN hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph); +HB_EXTERN hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); +HB_EXTERN hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +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_EXTERN hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +HB_EXTERN 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 */ + +/* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, + * otherwise callse hb_font_get_variation_glyph(). */ +HB_EXTERN hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents); +HB_EXTERN void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +/* Generates gidDDD if glyph has no name. */ +HB_EXTERN void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size); +/* Parses gidDDD and uniUUUU strings automatically. */ +HB_EXTERN hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* + * hb_font_t + */ + +/* Fonts are very light-weight objects */ + +HB_EXTERN hb_font_t * +hb_font_create (hb_face_t *face); + +HB_EXTERN hb_font_t * +hb_font_create_sub_font (hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_empty (void); + +HB_EXTERN hb_font_t * +hb_font_reference (hb_font_t *font); + +HB_EXTERN void +hb_font_destroy (hb_font_t *font); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_font_make_immutable (hb_font_t *font); + +HB_EXTERN hb_bool_t +hb_font_is_immutable (hb_font_t *font); + +HB_EXTERN void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_parent (hb_font_t *font); + +HB_EXTERN hb_face_t * +hb_font_get_face (hb_font_t *font); + + +HB_EXTERN void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy); + +/* Be *very* careful with this function! */ +HB_EXTERN void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy); + + +HB_EXTERN void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale); + +HB_EXTERN void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale); + +/* + * A zero value means "no hinting in that direction" + */ +HB_EXTERN void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem); + +HB_EXTERN void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem); + + +HB_EXTERN void +hb_font_set_var_coords_normalized (hb_font_t *font, + int *coords, /* XXX 2.14 normalized */ + unsigned int coords_length); + +HB_END_DECLS + +#endif /* HB_FONT_H */ diff --git a/gfx/harfbuzz/src/hb-ft.cc b/gfx/harfbuzz/src/hb-ft.cc new file mode 100644 index 000000000..f127066a6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ft.cc @@ -0,0 +1,744 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ft.h" + +#include "hb-font-private.hh" + +#include "hb-cache-private.hh" // Maybe use in the future? + +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_TRUETYPE_TABLES_H + + + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy. + * Have not investigated. + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves, like we do in uniscribe, etc. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + * + * - FT_Load_Glyph() is exteremely costly. Do something about it? + */ + + +struct hb_ft_font_t +{ + FT_Face ft_face; + int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ + bool unref; /* Whether to destroy ft_face when done. */ +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); + + if (unlikely (!ft_font)) + return NULL; + + ft_font->ft_face = ft_face; + ft_font->symbol = symbol; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + return ft_font; +} + +static void +_hb_ft_face_destroy (FT_Face ft_face) +{ + FT_Done_Face (ft_face); +} + +static void +_hb_ft_font_destroy (hb_ft_font_t *ft_font) +{ + if (ft_font->unref) + _hb_ft_face_destroy (ft_font->ft_face); + + free (ft_font); +} + +/** + * hb_ft_font_set_load_flags: + * @font: + * @load_flags: + * + * + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (font->immutable) + return; + + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: + * + * + * + * Return value: + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return NULL; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + + + +static hb_bool_t +hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); + + if (unlikely (!g)) + { + if (unlikely (ft_font->symbol) && unicode <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * http://www.microsoft.com/typography/otspec/recom.htm + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + if (!g) + return false; + } + else + return false; + } + + *glyph = g; + return true; +} + +static hb_bool_t +hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static hb_position_t +hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Fixed v; + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags, &v))) + return 0; + + if (font->x_scale < 0) + v = -v; + + return (v + (1<<9)) >> 10; +} + +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Fixed v; + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + if (font->y_scale < 0) + v = -v; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + return (-v + (1<<9)) >> 10; +} + +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + 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); + + if (font->x_scale < 0) + *x = -*x; + if (font->y_scale < 0) + *y = -*y; + + return true; +} + +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + 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; + if (font->x_scale < 0) + { + extents->x_bearing = -extents->x_bearing; + extents->width = -extents->width; + } + if (font->y_scale < 0) + { + extents->y_bearing = -extents->y_bearing; + extents->height = -extents->height; + } + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + 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); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + +static hb_bool_t +hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + metrics->ascender = ft_face->size->metrics.ascender; + metrics->descender = ft_face->size->metrics.descender; + metrics->line_gap = ft_face->size->metrics.height - (ft_face->size->metrics.ascender - ft_face->size->metrics.descender); + if (font->y_scale < 0) + { + metrics->ascender = -metrics->ascender; + metrics->descender = -metrics->descender; + metrics->line_gap = -metrics->line_gap; + } + return true; +} + +static hb_font_funcs_t *static_ft_funcs = NULL; + +#ifdef HB_USE_ATEXIT +static +void free_static_ft_funcs (void) +{ + hb_font_funcs_destroy (static_ft_funcs); +} +#endif + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ +retry: + hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs); + + if (unlikely (!funcs)) + { + funcs = hb_font_funcs_create (); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, NULL, NULL); + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, NULL, NULL); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, NULL, NULL); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, NULL, NULL); + hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, NULL, NULL); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, NULL, NULL); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, NULL, NULL); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, NULL, NULL); + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, NULL, NULL); + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, NULL, NULL); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, NULL, NULL); + hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, NULL, NULL); + hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, NULL, NULL); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, NULL, NULL); + + hb_font_funcs_make_immutable (funcs); + + if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, NULL, funcs)) { + hb_font_funcs_destroy (funcs); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_static_ft_funcs); /* First person registers atexit() callback. */ +#endif + }; + + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + + hb_font_set_funcs (font, + funcs, + _hb_ft_font_create (ft_face, symbol, unref), + (hb_destroy_func_t) _hb_ft_font_destroy); +} + + +static hb_blob_t * +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; + + /* 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) + return NULL; + + buffer = (FT_Byte *) malloc (length); + if (buffer == NULL) + return NULL; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + return NULL; + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (ft_face->stream->read == NULL) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + 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; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, (hb_destroy_func_t) _hb_ft_face_destroy); +} + +static void +hb_ft_face_finalize (FT_Face ft_face) +{ + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, NULL); + ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + +#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES + FT_MM_Var *mm_var = NULL; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed coords[mm_var->num_axis]; + int hbCoords[mm_var->num_axis]; + if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, coords)) + { + for (int i = 0; i < mm_var->num_axis; ++i) + hbCoords[i] = coords[i] >> 2; + + hb_font_set_var_coords_normalized (font, hbCoords, mm_var->num_axis); + } + } + free (mm_var); +#endif + + return font; +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, (hb_destroy_func_t) _hb_ft_face_destroy); +} + + +/* Thread-safe, lock-free, FT_Library */ + +static FT_Library ft_library; + +#ifdef HB_USE_ATEXIT +static +void free_ft_library (void) +{ + FT_Done_FreeType (ft_library); +} +#endif + +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 HB_USE_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; + } + + if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE)) + FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL); + + FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0); +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale); +#endif + if (font->x_scale < 0 || font->y_scale < 0) + { + FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, + 0, font->y_scale < 0 ? -1 : +1}; + FT_Set_Transform (ft_face, &matrix, NULL); + } + + ft_face->generic.data = blob; + ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); +} diff --git a/gfx/harfbuzz/src/hb-ft.h b/gfx/harfbuzz/src/hb-ft.h new file mode 100644 index 000000000..dc8ef8558 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ft.h @@ -0,0 +1,126 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2015 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_FT_H +#define HB_FT_H + +#include "hb.h" + +#include <ft2build.h> +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +HB_EXTERN hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +HB_EXTERN hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +HB_EXTERN int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. */ +HB_EXTERN void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/gfx/harfbuzz/src/hb-glib.cc b/gfx/harfbuzz/src/hb-glib.cc new file mode 100644 index 000000000..2b91b5b65 --- /dev/null +++ b/gfx/harfbuzz/src/hb-glib.cc @@ -0,0 +1,402 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-glib.h" + +#include "hb-unicode-private.hh" + + +#if !GLIB_CHECK_VERSION(2,29,14) +static const hb_script_t +glib_script_to_script[] = +{ + HB_SCRIPT_COMMON, + HB_SCRIPT_INHERITED, + HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, + HB_SCRIPT_BENGALI, + HB_SCRIPT_BOPOMOFO, + HB_SCRIPT_CHEROKEE, + HB_SCRIPT_COPTIC, + HB_SCRIPT_CYRILLIC, + HB_SCRIPT_DESERET, + HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_ETHIOPIC, + HB_SCRIPT_GEORGIAN, + HB_SCRIPT_GOTHIC, + HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, + HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HAN, + HB_SCRIPT_HANGUL, + HB_SCRIPT_HEBREW, + HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, + HB_SCRIPT_KATAKANA, + HB_SCRIPT_KHMER, + HB_SCRIPT_LAO, + HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, + HB_SCRIPT_MONGOLIAN, + HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, + HB_SCRIPT_OLD_ITALIC, + HB_SCRIPT_ORIYA, + HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, + HB_SCRIPT_SYRIAC, + HB_SCRIPT_TAMIL, + HB_SCRIPT_TELUGU, + HB_SCRIPT_THAANA, + HB_SCRIPT_THAI, + HB_SCRIPT_TIBETAN, + HB_SCRIPT_CANADIAN_SYLLABICS, + HB_SCRIPT_YI, + HB_SCRIPT_TAGALOG, + HB_SCRIPT_HANUNOO, + HB_SCRIPT_BUHID, + HB_SCRIPT_TAGBANWA, + + /* Unicode-4.0 additions */ + HB_SCRIPT_BRAILLE, + HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, + HB_SCRIPT_OSMANYA, + HB_SCRIPT_SHAVIAN, + HB_SCRIPT_LINEAR_B, + HB_SCRIPT_TAI_LE, + HB_SCRIPT_UGARITIC, + + /* Unicode-4.1 additions */ + HB_SCRIPT_NEW_TAI_LUE, + HB_SCRIPT_BUGINESE, + HB_SCRIPT_GLAGOLITIC, + HB_SCRIPT_TIFINAGH, + HB_SCRIPT_SYLOTI_NAGRI, + HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_KHAROSHTHI, + + /* Unicode-5.0 additions */ + HB_SCRIPT_UNKNOWN, + HB_SCRIPT_BALINESE, + HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_PHOENICIAN, + HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_NKO, + + /* Unicode-5.1 additions */ + HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, + HB_SCRIPT_REJANG, + HB_SCRIPT_SUNDANESE, + HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_CHAM, + HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_VAI, + HB_SCRIPT_CARIAN, + HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN, + + /* Unicode-5.2 additions */ + HB_SCRIPT_AVESTAN, + HB_SCRIPT_BAMUM, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, + HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_JAVANESE, + HB_SCRIPT_KAITHI, + HB_SCRIPT_TAI_THAM, + HB_SCRIPT_LISU, + HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_OLD_SOUTH_ARABIAN, + HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_SAMARITAN, + HB_SCRIPT_TAI_VIET, + + /* Unicode-6.0 additions */ + HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, + 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 + +hb_script_t +hb_glib_script_to_script (GUnicodeScript script) +{ +#if GLIB_CHECK_VERSION(2,29,14) + return (hb_script_t) g_unicode_script_to_iso15924 (script); +#else + if (likely ((unsigned int) script < ARRAY_LENGTH (glib_script_to_script))) + return glib_script_to_script[script]; + + if (unlikely (script == G_UNICODE_SCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; + + return HB_SCRIPT_UNKNOWN; +#endif +} + +GUnicodeScript +hb_glib_script_from_script (hb_script_t script) +{ +#if GLIB_CHECK_VERSION(2,29,14) + return g_unicode_script_from_iso15924 (script); +#else + unsigned int count = ARRAY_LENGTH (glib_script_to_script); + for (unsigned int i = 0; i < count; i++) + if (glib_script_to_script[i] == script) + return (GUnicodeScript) i; + + if (unlikely (script == HB_SCRIPT_INVALID)) + return G_UNICODE_SCRIPT_INVALID_CODE; + + return G_UNICODE_SCRIPT_UNKNOWN; +#endif +} + + +static hb_unicode_combining_class_t +hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); +} + +static unsigned int +hb_glib_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return g_unichar_iswide (unicode) ? 2 : 1; +} + +static hb_unicode_general_category_t +hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + /* hb_unicode_general_category_t and GUnicodeType are identical */ + return (hb_unicode_general_category_t) g_unichar_type (unicode); +} + +static hb_codepoint_t +hb_glib_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + g_unichar_get_mirror_char (unicode, &unicode); + return unicode; +} + +static hb_script_t +hb_glib_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return hb_glib_script_to_script (g_unichar_get_script (unicode)); +} + +static hb_bool_t +hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_compose (a, b, ab); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[12]; + gchar *normalized; + int len; + hb_bool_t ret; + + 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; + } else { + ret = false; + } + + g_free (normalized); + return ret; +} + +static hb_bool_t +hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_decompose (ab, a, b); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[6]; + gchar *normalized; + int len; + hb_bool_t ret; + + 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; + ret = *a != ab; + } else if (len == 2) { + *a = g_utf8_get_char (normalized); + *b = g_utf8_get_char (g_utf8_next_char (normalized)); + /* Here's the ugly part: if ab decomposes to a single character and + * that character decomposes again, we have to detect that and undo + * the second part :-(. */ + gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC); + hb_codepoint_t c = g_utf8_get_char (recomposed); + if (c != ab && c != *a) { + *a = c; + *b = 0; + } + g_free (recomposed); + ret = true; + } else { + /* If decomposed to more than two characters, take the last one, + * and recompose the rest to get the first component. */ + gchar *end = g_utf8_offset_to_pointer (normalized, len - 1); + gchar *recomposed; + *b = g_utf8_get_char (end); + recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC); + /* We expect that recomposed has exactly one character now. */ + *a = g_utf8_get_char (recomposed); + g_free (recomposed); + ret = true; + } + + g_free (normalized); + return ret; +} + +static unsigned int +hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_fully_decompose (u, true, decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN); +#endif + + /* If the user doesn't have GLib >= 2.29.12 we have to perform + * a round trip to UTF-8 and the associated memory management dance. */ + gchar utf8[6]; + gchar *utf8_decomposed, *c; + gsize utf8_len, utf8_decomposed_len, i; + + /* Convert @u to UTF-8 and normalise it in NFKD mode. This performs the compatibility decomposition. */ + utf8_len = g_unichar_to_utf8 (u, utf8); + utf8_decomposed = g_utf8_normalize (utf8, utf8_len, G_NORMALIZE_NFKD); + utf8_decomposed_len = g_utf8_strlen (utf8_decomposed, -1); + + assert (utf8_decomposed_len <= HB_UNICODE_MAX_DECOMPOSITION_LEN); + + for (i = 0, c = utf8_decomposed; i < utf8_decomposed_len; i++, c = g_utf8_next_char (c)) + *decomposed++ = g_utf8_get_char (c); + + g_free (utf8_decomposed); + + return utf8_decomposed_len; +} + +hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void) +{ + static const hb_unicode_funcs_t _hb_glib_unicode_funcs = { + HB_OBJECT_HEADER_STATIC, + + NULL, /* parent */ + true, /* immutable */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } + }; + + return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs); +} + +#if GLIB_CHECK_VERSION(2,31,10) +/** + * hb_glib_blob_create: + * + * Since: 0.9.38 + **/ +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes) +{ + gsize size = 0; + gconstpointer data = g_bytes_get_data (gbytes, &size); + return hb_blob_create ((const char *) data, + size, + HB_MEMORY_MODE_READONLY, + g_bytes_ref (gbytes), + (hb_destroy_func_t) g_bytes_unref); +} +#endif diff --git a/gfx/harfbuzz/src/hb-glib.h b/gfx/harfbuzz/src/hb-glib.h new file mode 100644 index 000000000..5f04183ba --- /dev/null +++ b/gfx/harfbuzz/src/hb-glib.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GLIB_H +#define HB_GLIB_H + +#include "hb.h" + +#include <glib.h> + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_glib_script_to_script (GUnicodeScript script); + +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void); + +#if GLIB_CHECK_VERSION(2,31,10) +HB_EXTERN hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); +#endif + +HB_END_DECLS + +#endif /* HB_GLIB_H */ diff --git a/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl b/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl new file mode 100644 index 000000000..ca458a384 --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl @@ -0,0 +1,73 @@ +/*** 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 ***/ +GType +@enum_name@_get_type (void) +{ + static gsize type_id = 0; + + if (g_once_init_enter (&type_id)) + { + 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 id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&type_id, id); + } + + return type_id; +} + +/*** END value-tail ***/ diff --git a/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl b/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl new file mode 100644 index 000000000..e28510c22 --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl @@ -0,0 +1,55 @@ +/*** BEGIN file-header ***/ +/* + * Copyright © 2013 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_IN +#error "Include <hb-gobject.h> instead." +#endif + +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H + +#include "hb.h" + +#include <glib-object.h> + +HB_BEGIN_DECLS + + +/*** END file-header ***/ + +/*** BEGIN value-header ***/ +HB_EXTERN GType @enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) + +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +HB_END_DECLS + +#endif /* HB_GOBJECT_ENUMS_H */ +/*** END file-tail ***/ diff --git a/gfx/harfbuzz/src/hb-gobject-structs.cc b/gfx/harfbuzz/src/hb-gobject-structs.cc new file mode 100644 index 000000000..fef00245b --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject-structs.cc @@ -0,0 +1,83 @@ +/* + * 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,copy_func,free_func) \ +GType \ +hb_gobject_##name##_get_type (void) \ +{ \ + static gsize type_id = 0; \ + if (g_once_init_enter (&type_id)) { \ + GType id = g_boxed_type_register_static (g_intern_static_string ("hb_" #name "_t"), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type_id, id); \ + } \ + return type_id; \ +} + +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy); + +#define HB_DEFINE_VALUE_TYPE(name) \ + static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ + { \ + hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \ + if (unlikely (!c)) return NULL; \ + *c = *l; \ + return c; \ + } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { free (l); } \ + HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy); + +HB_DEFINE_OBJECT_TYPE (buffer) +HB_DEFINE_OBJECT_TYPE (blob) +HB_DEFINE_OBJECT_TYPE (face) +HB_DEFINE_OBJECT_TYPE (font) +HB_DEFINE_OBJECT_TYPE (font_funcs) +HB_DEFINE_OBJECT_TYPE (set) +HB_DEFINE_OBJECT_TYPE (shape_plan) +HB_DEFINE_OBJECT_TYPE (unicode_funcs) +HB_DEFINE_VALUE_TYPE (feature) +HB_DEFINE_VALUE_TYPE (glyph_info) +HB_DEFINE_VALUE_TYPE (glyph_position) +HB_DEFINE_VALUE_TYPE (segment_properties) +HB_DEFINE_VALUE_TYPE (user_data_key) + +HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_part) diff --git a/gfx/harfbuzz/src/hb-gobject-structs.h b/gfx/harfbuzz/src/hb-gobject-structs.h new file mode 100644 index 000000000..1c303219b --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject-structs.h @@ -0,0 +1,117 @@ +/* + * 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_IN +#error "Include <hb-gobject.h> instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include <glib-object.h> + +HB_BEGIN_DECLS + + +/* Object types */ + +/** + * hb_gobject_blob_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +/** + * hb_gobject_buffer_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +/** + * hb_gobject_face_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +/** + * hb_gobject_font_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +/** + * hb_gobject_font_funcs_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +HB_EXTERN GType hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +HB_EXTERN GType hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +/** + * hb_gobject_unicode_funcs_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +HB_EXTERN GType hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +HB_EXTERN GType hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +HB_EXTERN GType hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +HB_EXTERN GType hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +HB_EXTERN GType hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/gfx/harfbuzz/src/hb-gobject.h b/gfx/harfbuzz/src/hb-gobject.h new file mode 100644 index 000000000..ea1bd25df --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject.h @@ -0,0 +1,40 @@ +/* + * 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 +#define HB_GOBJECT_H_IN + +#include "hb.h" + +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/gfx/harfbuzz/src/hb-graphite2.cc b/gfx/harfbuzz/src/hb-graphite2.cc new file mode 100644 index 000000000..a2d90db87 --- /dev/null +++ b/gfx/harfbuzz/src/hb-graphite2.cc @@ -0,0 +1,427 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * 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 + */ + +#define HB_SHAPER graphite2 +#define hb_graphite2_shaper_font_data_t gr_font +#include "hb-shaper-impl-private.hh" + +#include "hb-graphite2.h" + +#include <graphite2/Segment.h> + + +HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face) +HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font) + + +/* + * shaper face data + */ + +typedef struct hb_graphite2_tablelist_t { + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; + +struct hb_graphite2_shaper_face_data_t { + hb_face_t *face; + gr_face *grface; + hb_graphite2_tablelist_t *tlist; +}; + +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; + + hb_blob_t *blob = NULL; + + for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) + if (p->tag == tag) { + blob = p->blob; + break; + } + + if (unlikely (!blob)) + { + blob = face_data->face->reference_table (tag); + + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); + if (unlikely (!p)) { + hb_blob_destroy (blob); + return NULL; + } + p->blob = blob; + p->tag = tag; + + /* TODO Not thread-safe, but fairly harmless. + * We can do the double-chcked pointer cmpexch thing here. */ + p->next = face_data->tlist; + face_data->tlist = p; + } + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +hb_graphite2_shaper_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return NULL; + } + hb_blob_destroy (silf_blob); + + hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t)); + if (unlikely (!data)) + return NULL; + + data->face = face; + data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll); + + if (unlikely (!data->grface)) { + free (data); + return NULL; + } + + return data; +} + +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data) +{ + hb_graphite2_tablelist_t *tlist = data->tlist; + + while (tlist) + { + hb_graphite2_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + free (old); + } + + gr_face_destroy (data->grface); + + free (data); +} + +/* + * Since: 0.9.10 + */ +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) +{ + if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL; + return HB_SHAPER_DATA_GET (face)->grface; +} + + +/* + * shaper font data + */ + +static float hb_graphite2_get_advance (const void *hb_font, unsigned short gid) +{ + return ((hb_font_t *) hb_font)->get_glyph_h_advance (gid); +} + +hb_graphite2_shaper_font_data_t * +_hb_graphite2_shaper_font_data_create (hb_font_t *font) +{ + if (unlikely (!hb_graphite2_shaper_face_data_ensure (font->face))) return NULL; + + hb_face_t *face = font->face; + hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + + return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface); +} + +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data) +{ + gr_font_destroy (data); +} + +/* + * Since: 0.9.10 + */ +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font) +{ + if (unlikely (!hb_graphite2_shaper_font_data_ensure (font))) return NULL; + return HB_SHAPER_DATA_GET (font); +} + + +/* + * shaper shape_plan data + */ + +struct hb_graphite2_shaper_shape_plan_data_t {}; + +hb_graphite2_shaper_shape_plan_data_t * +_hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + float advance; +}; + +hb_bool_t +_hb_graphite2_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + gr_face *grface = HB_SHAPER_DATA_GET (face)->grface; + gr_font *grfont = HB_SHAPER_DATA_GET (font); + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = lang ? strchr (lang, '-') : NULL; + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + for (unsigned int i = 0; i < num_features; i++) + { + const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); + if (fref) + gr_fref_set_feature_value (fref, features[i].value, feats); + } + + gr_segment *seg = NULL; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + float curradvx = 0., curradvy = 0.; + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; + + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; + + /* TODO ensure_native_direction. */ + + 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 (grfont, grface, + script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], + feats, + gr_utf32, chars, buffer->len, + 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); + + if (unlikely (!seg)) { + if (feats) gr_featureval_destroy (feats); + return false; + } + + unsigned int glyph_count = gr_seg_n_slots (seg); + if (unlikely (!glyph_count)) { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + buffer->len = 0; + return true; + } + + buffer->ensure (glyph_count); + scratch = buffer->get_scratch_buffer (&scratch_size); + while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + + DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + return false; + } + scratch = buffer->get_scratch_buffer (&scratch_size); + } + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } + + ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); + ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); + +#undef ALLOCATE_ARRAY + + memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; + float curradv = HB_DIRECTION_IS_BACKWARD(buffer->props.direction) ? gr_slot_origin_X(gr_seg_first_slot(seg)) : 0.; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)); + clusters[0].advance = gr_seg_advance_X(seg) - curradv; + } + 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; + clusters[ci-1].advance += clusters[ci].advance; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_graphite2_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = buffer->info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + ci++; + clusters[ci].advance = curradv - gr_slot_origin_X(is); + } else { + clusters[ci].advance = gr_slot_origin_X(is) - curradv; + ci++; + } + curradv = gr_slot_origin_X(is); + } + 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; + } + + if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + clusters[ci].advance = gr_seg_advance_X(seg) - curradv; + ci++; + + for (unsigned int i = 0; i < ci; ++i) + { + for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) + { + hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + buffer->len = glyph_count; + + float yscale = font->y_scale / font->x_scale; + /* Positioning. */ + if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + int currclus = -1; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL); + curradvx = 0; + for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X (is) - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + if (info->cluster != currclus) { + pPos->x_advance = info->var1.i32; + curradvx += pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale; + curradvy += pPos->y_advance; + } + } + else + { + int currclus = -1; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL); + curradvx = gr_seg_advance_X(seg); + for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) + { + if (info->cluster != currclus) + { + pPos->x_advance = info->var1.i32; + if (currclus != -1) curradvx -= info[-1].var1.i32; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale; + curradvy -= pPos->y_advance; + pPos->x_offset = gr_slot_origin_X (is) - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + hb_buffer_reverse_clusters (buffer); + } + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + return true; +} diff --git a/gfx/harfbuzz/src/hb-graphite2.h b/gfx/harfbuzz/src/hb-graphite2.h new file mode 100644 index 000000000..122c3e476 --- /dev/null +++ b/gfx/harfbuzz/src/hb-graphite2.h @@ -0,0 +1,48 @@ +/* + * 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" + +#include <graphite2/Font.h> + +HB_BEGIN_DECLS + + +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +HB_EXTERN gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +HB_EXTERN gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/gfx/harfbuzz/src/hb-icu.cc b/gfx/harfbuzz/src/hb-icu.cc new file mode 100644 index 000000000..ee54721fd --- /dev/null +++ b/gfx/harfbuzz/src/hb-icu.cc @@ -0,0 +1,371 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-icu.h" + +#include "hb-unicode-private.hh" + +#include <unicode/uchar.h> +#include <unicode/unorm.h> +#include <unicode/ustring.h> +#include <unicode/utf16.h> +#include <unicode/uversion.h> + + +hb_script_t +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), -1); +} + +UScriptCode +hb_icu_script_from_script (hb_script_t script) +{ + if (unlikely (script == HB_SCRIPT_INVALID)) + return USCRIPT_INVALID_CODE; + + for (unsigned int i = 0; i < USCRIPT_CODE_LIMIT; i++) + if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) + return (UScriptCode) i; + + return USCRIPT_UNKNOWN; +} + + +static hb_unicode_combining_class_t +hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); +} + +static unsigned int +hb_icu_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH)) + { + case U_EA_WIDE: + case U_EA_FULLWIDTH: + return 2; + case U_EA_NEUTRAL: + case U_EA_AMBIGUOUS: + case U_EA_HALFWIDTH: + case U_EA_NARROW: + return 1; + } + return 1; +} + +static hb_unicode_general_category_t +hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY)) + { + case U_UNASSIGNED: return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + + case U_UPPERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case U_LOWERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case U_TITLECASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case U_MODIFIER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case U_OTHER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + + case U_NON_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case U_ENCLOSING_MARK: return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case U_COMBINING_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + + case U_DECIMAL_DIGIT_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case U_LETTER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case U_OTHER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + + case U_SPACE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case U_LINE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case U_PARAGRAPH_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + + case U_CONTROL_CHAR: return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case U_FORMAT_CHAR: return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case U_PRIVATE_USE_CHAR: return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case U_SURROGATE: return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + + + case U_DASH_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case U_START_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case U_END_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case U_CONNECTOR_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case U_OTHER_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + + case U_MATH_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case U_CURRENCY_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case U_MODIFIER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case U_OTHER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + + case U_INITIAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case U_FINAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return u_charMirror(unicode); +} + +static hb_script_t +hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + UErrorCode status = U_ZERO_ERROR; + UScriptCode scriptCode = uscript_getScript(unicode, &status); + + if (unlikely (U_FAILURE (status))) + return HB_SCRIPT_UNKNOWN; + + return hb_icu_script_to_script (scriptCode); +} + +#if U_ICU_VERSION_MAJOR_NUM >= 49 +static const UNormalizer2 *normalizer; +#endif + +static hb_bool_t +hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if U_ICU_VERSION_MAJOR_NUM >= 49 + { + UChar32 ret = unorm2_composePair (normalizer, a, b); + if (ret < 0) return false; + *ab = ret; + return true; + } +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + UChar utf16[4], normalized[5]; + unsigned int len; + hb_bool_t ret, err; + UErrorCode icu_err; + + len = 0; + err = false; + U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err); + if (err) return false; + U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err); + if (err) return false; + + icu_err = U_ZERO_ERROR; + len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); + if (U_FAILURE (icu_err)) + return false; + if (u_countChar32 (normalized, len) == 1) { + U16_GET_UNSAFE (normalized, 0, *ab); + ret = true; + } else { + ret = false; + } + + return ret; +} + +static hb_bool_t +hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if U_ICU_VERSION_MAJOR_NUM >= 49 + { + UChar decomposed[4]; + int len; + UErrorCode icu_err = U_ZERO_ERROR; + len = unorm2_getRawDecomposition (normalizer, ab, decomposed, + ARRAY_LENGTH (decomposed), &icu_err); + if (U_FAILURE (icu_err) || len < 0) return false; + + len = u_countChar32 (decomposed, len); + if (len == 1) { + U16_GET_UNSAFE (decomposed, 0, *a); + *b = 0; + return *a != ab; + } else if (len == 2) { + len =0; + U16_NEXT_UNSAFE (decomposed, len, *a); + U16_NEXT_UNSAFE (decomposed, len, *b); + } + return true; + } +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1]; + unsigned int len; + hb_bool_t ret, err; + UErrorCode icu_err; + + /* This function is a monster! Maybe it wasn't a good idea adding a + * pairwise decompose API... */ + /* Watchout for the dragons. Err, watchout for macros changing len. */ + + len = 0; + err = false; + U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err); + if (err) return false; + + icu_err = U_ZERO_ERROR; + len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); + if (U_FAILURE (icu_err)) + return false; + + len = u_countChar32 (normalized, len); + + if (len == 1) { + U16_GET_UNSAFE (normalized, 0, *a); + *b = 0; + ret = *a != ab; + } else if (len == 2) { + len =0; + U16_NEXT_UNSAFE (normalized, len, *a); + U16_NEXT_UNSAFE (normalized, len, *b); + + /* Here's the ugly part: if ab decomposes to a single character and + * that character decomposes again, we have to detect that and undo + * the second part :-(. */ + UChar recomposed[20]; + icu_err = U_ZERO_ERROR; + unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); + 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; + } else { + /* If decomposed to more than two characters, take the last one, + * and recompose the rest to get the first component. */ + U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */ + UChar recomposed[18 * 2]; + icu_err = U_ZERO_ERROR; + len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); + if (U_FAILURE (icu_err)) + return false; + /* We expect that recomposed has exactly one character now. */ + if (unlikely (u_countChar32 (recomposed, len) != 1)) + return false; + U16_GET_UNSAFE (recomposed, 0, *a); + ret = true; + } + + return ret; +} + +static unsigned int +hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data HB_UNUSED) +{ + UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1]; + unsigned int len; + int32_t utf32_len; + hb_bool_t err; + UErrorCode icu_err; + + /* Copy @u into a UTF-16 array to be passed to ICU. */ + len = 0; + err = false; + U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err); + if (err) + return 0; + + /* Normalise the codepoint using NFKD mode. */ + icu_err = U_ZERO_ERROR; + len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); + if (icu_err) + return 0; + + /* Convert the decomposed form from UTF-16 to UTF-32. */ + icu_err = U_ZERO_ERROR; + u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err); + if (icu_err) + return 0; + + return utf32_len; +} + + +hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void) +{ + static const hb_unicode_funcs_t _hb_icu_unicode_funcs = { + HB_OBJECT_HEADER_STATIC, + + NULL, /* parent */ + true, /* immutable */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } + }; + +#if U_ICU_VERSION_MAJOR_NUM >= 49 + if (!hb_atomic_ptr_get (&normalizer)) { + UErrorCode icu_err = U_ZERO_ERROR; + /* We ignore failure in getNFCInstace(). */ + (void) hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err)); + } +#endif + return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs); +} diff --git a/gfx/harfbuzz/src/hb-icu.h b/gfx/harfbuzz/src/hb-icu.h new file mode 100644 index 000000000..2db6a7b67 --- /dev/null +++ b/gfx/harfbuzz/src/hb-icu.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ICU_H +#define HB_ICU_H + +#include "hb.h" + +#include <unicode/uscript.h> + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_icu_script_to_script (UScriptCode script); + +HB_EXTERN UScriptCode +hb_icu_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void); + + +HB_END_DECLS + +#endif /* HB_ICU_H */ diff --git a/gfx/harfbuzz/src/hb-mutex-private.hh b/gfx/harfbuzz/src/hb-mutex-private.hh new file mode 100644 index 000000000..ed2703571 --- /dev/null +++ b/gfx/harfbuzz/src/hb-mutex-private.hh @@ -0,0 +1,141 @@ +/* + * 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_MUTEX_PRIVATE_HH +#define HB_MUTEX_PRIVATE_HH + +#include "hb-private.hh" + + +/* mutex */ + +/* We need external help for these */ + +#if defined(HB_MUTEX_IMPL_INIT) \ + && defined(hb_mutex_impl_init) \ + && defined(hb_mutex_impl_lock) \ + && defined(hb_mutex_impl_unlock) \ + && defined(hb_mutex_impl_finish) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) + +#include <windows.h> +typedef CRITICAL_SECTION hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT {0} +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) +#else +#define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#endif +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) + + +#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_INTEL_ATOMIC_PRIMITIVES) + +#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 + +/* 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 + + +#elif !defined(HB_NO_MT) + +#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) *(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 finish (void) { hb_mutex_impl_finish (&m); } +}; + + +#endif /* HB_MUTEX_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-object-private.hh b/gfx/harfbuzz/src/hb-object-private.hh new file mode 100644 index 000000000..6b73ff92d --- /dev/null +++ b/gfx/harfbuzz/src/hb-object-private.hh @@ -0,0 +1,202 @@ +/* + * 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_OBJECT_PRIVATE_HH +#define HB_OBJECT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-atomic-private.hh" +#include "hb-mutex-private.hh" + + +/* Debug */ + +#ifndef HB_DEBUG_OBJECT +#define HB_DEBUG_OBJECT (HB_DEBUG+0) +#endif + + +/* reference_count */ + +#define HB_REFERENCE_COUNT_INERT_VALUE -1 +#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD +#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INERT_VALUE)} + +struct hb_reference_count_t +{ + hb_atomic_int_t ref_count; + + inline void init (int v) { ref_count.set_unsafe (v); } + inline int get_unsafe (void) const { return ref_count.get_unsafe (); } + inline int inc (void) { return ref_count.inc (); } + inline int dec (void) { return ref_count.dec (); } + inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); } + + inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; } + inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; } +}; + + +/* user_data */ + +#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } + inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; } + + void finish (void) { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items; + + inline void init (void) { lock.init (); items.init (); } + + HB_INTERNAL bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + HB_INTERNAL void *get (hb_user_data_key_t *key); + + inline void finish (void) { items.finish (lock); lock.finish (); } +}; + + +/* object_header */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + hb_user_data_array_t user_data; + +#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_USER_DATA_ARRAY_INIT} + + private: + ASSERT_POD (); +}; + + +/* object */ + +template <typename Type> +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_unsafe () : 0); +} + +template <typename Type> +static inline Type *hb_object_create (void) +{ + Type *obj = (Type *) calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + return obj; +} +template <typename Type> +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (1); + obj->header.user_data.init (); +} +template <typename Type> +static inline bool hb_object_is_inert (const Type *obj) +{ + return unlikely (obj->header.ref_count.is_inert ()); +} +template <typename Type> +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template <typename Type> +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template <typename Type> +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + obj->header.ref_count.finish (); /* Do this before user_data */ + obj->header.user_data.finish (); + return true; +} +template <typename Type> +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + return obj->header.user_data.set (key, data, destroy, replace); +} + +template <typename Type> +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return NULL; + assert (hb_object_is_valid (obj)); + return obj->header.user_data.get (key); +} + + +#endif /* HB_OBJECT_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-open-file-private.hh b/gfx/harfbuzz/src/hb-open-file-private.hh new file mode 100644 index 000000000..5357ddcf5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-open-file-private.hh @@ -0,0 +1,268 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_PRIVATE_HH +#define HB_OPEN_FILE_PRIVATE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + ULONG offset; /* Offset from beginning of TrueType font + * file. */ + ULONG length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OffsetTable +{ + friend struct OpenTypeFontFile; + + inline unsigned int get_table_count (void) const + { return numTables; } + inline const TableRecord& get_table (unsigned int i) const + { + if (unlikely (i >= numTables)) return Null(TableRecord); + return tables[i]; + } + inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t.set (tag); + unsigned int count = numTables; + for (unsigned int i = 0; i < count; i++) + { + if (t == tables[i].tag) + { + if (table_index) *table_index = i; + return true; + } + } + if (table_index) *table_index = Index::NOT_FOUND_INDEX; + return false; + } + inline const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + USHORT numTables; /* Number of tables. */ + USHORT searchRangeZ; /* (Maximum power of 2 <= numTables) x 16 */ + USHORT entrySelectorZ; /* Log2(maximum power of 2 <= numTables). */ + USHORT rangeShiftZ; /* NumTables x 16-searchRange. */ + TableRecord tables[VAR]; /* TableRecord entries. numTables items */ + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + inline unsigned int get_face_count (void) const { return table.len; } + inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + inline unsigned int get_face_count (void) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + inline const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null(OpenTypeFontFace); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + static const hb_tag_t tableTag = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */ + + static const hb_tag_t CFFTag = HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */ + static const hb_tag_t TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */ + static const hb_tag_t TTCTag = HB_TAG ('t','t','c','f'); /* TrueType Collection */ + static const hb_tag_t TrueTag = HB_TAG ('t','r','u','e'); /* Obsolete Apple TrueType */ + static const hb_tag_t Typ1Tag = HB_TAG ('t','y','p','1'); /* Obsolete Apple Type1 font in SFNT container */ + + inline hb_tag_t get_tag (void) const { return u.tag; } + + inline unsigned int get_face_count (void) const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + default: return 0; + } + } + inline const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + default: return Null(OpenTypeFontFace); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-open-type-private.hh b/gfx/harfbuzz/src/hb-open-type-private.hh new file mode 100644 index 000000000..2cc1fb20d --- /dev/null +++ b/gfx/harfbuzz/src/hb-open-type-private.hh @@ -0,0 +1,1067 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_PRIVATE_HH +#define HB_OPEN_TYPE_PRIVATE_HH + +#include "hb-private.hh" + + +namespace OT { + + + +/* + * Casts + */ + +/* Cast to struct T, reference to reference */ +template<typename Type, typename TObject> +static inline const Type& CastR(const TObject &X) +{ return reinterpret_cast<const Type&> (X); } +template<typename Type, typename TObject> +static inline Type& CastR(TObject &X) +{ return reinterpret_cast<Type&> (X); } + +/* Cast to struct T, pointer to pointer */ +template<typename Type, typename TObject> +static inline const Type* CastP(const TObject *X) +{ return reinterpret_cast<const Type*> (X); } +template<typename Type, typename TObject> +static inline Type* CastP(TObject *X) +{ return reinterpret_cast<Type*> (X); } + +/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template<typename Type> +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast<const Type*> ((const char *) P + offset); } +template<typename Type> +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast<Type*> ((char *) P + offset); } + +/* StructAfter<T>(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template<typename Type, typename TObject> +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset<Type>(&X, X.get_size()); } +template<typename Type, typename TObject> +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset<Type>(&X, X.get_size()); } + + + +/* + * Size checking + */ + +/* Check _assertion in a method environment */ +#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_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_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ + static const unsigned int static_size = (size); \ + static const unsigned int min_size = (size); \ + inline unsigned int get_size (void) const { return (size); } + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + 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_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) + + + +/* + * Null objects + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ +/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */ +static const void *_NullPool[(256+8) / sizeof (void *)]; + +/* Generic nul-content Null objects. */ +template <typename Type> +static inline const Type& Null (void) { + ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool)); + return *CastP<Type> (_NullPool); +} + +/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */ +#define DEFINE_NULL_DATA(Type, data) \ +static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \ +template <> \ +/*static*/ inline const Type& Null<Type> (void) { \ + return *CastP<Type> (_Null##Type); \ +} /* The following line really exists such that we end in a place needing semicolon */ \ +ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type)) + +/* Accessor macro. */ +#define Null(Type) Null<Type>() + + +/* + * Dispatch + */ + +template <typename Context, typename Return, unsigned int MaxDebugDepth> +struct hb_dispatch_context_t +{ + static const unsigned int max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } +}; + + +/* + * Sanitize + */ + +#ifndef HB_DEBUG_SANITIZE +#define HB_DEBUG_SANITIZE (HB_DEBUG+0) +#endif + + +#define TRACE_SANITIZE(this) \ + hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + ""); + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE> +{ + inline hb_sanitize_context_t (void) : + debug_depth (0), + start (NULL), end (NULL), + writable (false), edit_count (0), + blob (NULL) {} + + inline const char *get_name (void) { return "SANITIZE"; } + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) + { return format->sanitize (this); } + template <typename T> + inline return_t dispatch (const T &obj) { return obj.sanitize (this); } + static return_t default_return_value (void) { return true; } + static return_t no_dispatch_return_value (void) { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + inline void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + inline void start_processing (void) + { + this->start = hb_blob_get_data (this->blob, NULL); + this->end = this->start + hb_blob_get_length (this->blob); + assert (this->start <= this->end); /* Must not overflow. */ + this->edit_count = 0; + this->debug_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + inline void end_processing (void) + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = NULL; + this->start = this->end = NULL; + } + + inline bool check_range (const void *base, unsigned int len) const + { + const char *p = (const char *) base; + bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const + { + const char *p = (const char *) base; + bool overflows = _hb_unsigned_int_mul_overflows (len, record_size); + unsigned int array_size = record_size * len; + bool ok = !overflows && this->check_range (base, array_size); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", + p, p + (record_size * len), record_size, len, (unsigned int) array_size, + this->start, this->end, + overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template <typename Type> + inline bool check_struct (const Type *obj) const + { + return likely (this->check_range (obj, obj->min_size)); + } + + inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template <typename Type, typename ValueType> + inline bool try_set (const Type *obj, const ValueType &v) { + if (this->may_edit (obj, obj->static_size)) { + const_cast<Type *> (obj)->set (v); + return true; + } + return false; + } + + mutable unsigned int debug_depth; + const char *start, *end; + bool writable; + unsigned int edit_count; + hb_blob_t *blob; +}; + + + +/* Template to sanitize an object. */ +template <typename Type> +struct Sanitizer +{ + static hb_blob_t *sanitize (hb_blob_t *blob) { + hb_sanitize_context_t c[1]; + bool sane; + + /* TODO is_sane() stuff */ + + c->init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, c->start, "start"); + + c->start_processing (); + + if (unlikely (!c->start)) { + c->end_processing (); + return blob; + } + + Type *t = CastP<Type> (const_cast<char *> (c->start)); + + sane = t->sanitize (c); + if (sane) { + if (c->edit_count) { + DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count); + + /* sanitize again to ensure no toe-stepping */ + c->edit_count = 0; + sane = t->sanitize (c); + if (c->edit_count) { + DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count); + sane = false; + } + } + } else { + unsigned int edit_count = c->edit_count; + if (edit_count && !c->writable) { + c->start = hb_blob_get_data_writable (blob, NULL); + c->end = c->start + hb_blob_get_length (blob); + + if (c->start) { + c->writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, c->start, "retry"); + goto retry; + } + } + } + + c->end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED"); + if (sane) + return blob; + else { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + static const Type* lock_instance (hb_blob_t *blob) { + hb_blob_make_immutable (blob); + const char *base = hb_blob_get_data (blob, NULL); + return unlikely (!base) ? &Null(Type) : CastP<Type> (base); + } +}; + + + +/* + * Serialize + */ + +#ifndef HB_DEBUG_SERIALIZE +#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) +#endif + + +#define TRACE_SERIALIZE(this) \ + hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \ + (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ + ""); + + +struct hb_serialize_context_t +{ + inline hb_serialize_context_t (void *start_, unsigned int size) + { + this->start = (char *) start_; + this->end = this->start + size; + + this->ran_out_of_room = false; + this->head = this->start; + this->debug_depth = 0; + } + + template <typename Type> + inline Type *start_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + return start_embed<Type> (); + } + + inline void end_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %d bytes; %s", + this->start, this->end, + (int) (this->head - this->start), + this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); + + } + + template <typename Type> + inline Type *copy (void) + { + assert (!this->ran_out_of_room); + unsigned int len = this->head - this->start; + void *p = malloc (len); + if (p) + memcpy (p, this->start, len); + return reinterpret_cast<Type *> (p); + } + + template <typename Type> + inline Type *allocate_size (unsigned int size) + { + if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { + this->ran_out_of_room = true; + return NULL; + } + memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast<Type *> (ret); + } + + template <typename Type> + inline Type *allocate_min (void) + { + return this->allocate_size<Type> (Type::min_size); + } + + template <typename Type> + inline Type *start_embed (void) + { + Type *ret = reinterpret_cast<Type *> (this->head); + return ret; + } + + template <typename Type> + inline Type *embed (const Type &obj) + { + unsigned int size = obj.get_size (); + Type *ret = this->allocate_size<Type> (size); + if (unlikely (!ret)) return NULL; + memcpy (ret, obj, size); + return ret; + } + + template <typename Type> + inline Type *extend_min (Type &obj) + { + unsigned int size = obj.min_size; + assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL; + return reinterpret_cast<Type *> (&obj); + } + + template <typename Type> + inline Type *extend (Type &obj) + { + unsigned int size = obj.get_size (); + assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL; + return reinterpret_cast<Type *> (&obj); + } + + inline void truncate (void *new_head) + { + assert (this->start < new_head && new_head <= this->head); + this->head = (char *) new_head; + } + + unsigned int debug_depth; + char *start, *end, *head; + bool ran_out_of_room; +}; + +template <typename Type> +struct Supplier +{ + inline Supplier (const Type *array, unsigned int len_) + { + head = array; + len = len_; + } + inline const Type operator [] (unsigned int i) const + { + if (unlikely (i >= len)) return Type (); + return head[i]; + } + + inline void advance (unsigned int count) + { + if (unlikely (count > len)) + count = len; + len -= count; + head += count; + } + + private: + inline Supplier (const Supplier<Type> &); /* Disallow copy */ + inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */ + + unsigned int len; + const Type *head; +}; + + + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + + +template <typename Type, int Bytes> struct BEInt; + +template <typename Type> +struct BEInt<Type, 1> +{ + public: + inline void set (Type V) + { + v = V; + } + inline operator Type (void) const + { + return v; + } + private: uint8_t v; +}; +template <typename Type> +struct BEInt<Type, 2> +{ + public: + inline void set (Type V) + { + v[0] = (V >> 8) & 0xFF; + v[1] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 8) + + (v[1] ); + } + private: uint8_t v[2]; +}; +template <typename Type> +struct BEInt<Type, 3> +{ + public: + inline void set (Type V) + { + v[0] = (V >> 16) & 0xFF; + v[1] = (V >> 8) & 0xFF; + v[2] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); + } + private: uint8_t v[3]; +}; +template <typename Type> +struct BEInt<Type, 4> +{ + public: + inline void set (Type V) + { + v[0] = (V >> 24) & 0xFF; + v[1] = (V >> 16) & 0xFF; + v[2] = (V >> 8) & 0xFF; + v[3] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); + } + private: uint8_t v[4]; +}; + +/* Integer types in big-endian order and no alignment requirement */ +template <typename Type, unsigned int Size> +struct IntType +{ + inline void set (Type i) { v.set (i); } + inline operator Type(void) const { return v; } + inline bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; } + inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); } + static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); } + inline int cmp (Type a) const + { + Type b = v; + if (sizeof (Type) < sizeof (int)) + return (int) a - (int) b; + else + return a < b ? -1 : a == b ? 0 : +1; + } + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + BEInt<Type, Size> v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType<int8_t , 1> CHAR; /* 8-bit signed integer. */ +typedef IntType<uint8_t , 1> BYTE; /* 8-bit unsigned integer. */ +typedef IntType<int8_t , 1> INT8; /* 8-bit signed integer. */ +typedef IntType<uint16_t, 2> USHORT; /* 16-bit unsigned integer. */ +typedef IntType<int16_t, 2> SHORT; /* 16-bit signed integer. */ +typedef IntType<uint32_t, 4> ULONG; /* 32-bit unsigned integer. */ +typedef IntType<int32_t, 4> LONG; /* 32-bit signed integer. */ +typedef IntType<uint32_t, 3> UINT24; /* 24-bit unsigned 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; + +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +struct F2DOT14 : SHORT +{ + //inline float to_float (void) const { return ???; } + //inline void set_float (float f) { v.set (f * ???); } + public: + DEFINE_SIZE_STATIC (2); +}; + +/* 32-bit signed fixed-point number (16.16). */ +struct Fixed: LONG +{ + //inline float to_float (void) const { return ???; } + //inline void set_float (float f) { v.set (f * ???); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* 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) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + LONG major; + ULONG minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : ULONG +{ + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + inline operator const char* (void) const { return reinterpret_cast<const char *> (&this->v); } + inline operator char* (void) { return reinterpret_cast<char *> (&this->v); } + public: + DEFINE_SIZE_STATIC (4); +}; +DEFINE_NULL_DATA (Tag, " "); + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct GlyphID : USHORT { + static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); } + inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; } +}; + +/* Script/language-system/feature index */ +struct Index : USHORT { + static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; +}; +DEFINE_NULL_DATA (Index, "\xff\xff"); + +/* Offset, Null offset = 0 */ +template <typename Type=USHORT> +struct Offset : Type +{ + inline bool is_null (void) const { return 0 == *this; } + public: + DEFINE_SIZE_STATIC (sizeof(Type)); +}; + + +/* CheckSum */ +struct CheckSum : ULONG +{ + /* This is reference implementation from the spec. */ + static inline uint32_t CalcTableChecksum (const ULONG *Table, uint32_t Length) + { + uint32_t Sum = 0L; + const ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + inline void set_for_data (const void *data, unsigned int length) + { set (CalcTableChecksum ((const ULONG *) data, length)); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +template <typename FixedType=USHORT> +struct FixedVersion +{ + inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FixedType major; + FixedType minor; + public: + DEFINE_SIZE_STATIC (2 * sizeof(FixedType)); +}; + + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template <typename Type, typename OffsetType=USHORT> +struct OffsetTo : Offset<OffsetType> +{ + inline const Type& operator () (const void *base) const + { + unsigned int offset = *this; + if (unlikely (!offset)) return Null(Type); + return StructAtOffset<Type> (base, offset); + } + + inline Type& serialize (hb_serialize_context_t *c, const void *base) + { + Type *t = c->start_embed<Type> (); + this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + return *t; + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + unsigned int offset = *this; + if (unlikely (!offset)) return_trace (true); + if (unlikely (!c->check_range (base, offset))) return_trace (false); + const Type &obj = StructAtOffset<Type> (base, offset); + return_trace (likely (obj.sanitize (c)) || neuter (c)); + } + template <typename T> + inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + unsigned int offset = *this; + if (unlikely (!offset)) return_trace (true); + if (unlikely (!c->check_range (base, offset))) return_trace (false); + const Type &obj = StructAtOffset<Type> (base, offset); + return_trace (likely (obj.sanitize (c, user_data)) || neuter (c)); + } + + /* Set the offset to Null */ + inline bool neuter (hb_sanitize_context_t *c) const { + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof(OffsetType)); +}; +template <typename Base, typename OffsetType, typename Type> +static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType> &offset) { return offset (base); } +template <typename Base, typename OffsetType, typename Type> +static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType> &offset) { return offset (base); } + + +/* + * Array Types + */ + +/* An array with a number of elements. */ +template <typename Type, typename LenType=USHORT> +struct ArrayOf +{ + const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const + { + unsigned int count = len; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + count = MIN (count, *pcount); + *pcount = count; + return array + start_offset; + } + + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= len)) return Null(Type); + return array[i]; + } + inline Type& operator [] (unsigned int i) + { + return array[i]; + } + inline unsigned int get_size (void) const + { return len.static_size + len * Type::static_size; } + + inline bool serialize (hb_serialize_context_t *c, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!c->extend (*this))) return_trace (false); + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<Type> &items, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!serialize (c, items_len))) return_trace (false); + for (unsigned int i = 0; i < items_len; i++) + array[i] = items[i]; + items.advance (items_len); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && array[0].sanitize (c)); + + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!array[i].sanitize (c, base))) + return_trace (false); + return_trace (true); + } + template <typename T> + inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!array[i].sanitize (c, base, user_data))) + return_trace (false); + return_trace (true); + } + + template <typename SearchType> + inline int lsearch (const SearchType &x) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (!this->array[i].cmp (x)) + return i; + return -1; + } + + private: + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len)); + } + + public: + LenType len; + Type array[VAR]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), array); +}; + +/* Array of Offset's */ +template <typename Type, typename OffsetType=USHORT> +struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {}; + +/* Array of offsets relative to the beginning of the array itself. */ +template <typename Type> +struct OffsetListOf : OffsetArrayOf<Type> +{ + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= this->len)) return Null(Type); + return this+this->array[i]; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (OffsetArrayOf<Type>::sanitize (c, this)); + } + template <typename T> + inline bool sanitize (hb_sanitize_context_t *c, T user_data) const + { + TRACE_SANITIZE (this); + return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data)); + } +}; + + +/* An array starting at second element. */ +template <typename Type, typename LenType=USHORT> +struct HeadlessArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= len || !i)) return Null(Type); + return array[i-1]; + } + inline unsigned int get_size (void) const + { return len.static_size + (len ? len - 1 : 0) * Type::static_size; } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<Type> &items, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!items_len)) return_trace (true); + if (unlikely (!c->extend (*this))) return_trace (false); + for (unsigned int i = 0; i < items_len - 1; i++) + array[i] = items[i]; + items.advance (items_len - 1); + return_trace (true); + } + + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + return c->check_struct (this) + && c->check_array (this, Type::static_size, len); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && array[0].sanitize (c)); + + return_trace (true); + } + + LenType len; + Type array[VAR]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), array); +}; + + +/* An array with sorted elements. Supports binary searching. */ +template <typename Type, typename LenType=USHORT> +struct SortedArrayOf : ArrayOf<Type, LenType> +{ + template <typename SearchType> + inline int bsearch (const SearchType &x) const + { + /* Hand-coded bsearch here since this is in the hot inner loop. */ + int min = 0, max = (int) this->len - 1; + while (min <= max) + { + int mid = (min + max) / 2; + int c = this->array[mid].cmp (x); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + return mid; + } + return -1; + } +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-cbdt-table.hh b/gfx/harfbuzz/src/hb-ot-cbdt-table.hh new file mode 100644 index 000000000..52897abd3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cbdt-table.hh @@ -0,0 +1,384 @@ +/* + * Copyright © 2016 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): Seigo Nonaka + */ + +#ifndef HB_OT_CBDT_TABLE_HH +#define HB_OT_CBDT_TABLE_HH + +#include "hb-open-type-private.hh" + +namespace OT { + +struct SmallGlyphMetrics +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void get_extents (hb_glyph_extents_t *extents) const + { + extents->x_bearing = bearingX; + extents->y_bearing = bearingY; + extents->width = width; + extents->height = -height; + } + + BYTE height; + BYTE width; + CHAR bearingX; + CHAR bearingY; + BYTE advance; + + DEFINE_SIZE_STATIC(5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + CHAR vertBearingX; + CHAR vertBearingY; + BYTE vertAdvance; + + DEFINE_SIZE_STATIC(8); +}; + +struct SBitLineMetrics +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + CHAR ascender; + CHAR decender; + BYTE widthMax; + CHAR caretSlopeNumerator; + CHAR caretSlopeDenominator; + CHAR caretOffset; + CHAR minOriginSB; + CHAR minAdvanceSB; + CHAR maxBeforeBL; + CHAR minAfterBL; + CHAR padding1; + CHAR padding2; + + DEFINE_SIZE_STATIC(12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + USHORT indexFormat; + USHORT imageFormat; + ULONG imageDataOffset; + + DEFINE_SIZE_STATIC(8); +}; + +template <typename OffsetType> +struct IndexSubtableFormat1Or3 +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1)); + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length) const + { + if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) + return false; + + *offset = header.imageDataOffset + offsetArrayZ[idx]; + *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; + return true; + } + + IndexSubtableHeader header; + Offset<OffsetType> offsetArrayZ[VAR]; + + DEFINE_SIZE_ARRAY(8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<ULONG> {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<USHORT> {}; + +struct IndexSubtable +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c)) return_trace (false); + switch (u.header.indexFormat) { + case 1: return_trace (u.format1.sanitize (c, glyph_count)); + case 3: return_trace (u.format3.sanitize (c, glyph_count)); + default:return_trace (true); + } + } + + inline bool get_extents (hb_glyph_extents_t *extents) const + { + switch (u.header.indexFormat) { + case 2: case 5: /* TODO */ + case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ + default:return (false); + } + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + *format = u.header.imageFormat; + switch (u.header.indexFormat) { + case 1: return u.format1.get_image_data (idx, offset, length); + case 3: return u.format3.get_image_data (idx, offset, length); + default: return false; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + firstGlyphIndex <= lastGlyphIndex && + offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1)); + } + + inline bool get_extents (hb_glyph_extents_t *extents) const + { + return (this+offsetToSubtable).get_extents (extents); + } + + bool get_image_data (unsigned int gid, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) + { + return false; + } + return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + USHORT firstGlyphIndex; + USHORT lastGlyphIndex; + OffsetTo<IndexSubtable, ULONG> offsetToSubtable; + + DEFINE_SIZE_STATIC(8); +}; + +struct IndexSubtableArray +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count))) + return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!indexSubtablesZ[i].sanitize (c, this))) + return_trace (false); + return_trace (true); + } + + public: + const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const + { + for (unsigned int i = 0; i < numTables; ++i) + { + unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; + unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; + if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) { + return &indexSubtablesZ[i]; + } + } + return NULL; + } + + protected: + IndexSubtableRecord indexSubtablesZ[VAR]; + + public: + DEFINE_SIZE_ARRAY(0, indexSubtablesZ); +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && + c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const + { + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + protected: + OffsetTo<IndexSubtableArray, ULONG> indexSubtableArrayOffset; + ULONG indexTablesSize; + ULONG numberOfIndexSubtables; + ULONG colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + USHORT startGlyphIndex; + USHORT endGlyphIndex; + BYTE ppemX; + BYTE ppemY; + BYTE bitDepth; + CHAR flags; + +public: + DEFINE_SIZE_STATIC(48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + ULONG dataLen; + BYTE dataZ[VAR]; + + DEFINE_SIZE_ARRAY(9, dataZ); +}; + + +/* + * CBLC -- Color Bitmap Location Table + */ + +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') + +struct CBLC +{ + static const hb_tag_t tableTag = HB_OT_TAG_CBLC; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3) && + sizeTables.sanitize (c, this)); + } + + public: + const IndexSubtableRecord *find_table (hb_codepoint_t glyph, + unsigned int *x_ppem, unsigned int *y_ppem) const + { + /* TODO: Make it possible to select strike. */ + + unsigned int count = sizeTables.len; + for (uint32_t i = 0; i < count; ++i) + { + unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex; + unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex; + if (startGlyphIndex <= glyph && glyph <= endGlyphIndex) + { + *x_ppem = sizeTables[i].ppemX; + *y_ppem = sizeTables[i].ppemY; + return sizeTables[i].find_table (glyph, this); + } + } + + return NULL; + } + + protected: + FixedVersion<>version; + ArrayOf<BitmapSizeTable, ULONG> sizeTables; + + public: + DEFINE_SIZE_ARRAY(8, sizeTables); +}; + +/* + * CBDT -- Color Bitmap Data Table + */ +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + +struct CBDT +{ + static const hb_tag_t tableTag = HB_OT_TAG_CBDT; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<>version; + BYTE dataZ[VAR]; + + public: + DEFINE_SIZE_ARRAY(4, dataZ); +}; + +} /* namespace OT */ + +#endif /* HB_OT_CBDT_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-cmap-table.hh b/gfx/harfbuzz/src/hb-ot-cmap-table.hh new file mode 100644 index 000000000..d7a94a1ef --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh @@ -0,0 +1,535 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_CMAP_TABLE_HH +#define HB_OT_CMAP_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * cmap -- Character To Glyph Index Mapping Table + */ + +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') + + +struct CmapSubtableFormat0 +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; + if (!gid) + return false; + *glyph = gid; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT format; /* Format number is set to 0. */ + USHORT lengthZ; /* Byte length of this subtable. */ + USHORT languageZ; /* Ignore. */ + BYTE glyphIdArray[256];/* An array that maps character + * code to glyph index values. */ + public: + DEFINE_SIZE_STATIC (6 + 256); +}; + +struct CmapSubtableFormat4 +{ + struct accelerator_t + { + inline void init (const CmapSubtableFormat4 *subtable) + { + segCount = subtable->segCountX2 / 2; + endCount = subtable->values; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; + } + + static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { + const accelerator_t *thiz = (const accelerator_t *) obj; + + /* Custom two-array bsearch. */ + int min = 0, max = (int) thiz->segCount - 1; + const USHORT *startCount = thiz->startCount; + const USHORT *endCount = thiz->endCount; + unsigned int i; + while (min <= max) + { + int mid = (min + max) / 2; + if (codepoint < startCount[mid]) + max = mid - 1; + else if (codepoint > endCount[mid]) + min = mid + 1; + else + { + i = mid; + goto found; + } + } + return false; + + found: + hb_codepoint_t gid; + unsigned int rangeOffset = thiz->idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + thiz->idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount; + if (unlikely (index >= thiz->glyphIdArrayLength)) + return false; + gid = thiz->glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += thiz->idDelta[i]; + } + + *glyph = gid & 0xFFFFu; + return true; + } + + const USHORT *endCount; + const USHORT *startCount; + const USHORT *idDelta; + const USHORT *idRangeOffset; + const USHORT *glyphIdArray; + unsigned int segCount; + unsigned int glyphIdArrayLength; + }; + + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + accelerator_t accel; + accel.init (this); + return accel.get_glyph_func (&accel, codepoint, glyph); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + if (unlikely (!c->check_range (this, length))) + { + /* Some broken fonts have too long of a "length" value. + * If that is the case, just change the value to truncate + * the subtable at the end of the blob. */ + uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); + if (!c->try_set (&length, new_length)) + return_trace (false); + } + + return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + } + + protected: + USHORT format; /* Format number is set to 4. */ + USHORT length; /* This is the length in bytes of the + * subtable. */ + USHORT languageZ; /* Ignore. */ + USHORT segCountX2; /* 2 x segCount. */ + USHORT searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ + USHORT entrySelectorZ; /* log2(searchRange/2) */ + USHORT rangeShiftZ; /* 2 x segCount - searchRange */ + + USHORT values[VAR]; +#if 0 + USHORT endCount[segCount]; /* End characterCode for each segment, + * last=0xFFFFu. */ + USHORT reservedPad; /* Set to 0. */ + USHORT startCount[segCount]; /* Start character code for each segment. */ + SHORT idDelta[segCount]; /* Delta for all character codes in segment. */ + USHORT idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + USHORT glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ +#endif + + public: + DEFINE_SIZE_ARRAY (14, values); +}; + +struct CmapSubtableLongGroup +{ + friend struct CmapSubtableFormat12; + friend struct CmapSubtableFormat13; + + int cmp (hb_codepoint_t codepoint) const + { + if (codepoint < startCharCode) return -1; + if (codepoint > endCharCode) return +1; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + ULONG startCharCode; /* First character code in this group. */ + ULONG endCharCode; /* Last character code in this group. */ + ULONG glyphID; /* Glyph index; interpretation depends on + * subtable format. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +template <typename UINT> +struct CmapSubtableTrimmed +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + /* Rely on our implicit array bound-checking. */ + hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; + if (!gid) + return false; + *glyph = gid; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); + } + + protected: + UINT formatReserved; /* Subtable format and (maybe) padding. */ + UINT lengthZ; /* Byte length of this subtable. */ + UINT languageZ; /* Ignore. */ + UINT startCharCode; /* First character code covered. */ + ArrayOf<GlyphID, UINT> + glyphIdArray; /* Array of glyph index values for character + * codes in the range. */ + public: + DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); +}; + +struct CmapSubtableFormat6 : CmapSubtableTrimmed<USHORT> {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed<ULONG > {}; + +template <typename T> +struct CmapSubtableLongSegmented +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + int i = groups.bsearch (codepoint); + if (i == -1) + return false; + *glyph = T::group_get_glyph (groups[i], codepoint); + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && groups.sanitize (c)); + } + + protected: + USHORT format; /* Subtable format; set to 12. */ + USHORT reservedZ; /* Reserved; set to 0. */ + ULONG lengthZ; /* Byte length of this subtable. */ + ULONG languageZ; /* Ignore. */ + SortedArrayOf<CmapSubtableLongGroup, ULONG> + groups; /* Groupings. */ + public: + DEFINE_SIZE_ARRAY (16, groups); +}; + +struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12> +{ + static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return group.glyphID + (u - group.startCharCode); } +}; + +struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13> +{ + static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) + { return group.glyphID; } +}; + +typedef enum +{ + GLYPH_VARIANT_NOT_FOUND = 0, + GLYPH_VARIANT_FOUND = 1, + GLYPH_VARIANT_USE_DEFAULT = 2 +} glyph_variant_t; + +struct UnicodeValueRange +{ + inline int cmp (const hb_codepoint_t &codepoint) const + { + if (codepoint < startUnicodeValue) return -1; + if (codepoint > startUnicodeValue + additionalCount) return +1; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + UINT24 startUnicodeValue; /* First value in this range. */ + BYTE additionalCount; /* Number of additional values in this + * range. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef SortedArrayOf<UnicodeValueRange, ULONG> DefaultUVS; + +struct UVSMapping +{ + inline int cmp (const hb_codepoint_t &codepoint) const + { + return unicodeValue.cmp (codepoint); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + UINT24 unicodeValue; /* Base Unicode value of the UVS */ + GlyphID glyphID; /* Glyph ID of the UVS */ + public: + DEFINE_SIZE_STATIC (5); +}; + +typedef SortedArrayOf<UVSMapping, ULONG> NonDefaultUVS; + +struct VariationSelectorRecord +{ + inline glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const + { + int i; + const DefaultUVS &defaults = base+defaultUVS; + i = defaults.bsearch (codepoint); + if (i != -1) + return GLYPH_VARIANT_USE_DEFAULT; + const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; + i = nonDefaults.bsearch (codepoint); + if (i != -1) + { + *glyph = nonDefaults[i].glyphID; + return GLYPH_VARIANT_FOUND; + } + return GLYPH_VARIANT_NOT_FOUND; + } + + inline int cmp (const hb_codepoint_t &variation_selector) const + { + return varSelector.cmp (variation_selector); + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + defaultUVS.sanitize (c, base) && + nonDefaultUVS.sanitize (c, base)); + } + + UINT24 varSelector; /* Variation selector. */ + OffsetTo<DefaultUVS, ULONG> + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + OffsetTo<NonDefaultUVS, ULONG> + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + public: + DEFINE_SIZE_STATIC (11); +}; + +struct CmapSubtableFormat14 +{ + inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + record.sanitize (c, this)); + } + + protected: + USHORT format; /* Format number is set to 14. */ + ULONG lengthZ; /* Byte length of this subtable. */ + SortedArrayOf<VariationSelectorRecord, ULONG> + record; /* Variation selector records; sorted + * in increasing order of `varSelector'. */ + public: + DEFINE_SIZE_ARRAY (10, record); +}; + +struct CmapSubtable +{ + /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ + + inline bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 0: return u.format0 .get_glyph(codepoint, glyph); + case 4: return u.format4 .get_glyph(codepoint, glyph); + case 6: return u.format6 .get_glyph(codepoint, glyph); + case 10: return u.format10.get_glyph(codepoint, glyph); + case 12: return u.format12.get_glyph(codepoint, glyph); + case 13: return u.format13.get_glyph(codepoint, glyph); + case 14: + default: return false; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0 .sanitize (c)); + case 4: return_trace (u.format4 .sanitize (c)); + case 6: return_trace (u.format6 .sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + case 12: return_trace (u.format12.sanitize (c)); + case 13: return_trace (u.format13.sanitize (c)); + case 14: return_trace (u.format14.sanitize (c)); + default:return_trace (true); + } + } + + public: + union { + USHORT format; /* Format identifier */ + CmapSubtableFormat0 format0; + CmapSubtableFormat4 format4; + CmapSubtableFormat6 format6; + CmapSubtableFormat10 format10; + CmapSubtableFormat12 format12; + CmapSubtableFormat13 format13; + CmapSubtableFormat14 format14; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct EncodingRecord +{ + inline int cmp (const EncodingRecord &other) const + { + int ret; + ret = platformID.cmp (other.platformID); + if (ret) return ret; + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + subtable.sanitize (c, base)); + } + + USHORT platformID; /* Platform ID. */ + USHORT encodingID; /* Platform-specific encoding ID. */ + OffsetTo<CmapSubtable, ULONG> + subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct cmap +{ + static const hb_tag_t tableTag = HB_OT_TAG_cmap; + + inline const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID.set (platform_id); + key.encodingID.set (encoding_id); + + /* Note: We can use bsearch, but since it has no performance + * implications, we use lsearch and as such accept fonts with + * unsorted subtable list. */ + int result = encodingRecord./*bsearch*/lsearch (key); + if (result == -1 || !encodingRecord[result].subtable) + return NULL; + + return &(this+encodingRecord[result].subtable); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + USHORT version; /* Table version number (0). */ + SortedArrayOf<EncodingRecord> + encodingRecord; /* Encoding tables. */ + public: + DEFINE_SIZE_ARRAY (4, encodingRecord); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_CMAP_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-font.cc b/gfx/harfbuzz/src/hb-ot-font.cc new file mode 100644 index 000000000..5be055d34 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-font.cc @@ -0,0 +1,662 @@ +/* + * Copyright © 2011,2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#include "hb-private.hh" + +#include "hb-ot.h" + +#include "hb-font-private.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-cbdt-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-os2-table.hh" +//#include "hb-ot-post-table.hh" + + +struct hb_ot_face_metrics_accelerator_t +{ + unsigned int num_metrics; + unsigned int num_advances; + unsigned int default_advance; + unsigned short ascender; + unsigned short descender; + unsigned short line_gap; + bool has_font_extents; + + const OT::_mtx *table; + hb_blob_t *blob; + + inline void init (hb_face_t *face, + hb_tag_t _hea_tag, + hb_tag_t _mtx_tag, + hb_tag_t os2_tag, + unsigned int default_advance = 0) + { + this->default_advance = default_advance ? default_advance : face->get_upem (); + + bool got_font_extents = false; + if (os2_tag) + { + hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>::sanitize (face->reference_table (os2_tag)); + const OT::os2 *os2 = OT::Sanitizer<OT::os2>::lock_instance (os2_blob); +#define USE_TYPO_METRICS (1u<<7) + if (0 != (os2->fsSelection & USE_TYPO_METRICS)) + { + this->ascender = os2->sTypoAscender; + this->descender = os2->sTypoDescender; + this->line_gap = os2->sTypoLineGap; + got_font_extents = (this->ascender | this->descender) != 0; + } + hb_blob_destroy (os2_blob); + } + + hb_blob_t *_hea_blob = OT::Sanitizer<OT::_hea>::sanitize (face->reference_table (_hea_tag)); + const OT::_hea *_hea = OT::Sanitizer<OT::_hea>::lock_instance (_hea_blob); + this->num_advances = _hea->numberOfLongMetrics; + if (!got_font_extents) + { + this->ascender = _hea->ascender; + this->descender = _hea->descender; + this->line_gap = _hea->lineGap; + got_font_extents = (this->ascender | this->descender) != 0; + } + hb_blob_destroy (_hea_blob); + + this->has_font_extents = got_font_extents; + + this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag)); + + /* Cap num_metrics() and num_advances() based on table length. */ + unsigned int len = hb_blob_get_length (this->blob); + if (unlikely (this->num_advances * 4 > len)) + this->num_advances = len / 4; + this->num_metrics = this->num_advances + (len - 4 * this->num_advances) / 2; + + /* We MUST set num_metrics to zero if num_advances is zero. + * Our get_advance() depends on that. */ + if (unlikely (!this->num_advances)) + { + this->num_metrics = this->num_advances = 0; + hb_blob_destroy (this->blob); + this->blob = hb_blob_get_empty (); + } + this->table = OT::Sanitizer<OT::_mtx>::lock_instance (this->blob); + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline unsigned int get_advance (hb_codepoint_t glyph) const + { + if (unlikely (glyph >= this->num_metrics)) + { + /* If this->num_metrics is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ + if (this->num_metrics) + return 0; + else + return this->default_advance; + } + + if (glyph >= this->num_advances) + glyph = this->num_advances - 1; + + return this->table->longMetric[glyph].advance; + } +}; + +struct hb_ot_face_glyf_accelerator_t +{ + bool short_offset; + unsigned int num_glyphs; + const OT::loca *loca; + const OT::glyf *glyf; + hb_blob_t *loca_blob; + hb_blob_t *glyf_blob; + unsigned int glyf_len; + + inline void init (hb_face_t *face) + { + hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (face->reference_table (HB_OT_TAG_head)); + const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob); + if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0) + { + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + hb_blob_destroy (head_blob); + return; + } + this->short_offset = 0 == head->indexToLocFormat; + hb_blob_destroy (head_blob); + + this->loca_blob = OT::Sanitizer<OT::loca>::sanitize (face->reference_table (HB_OT_TAG_loca)); + this->loca = OT::Sanitizer<OT::loca>::lock_instance (this->loca_blob); + this->glyf_blob = OT::Sanitizer<OT::glyf>::sanitize (face->reference_table (HB_OT_TAG_glyf)); + this->glyf = OT::Sanitizer<OT::glyf>::lock_instance (this->glyf_blob); + + this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1; + this->glyf_len = hb_blob_get_length (this->glyf_blob); + } + + inline void fini (void) + { + hb_blob_destroy (this->loca_blob); + hb_blob_destroy (this->glyf_blob); + } + + inline bool get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + if (unlikely (glyph >= this->num_glyphs)) + return false; + + unsigned int start_offset, end_offset; + if (this->short_offset) + { + start_offset = 2 * this->loca->u.shortsZ[glyph]; + end_offset = 2 * this->loca->u.shortsZ[glyph + 1]; + } + else + { + start_offset = this->loca->u.longsZ[glyph]; + end_offset = this->loca->u.longsZ[glyph + 1]; + } + + if (start_offset > end_offset || end_offset > this->glyf_len) + return false; + + if (end_offset - start_offset < OT::glyfGlyphHeader::static_size) + return true; /* Empty glyph; zero extents. */ + + const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset<OT::glyfGlyphHeader> (this->glyf, start_offset); + + extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); + extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); + extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; + extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + + return true; + } +}; + +struct hb_ot_face_cbdt_accelerator_t +{ + hb_blob_t *cblc_blob; + hb_blob_t *cbdt_blob; + const OT::CBLC *cblc; + const OT::CBDT *cbdt; + + unsigned int cbdt_len; + float upem; + + inline void init (hb_face_t *face) + { + upem = face->get_upem(); + + cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC)); + cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT)); + cbdt_len = hb_blob_get_length (cbdt_blob); + + if (hb_blob_get_length (cblc_blob) == 0) { + cblc = NULL; + cbdt = NULL; + return; /* Not a bitmap font. */ + } + cblc = OT::Sanitizer<OT::CBLC>::lock_instance (cblc_blob); + cbdt = OT::Sanitizer<OT::CBDT>::lock_instance (cbdt_blob); + + } + + inline void fini (void) + { + hb_blob_destroy (this->cblc_blob); + hb_blob_destroy (this->cbdt_blob); + } + + inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */ + + if (cblc == NULL) + return false; // Not a color bitmap font. + + const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem); + if (subtable_record == NULL) + return false; + + if (subtable_record->get_extents (extents)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format)) + return false; + + { + /* TODO Move the following into CBDT struct when adding more formats. */ + + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) + { + case 17: { + if (unlikely (image_length < OT::GlyphBitmapDataFormat17::min_size)) + return false; + + const OT::GlyphBitmapDataFormat17& glyphFormat17 = + OT::StructAtOffset<OT::GlyphBitmapDataFormat17> (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (extents); + } + break; + default: + // TODO: Support other image formats. + return false; + } + } + + /* Convert to the font units. */ + extents->x_bearing *= upem / (float) x_ppem; + extents->y_bearing *= upem / (float) y_ppem; + extents->width *= upem / (float) x_ppem; + extents->height *= upem / (float) y_ppem; + + return true; + } +}; + +typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + +template <typename Type> +static inline bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) +{ + const Type *typed_obj = (const Type *) obj; + return typed_obj->get_glyph (codepoint, glyph); +} + +template <typename Type> +static inline bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) +{ + const Type *typed_obj = (const Type *) obj; + if (likely (typed_obj->get_glyph (codepoint, glyph))) + return true; + + if (codepoint <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * http://www.microsoft.com/typography/otspec/recom.htm + * under "Non-Standard (Symbol) Fonts". */ + return typed_obj->get_glyph (0xF000u + codepoint, glyph); + } + + return false; +} + +struct hb_ot_face_cmap_accelerator_t +{ + hb_cmap_get_glyph_func_t get_glyph_func; + const void *get_glyph_data; + OT::CmapSubtableFormat4::accelerator_t format4_accel; + + const OT::CmapSubtableFormat14 *uvs_table; + hb_blob_t *blob; + + inline void init (hb_face_t *face) + { + this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap)); + const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob); + const OT::CmapSubtable *subtable = NULL; + const OT::CmapSubtableFormat14 *subtable_uvs = NULL; + + bool symbol = false; + /* 32-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 10); + if (!subtable) subtable = cmap->find_subtable (0, 6); + if (!subtable) subtable = cmap->find_subtable (0, 4); + /* 16-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 1); + if (!subtable) subtable = cmap->find_subtable (0, 3); + if (!subtable) subtable = cmap->find_subtable (0, 2); + if (!subtable) subtable = cmap->find_subtable (0, 1); + if (!subtable) subtable = cmap->find_subtable (0, 0); + if (!subtable) + { + subtable = cmap->find_subtable (3, 0); + if (subtable) symbol = true; + } + /* Meh. */ + if (!subtable) subtable = &OT::Null(OT::CmapSubtable); + + /* UVS subtable. */ + if (!subtable_uvs) + { + const OT::CmapSubtable *st = cmap->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + /* Meh. */ + if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); + + this->uvs_table = subtable_uvs; + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>; + else + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>; break; + case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>; break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_func = this->format4_accel.get_glyph_func; + } + break; + } + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) const + { + return this->get_glyph_func (this->get_glyph_data, unicode, glyph); + } + + inline bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (this->uvs_table->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case OT::GLYPH_VARIANT_NOT_FOUND: return false; + case OT::GLYPH_VARIANT_FOUND: return true; + case OT::GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph); + } +}; + +template <typename T> +struct hb_lazy_loader_t +{ + inline void init (hb_face_t *face_) + { + face = face_; + instance = NULL; + } + + inline void fini (void) + { + if (instance && instance != &OT::Null(T)) + { + instance->fini(); + free (instance); + } + } + + inline const T* operator-> (void) const + { + retry: + T *p = (T *) hb_atomic_ptr_get (&instance); + if (unlikely (!p)) + { + p = (T *) calloc (1, sizeof (T)); + if (unlikely (!p)) + return &OT::Null(T); + p->init (face); + if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p))) + { + p->fini (); + goto retry; + } + } + return p; + } + + private: + hb_face_t *face; + T *instance; +}; + +struct hb_ot_font_t +{ + hb_ot_face_cmap_accelerator_t cmap; + hb_ot_face_metrics_accelerator_t h_metrics; + hb_ot_face_metrics_accelerator_t v_metrics; + hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf; + hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt; +}; + + +static hb_ot_font_t * +_hb_ot_font_create (hb_face_t *face) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); + + if (unlikely (!ot_font)) + return NULL; + + ot_font->cmap.init (face); + ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_os2); + ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_TAG_NONE, + ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */ + ot_font->glyf.init (face); + ot_font->cbdt.init (face); + + return ot_font; +} + +static void +_hb_ot_font_destroy (hb_ot_font_t *ot_font) +{ + ot_font->cmap.fini (); + ot_font->h_metrics.fini (); + ot_font->v_metrics.fini (); + ot_font->glyf.fini (); + ot_font->cbdt.fini (); + + free (ot_font); +} + + +static hb_bool_t +hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) + +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return ot_font->cmap.get_nominal_glyph (unicode, glyph); +} + +static hb_bool_t +hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph); +} + +static hb_position_t +hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return font->em_scale_x (ot_font->h_metrics.get_advance (glyph)); +} + +static hb_position_t +hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph)); +} + +static hb_bool_t +hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + bool ret = ot_font->glyf->get_extents (glyph, extents); + if (!ret) + ret = ot_font->cbdt->get_extents (glyph, extents); + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); + return ret; +} + +static hb_bool_t +hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender); + metrics->descender = font->em_scale_y (ot_font->h_metrics.descender); + metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap); + return ot_font->h_metrics.has_font_extents; +} + +static hb_bool_t +hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender); + metrics->descender = font->em_scale_x (ot_font->v_metrics.descender); + metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap); + return ot_font->v_metrics.has_font_extents; +} + +static hb_font_funcs_t *static_ot_funcs = NULL; + +#ifdef HB_USE_ATEXIT +static +void free_static_ot_funcs (void) +{ + hb_font_funcs_destroy (static_ot_funcs); +} +#endif + +static hb_font_funcs_t * +_hb_ot_get_font_funcs (void) +{ +retry: + hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs); + + if (unlikely (!funcs)) + { + funcs = hb_font_funcs_create (); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, NULL, NULL); + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, NULL, NULL); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, NULL, NULL); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, NULL, NULL); + hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, NULL, NULL); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, NULL, NULL); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, NULL, NULL); + //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, NULL, NULL); + //hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, NULL, NULL); TODO + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, NULL, NULL); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, NULL, NULL); + //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, NULL, NULL); TODO + //hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, NULL, NULL); TODO + //hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, NULL, NULL); TODO + + hb_font_funcs_make_immutable (funcs); + + if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, NULL, funcs)) { + hb_font_funcs_destroy (funcs); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_static_ot_funcs); /* First person registers atexit() callback. */ +#endif + }; + + return funcs; +} + + +/** + * hb_ot_font_set_funcs: + * + * Since: 0.9.28 + **/ +void +hb_ot_font_set_funcs (hb_font_t *font) +{ + hb_ot_font_t *ot_font = _hb_ot_font_create (font->face); + if (unlikely (!ot_font)) + return; + + hb_font_set_funcs (font, + _hb_ot_get_font_funcs (), + ot_font, + (hb_destroy_func_t) _hb_ot_font_destroy); +} diff --git a/gfx/harfbuzz/src/hb-ot-font.h b/gfx/harfbuzz/src/hb-ot-font.h new file mode 100644 index 000000000..80eaa54b1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-font.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#ifndef HB_OT_H_IN +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_ot_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_OT_FONT_H */ diff --git a/gfx/harfbuzz/src/hb-ot-glyf-table.hh b/gfx/harfbuzz/src/hb-ot-glyf-table.hh new file mode 100644 index 000000000..dc7aa8469 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2015 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_GLYF_TABLE_HH +#define HB_OT_GLYF_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + */ + +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + + +struct loca +{ + static const hb_tag_t tableTag = HB_OT_TAG_loca; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + public: + union { + USHORT shortsZ[VAR]; /* Location offset divided by 2. */ + ULONG longsZ[VAR]; /* Location offset. */ + } u; + DEFINE_SIZE_ARRAY (0, u.longsZ); +}; + + +/* + * glyf -- TrueType Glyph Data + */ + +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + + +struct glyf +{ + static const hb_tag_t tableTag = HB_OT_TAG_glyf; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + public: + BYTE dataX[VAR]; /* Glyphs data. */ + + DEFINE_SIZE_ARRAY (0, dataX); +}; + +struct glyfGlyphHeader +{ + SHORT numberOfContours; /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-head-table.hh b/gfx/harfbuzz/src/hb-ot-head-table.hh new file mode 100644 index 000000000..9c3e51eb0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-head-table.hh @@ -0,0 +1,154 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * head -- Font Header + */ + +#define HB_OT_TAG_head HB_TAG('h','e','a','d') + +struct head +{ + static const hb_tag_t tableTag = HB_OT_TAG_head; + + inline unsigned int get_upem (void) const + { + unsigned int upem = unitsPerEm; + /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ + return 16 <= upem && upem <= 16384 ? upem : 1000; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + version.major == 1 && + magicNumber == 0x5F0F3CF5u); + } + + protected: + FixedVersion<>version; /* Version of the head table--currently + * 0x00010000u for version 1.0. */ + FixedVersion<>fontRevision; /* Set by font manufacturer. */ + ULONG checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as ULONG, then store + * 0xB1B0AFBAu - sum. */ + ULONG magicNumber; /* Set to 0x5F0F3CF5u. */ + USHORT flags; /* Bit 0: Baseline for font at y=0; + * Bit 1: Left sidebearing point at x=0; + * Bit 2: Instructions may depend on point size; + * Bit 3: Force ppem to integer values for all + * internal scaler math; may use fractional + * ppem sizes if this bit is clear; + * Bit 4: Instructions may alter advance width + * (the advance widths might not scale linearly); + + * Bits 5-10: These should be set according to + * Apple's specification. However, they are not + * implemented in OpenType. + * Bit 5: This bit should be set in fonts that are + * intended to e laid out vertically, and in + * which the glyphs have been drawn such that an + * x-coordinate of 0 corresponds to the desired + * vertical baseline. + * Bit 6: This bit must be set to zero. + * Bit 7: This bit should be set if the font + * requires layout for correct linguistic + * rendering (e.g. Arabic fonts). + * Bit 8: This bit should be set for a GX font + * which has one or more metamorphosis effects + * designated as happening by default. + * Bit 9: This bit should be set if the font + * contains any strong right-to-left glyphs. + * Bit 10: This bit should be set if the font + * contains Indic-style rearrangement effects. + + * Bit 11: Font data is 'lossless,' as a result + * of having been compressed and decompressed + * with the Agfa MicroType Express engine. + * Bit 12: Font converted (produce compatible metrics) + * Bit 13: Font optimized for ClearType™. + * Note, fonts that rely on embedded bitmaps (EBDT) + * for rendering should not be considered optimized + * for ClearType, and therefore should keep this bit + * cleared. + * Bit 14: Last Resort font. If set, indicates that + * the glyphs encoded in the cmap subtables are simply + * generic symbolic representations of code point + * ranges and don’t truly represent support for those + * code points. If unset, indicates that the glyphs + * encoded in the cmap subtables represent proper + * support for those code points. + * Bit 15: Reserved, set to 0. */ + USHORT unitsPerEm; /* Valid range is from 16 to 16384. This value + * should be a power of 2 for fonts that have + * TrueType outlines. */ + LONGDATETIME created; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + LONGDATETIME modified; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + SHORT xMin; /* For all glyph bounding boxes. */ + SHORT yMin; /* For all glyph bounding boxes. */ + SHORT xMax; /* For all glyph bounding boxes. */ + SHORT yMax; /* For all glyph bounding boxes. */ + USHORT macStyle; /* Bit 0: Bold (if set to 1); + * Bit 1: Italic (if set to 1) + * Bit 2: Underline (if set to 1) + * Bit 3: Outline (if set to 1) + * Bit 4: Shadow (if set to 1) + * Bit 5: Condensed (if set to 1) + * Bit 6: Extended (if set to 1) + * Bits 7-15: Reserved (set to 0). */ + USHORT lowestRecPPEM; /* Smallest readable size in pixels. */ + SHORT fontDirectionHint; /* Deprecated (Set to 2). + * 0: Fully mixed directional glyphs; + * 1: Only strongly left to right; + * 2: Like 1 but also contains neutrals; + * -1: Only strongly right to left; + * -2: Like -1 but also contains neutrals. */ + public: + SHORT indexToLocFormat; /* 0 for short offsets, 1 for long. */ + SHORT glyphDataFormat; /* 0 for current format. */ + + DEFINE_SIZE_STATIC (54); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-hhea-table.hh b/gfx/harfbuzz/src/hb-ot-hhea-table.hh new file mode 100644 index 000000000..c8e9536cf --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh @@ -0,0 +1,103 @@ +/* + * 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" + + +namespace OT { + + +/* + * hhea -- The Horizontal Header Table + * vhea -- The Vertical Header Table + */ + +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') + + +struct _hea +{ + static const hb_tag_t tableTag = HB_TAG('_','h','e','a'); + + static const hb_tag_t hheaTag = HB_OT_TAG_hhea; + static const hb_tag_t vheaTag = HB_OT_TAG_vhea; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && likely (version.major == 1)); + } + + public: + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + SHORT caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + SHORT caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + SHORT caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + SHORT reserved1; /* Set to 0. */ + SHORT reserved2; /* Set to 0. */ + SHORT reserved3; /* Set to 0. */ + SHORT reserved4; /* Set to 0. */ + SHORT metricDataFormat; /* 0 for current format. */ + USHORT numberOfLongMetrics; /* Number of LongMetric entries in metric + * table. */ + public: + DEFINE_SIZE_STATIC (36); +}; + +struct hhea : _hea { + static const hb_tag_t tableTag = HB_OT_TAG_hhea; +}; +struct vhea : _hea { + static const hb_tag_t tableTag = HB_OT_TAG_vhea; +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh new file mode 100644 index 000000000..a9606b3d2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh @@ -0,0 +1,104 @@ +/* + * 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" + + +namespace OT { + + +/* + * hmtx -- The Horizontal Metrics Table + * vmtx -- The Vertical Metrics Table + */ + +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +struct LongMetric +{ + UFWORD advance; /* Advance width/height. */ + FWORD lsb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct _mtx +{ + static const hb_tag_t tableTag = HB_TAG('_','m','t','x'); + + static const hb_tag_t hmtxTag = HB_OT_TAG_hmtx; + static const hb_tag_t vmtxTag = HB_OT_TAG_vmtx; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + public: + LongMetric longMetric[VAR]; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ + FWORD leadingBearingX[VAR]; /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ + public: + DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX); +}; + +struct hmtx : _mtx { + static const hb_tag_t tableTag = HB_OT_TAG_hmtx; +}; +struct vmtx : _mtx { + static const hb_tag_t tableTag = HB_OT_TAG_vmtx; +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh new file mode 100644 index 000000000..62ca7a348 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh @@ -0,0 +1,1713 @@ +/* + * Copyright © 2007,2008,2009 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_COMMON_PRIVATE_HH +#define HB_OT_LAYOUT_COMMON_PRIVATE_HH + +#include "hb-ot-layout-private.hh" +#include "hb-open-type-private.hh" +#include "hb-set-private.hh" + + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 6 +#endif +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif + + +namespace OT { + + +#define TRACE_DISPATCH(this, format) \ + hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "format %d", (int) format); + + +#define NOT_COVERED ((unsigned int) -1) + + + +/* + * + * OpenType Layout Common Table Formats + * + */ + + +/* + * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList + */ + +template <typename Type> +struct Record +{ + inline int cmp (hb_tag_t a) const { + return tag.cmp (a); + } + + struct sanitize_closure_t { + hb_tag_t tag; + const void *list_base; + }; + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + const sanitize_closure_t closure = {tag, base}; + return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + } + + Tag tag; /* 4-byte Tag identifier */ + OffsetTo<Type> + offset; /* Offset from beginning of object holding + * the Record */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template <typename Type> +struct RecordArrayOf : SortedArrayOf<Record<Type> > { + inline const Tag& get_tag (unsigned int i) const + { + /* We cheat slightly and don't define separate Null objects + * for Record types. Instead, we return the correct Null(Tag) + * here. */ + if (unlikely (i >= this->len)) return Null(Tag); + return (*this)[i].tag; + } + inline unsigned int get_tags (unsigned int start_offset, + unsigned int *record_count /* IN/OUT */, + hb_tag_t *record_tags /* OUT */) const + { + if (record_count) { + const Record<Type> *arr = this->sub_array (start_offset, record_count); + unsigned int count = *record_count; + for (unsigned int i = 0; i < count; i++) + record_tags[i] = arr[i].tag; + } + return this->len; + } + inline bool find_index (hb_tag_t tag, unsigned int *index) const + { + /* If we want to allow non-sorted data, we can lsearch(). */ + int i = this->/*lsearch*/bsearch (tag); + if (i != -1) { + if (index) *index = i; + return true; + } else { + if (index) *index = Index::NOT_FOUND_INDEX; + return false; + } + } +}; + +template <typename Type> +struct RecordListOf : RecordArrayOf<Type> +{ + inline const Type& operator [] (unsigned int i) const + { return this+RecordArrayOf<Type>::operator [](i).offset; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (RecordArrayOf<Type>::sanitize (c, this)); + } +}; + + +struct RangeRecord +{ + inline int cmp (hb_codepoint_t g) const { + return g < start ? -1 : g <= end ? 0 : +1 ; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline bool intersects (const hb_set_t *glyphs) const { + return glyphs->intersects (start, end); + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const { + glyphs->add_range (start, end); + } + + GlyphID start; /* First GlyphID in the range */ + GlyphID end; /* Last GlyphID in the range */ + USHORT value; /* Value */ + public: + DEFINE_SIZE_STATIC (6); +}; +DEFINE_NULL_DATA (RangeRecord, "\000\001"); + + +struct IndexArray : ArrayOf<Index> +{ + inline unsigned int get_indexes (unsigned int start_offset, + unsigned int *_count /* IN/OUT */, + unsigned int *_indexes /* OUT */) const + { + if (_count) { + const USHORT *arr = this->sub_array (start_offset, _count); + unsigned int count = *_count; + for (unsigned int i = 0; i < count; i++) + _indexes[i] = arr[i]; + } + return this->len; + } +}; + + +struct Script; +struct LangSys; +struct Feature; + + +struct LangSys +{ + inline unsigned int get_feature_count (void) const + { return featureIndex.len; } + inline hb_tag_t get_feature_index (unsigned int i) const + { return featureIndex[i]; } + inline unsigned int get_feature_indexes (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) const + { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } + + inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; } + inline unsigned int get_required_feature_index (void) const + { + if (reqFeatureIndex == 0xFFFFu) + return Index::NOT_FOUND_INDEX; + return reqFeatureIndex;; + } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record<LangSys>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && featureIndex.sanitize (c)); + } + + Offset<> lookupOrderZ; /* = Null (reserved for an offset to a + * reordering table) */ + USHORT reqFeatureIndex;/* Index of a feature required for this + * language system--if no required features + * = 0xFFFFu */ + IndexArray featureIndex; /* Array of indices into the FeatureList */ + public: + DEFINE_SIZE_ARRAY (6, featureIndex); +}; +DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); + + +struct Script +{ + inline unsigned int get_lang_sys_count (void) const + { return langSys.len; } + inline const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + inline unsigned int get_lang_sys_tags (unsigned int start_offset, + unsigned int *lang_sys_count /* IN/OUT */, + hb_tag_t *lang_sys_tags /* OUT */) const + { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } + inline const LangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } + inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record<Script>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); + return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); + } + + protected: + OffsetTo<LangSys> + defaultLangSys; /* Offset to DefaultLangSys table--from + * beginning of Script table--may be Null */ + RecordArrayOf<LangSys> + langSys; /* Array of LangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY (4, langSys); +}; + +typedef RecordListOf<Script> ScriptList; + + +/* http://www.microsoft.com/typography/otspec/features_pt.htm#size */ +struct FeatureParamsSize +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + + /* This subtable has some "history", if you will. Some earlier versions of + * Adobe tools calculated the offset of the FeatureParams sutable from the + * beginning of the FeatureList table! Now, that is dealt with in the + * Feature implementation. But we still need to be able to tell junk from + * real data. Note: We don't check that the nameID actually exists. + * + * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk : + * + * Yes, it is correct that a new version of the AFDKO (version 2.0) will be + * coming out soon, and that the makeotf program will build a font with a + * 'size' feature that is correct by the specification. + * + * The specification for this feature tag is in the "OpenType Layout Tag + * Registry". You can see a copy of this at: + * http://partners.adobe.com/public/developer/opentype/index_tag8.html#size + * + * Here is one set of rules to determine if the 'size' feature is built + * correctly, or as by the older versions of MakeOTF. You may be able to do + * better. + * + * Assume that the offset to the size feature is according to specification, + * and make the following value checks. If it fails, assume the the size + * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it. + * If this fails, reject the 'size' feature. The older makeOTF's calculated the + * offset from the beginning of the FeatureList table, rather than from the + * beginning of the 'size' Feature table. + * + * If "design size" == 0: + * fails check + * + * Else if ("subfamily identifier" == 0 and + * "range start" == 0 and + * "range end" == 0 and + * "range start" == 0 and + * "menu name ID" == 0) + * passes check: this is the format used when there is a design size + * specified, but there is no recommended size range. + * + * Else if ("design size" < "range start" or + * "design size" > "range end" or + * "range end" <= "range start" or + * "menu name ID" < 256 or + * "menu name ID" > 32767 or + * menu name ID is not a name ID which is actually in the name table) + * fails test + * Else + * passes test. + */ + + if (!designSize) + return_trace (false); + else if (subfamilyID == 0 && + subfamilyNameID == 0 && + rangeStart == 0 && + rangeEnd == 0) + return_trace (true); + else if (designSize < rangeStart || + designSize > rangeEnd || + subfamilyNameID < 256 || + subfamilyNameID > 32767) + return_trace (false); + else + return_trace (true); + } + + USHORT designSize; /* Represents the design size in 720/inch + * units (decipoints). The design size entry + * must be non-zero. When there is a design + * size but no recommended size range, the + * rest of the array will consist of zeros. */ + USHORT subfamilyID; /* Has no independent meaning, but serves + * as an identifier that associates fonts + * in a subfamily. All fonts which share a + * Preferred or Font Family name and which + * differ only by size range shall have the + * same subfamily value, and no fonts which + * differ in weight or style shall have the + * same subfamily value. If this value is + * zero, the remaining fields in the array + * will be ignored. */ + USHORT subfamilyNameID;/* If the preceding value is non-zero, this + * value must be set in the range 256 - 32767 + * (inclusive). It records the value of a + * field in the name table, which must + * contain English-language strings encoded + * in Windows Unicode and Macintosh Roman, + * and may contain additional strings + * localized to other scripts and languages. + * Each of these strings is the name an + * application should use, in combination + * with the family name, to represent the + * subfamily in a menu. Applications will + * choose the appropriate version based on + * their selection criteria. */ + USHORT rangeStart; /* Large end of the recommended usage range + * (inclusive), stored in 720/inch units + * (decipoints). */ + USHORT rangeEnd; /* Small end of the recommended usage range + (exclusive), stored in 720/inch units + * (decipoints). */ + public: + DEFINE_SIZE_STATIC (10); +}; + +/* http://www.microsoft.com/typography/otspec/features_pt.htm#ssxx */ +struct FeatureParamsStylisticSet +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Right now minorVersion is at zero. Which means, any table supports + * the uiNameID field. */ + return_trace (c->check_struct (this)); + } + + USHORT version; /* (set to 0): This corresponds to a “minor” + * version number. Additional data may be + * added to the end of this Feature Parameters + * table in the future. */ + + USHORT uiNameID; /* The 'name' table name ID that specifies a + * string (or strings, for multiple languages) + * for a user-interface label for this + * feature. The values of uiLabelNameId and + * sampleTextNameId are expected to be in the + * font-specific name ID range (256-32767), + * though that is not a requirement in this + * Feature Parameters specification. The + * user-interface label for the feature can + * be provided in multiple languages. An + * English string should be included as a + * fallback. The string should be kept to a + * minimal length to fit comfortably with + * different application interfaces. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +/* http://www.microsoft.com/typography/otspec/features_ae.htm#cv01-cv99 */ +struct FeatureParamsCharacterVariants +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + characters.sanitize (c)); + } + + USHORT format; /* Format number is set to 0. */ + USHORT featUILableNameID; /* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) for a + * user-interface label for this + * feature. (May be NULL.) */ + USHORT featUITooltipTextNameID;/* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) that an + * application can use for tooltip + * text for this feature. (May be + * NULL.) */ + USHORT sampleTextNameID; /* The ‘name’ table name ID that + * specifies sample text that + * illustrates the effect of this + * feature. (May be NULL.) */ + USHORT numNamedParameters; /* Number of named parameters. (May + * be zero.) */ + USHORT firstParamUILabelNameID;/* The first ‘name’ table name ID + * used to specify strings for + * user-interface labels for the + * feature parameters. (Must be zero + * if numParameters is zero.) */ + ArrayOf<UINT24> + characters; /* Array of the Unicode Scalar Value + * of the characters for which this + * feature provides glyph variants. + * (May be zero.) */ + public: + DEFINE_SIZE_ARRAY (14, characters); +}; + +struct FeatureParams +{ + inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const + { + TRACE_SANITIZE (this); + if (tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.sanitize (c)); + return_trace (true); + } + + inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const + { + if (tag == HB_TAG ('s','i','z','e')) + return u.size; + return Null(FeatureParamsSize); + } + + private: + union { + FeatureParamsSize size; + FeatureParamsStylisticSet stylisticSet; + FeatureParamsCharacterVariants characterVariants; + } u; + DEFINE_SIZE_STATIC (17); +}; + +struct Feature +{ + inline unsigned int get_lookup_count (void) const + { return lookupIndex.len; } + inline hb_tag_t get_lookup_index (unsigned int i) const + { return lookupIndex[i]; } + inline unsigned int get_lookup_indexes (unsigned int start_index, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_tags /* OUT */) const + { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } + + inline const FeatureParams &get_feature_params (void) const + { return this+featureParams; } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record<Feature>::sanitize_closure_t *closure = NULL) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) + return_trace (false); + + /* Some earlier versions of Adobe tools calculated the offset of the + * FeatureParams subtable from the beginning of the FeatureList table! + * + * If sanitizing "failed" for the FeatureParams subtable, try it with the + * alternative location. We would know sanitize "failed" if old value + * of the offset was non-zero, but it's zeroed now. + * + * Only do this for the 'size' feature, since at the time of the faulty + * Adobe tools, only the 'size' feature had FeatureParams defined. + */ + + OffsetTo<FeatureParams> orig_offset = featureParams; + if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) + return_trace (false); + + if (likely (orig_offset.is_null ())) + return_trace (true); + + if (featureParams == 0 && closure && + closure->tag == HB_TAG ('s','i','z','e') && + closure->list_base && closure->list_base < this) + { + unsigned int new_offset_int = (unsigned int) orig_offset - + (((char *) this) - ((char *) closure->list_base)); + + OffsetTo<FeatureParams> new_offset; + /* Check that it did not overflow. */ + new_offset.set (new_offset_int); + if (new_offset == new_offset_int && + c->try_set (&featureParams, new_offset) && + !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) + return_trace (false); + + if (c->edit_count > 1) + c->edit_count--; /* This was a "legitimate" edit; don't contribute to error count. */ + } + + return_trace (true); + } + + OffsetTo<FeatureParams> + featureParams; /* Offset to Feature Parameters table (if one + * has been defined for the feature), relative + * to the beginning of the Feature Table; = Null + * if not required */ + IndexArray lookupIndex; /* Array of LookupList indices */ + public: + DEFINE_SIZE_ARRAY (4, lookupIndex); +}; + +typedef RecordListOf<Feature> FeatureList; + + +struct LookupFlag : USHORT +{ + enum Flags { + RightToLeft = 0x0001u, + IgnoreBaseGlyphs = 0x0002u, + IgnoreLigatures = 0x0004u, + IgnoreMarks = 0x0008u, + IgnoreFlags = 0x000Eu, + UseMarkFilteringSet = 0x0010u, + Reserved = 0x00E0u, + MarkAttachmentType = 0xFF00u + }; + public: + DEFINE_SIZE_STATIC (2); +}; + +} /* namespace OT */ +/* This has to be outside the namespace. */ +HB_MARK_AS_FLAG_T (OT::LookupFlag::Flags); +namespace OT { + +struct Lookup +{ + inline unsigned int get_subtable_count (void) const { return subTable.len; } + + template <typename SubTableType> + inline const SubTableType& get_subtable (unsigned int i) const + { return this+CastR<OffsetArrayOf<SubTableType> > (subTable)[i]; } + + template <typename SubTableType> + inline const OffsetArrayOf<SubTableType>& get_subtables (void) const + { return CastR<OffsetArrayOf<SubTableType> > (subTable); } + template <typename SubTableType> + inline OffsetArrayOf<SubTableType>& get_subtables (void) + { return CastR<OffsetArrayOf<SubTableType> > (subTable); } + + inline unsigned int get_type (void) const { return lookupType; } + + /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and + * higher 16-bit is mark-filtering-set if the lookup uses one. + * Not to be confused with glyph_props which is very similar. */ + inline uint32_t get_props (void) const + { + unsigned int flag = lookupFlag; + if (unlikely (flag & LookupFlag::UseMarkFilteringSet)) + { + const USHORT &markFilteringSet = StructAfter<USHORT> (subTable); + flag += (markFilteringSet << 16); + } + return flag; + } + + template <typename SubTableType, typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + unsigned int lookup_type = get_type (); + TRACE_DISPATCH (this, lookup_type); + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) { + typename context_t::return_t r = get_subtable<SubTableType> (i).dispatch (c, lookup_type); + if (c->stop_sublookup_iteration (r)) + return_trace (r); + } + return_trace (c->default_return_value ()); + } + + inline bool serialize (hb_serialize_context_t *c, + unsigned int lookup_type, + uint32_t lookup_props, + unsigned int num_subtables) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + lookupType.set (lookup_type); + lookupFlag.set (lookup_props & 0xFFFFu); + if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + USHORT &markFilteringSet = StructAfter<USHORT> (subTable); + markFilteringSet.set (lookup_props >> 16); + } + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Real sanitize of the subtables is done by GSUB/GPOS/... */ + if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + const USHORT &markFilteringSet = StructAfter<USHORT> (subTable); + if (!markFilteringSet.sanitize (c)) return_trace (false); + } + return_trace (true); + } + + private: + USHORT lookupType; /* Different enumerations for GSUB and GPOS */ + USHORT lookupFlag; /* Lookup qualifiers */ + ArrayOf<Offset<> > + subTable; /* Array of SubTables */ + USHORT markFilteringSetX[VAR]; /* Index (base 0) into GDEF mark glyph sets + * structure. This field is only present if bit + * UseMarkFilteringSet of lookup flags is set. */ + public: + DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX); +}; + +typedef OffsetListOf<Lookup> LookupList; + + +/* + * Coverage Table + */ + +struct CoverageFormat1 +{ + friend struct Coverage; + + private: + inline unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + int i = glyphArray.bsearch (glyph_id); + ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED); + return i; + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + glyphArray.len.set (num_glyphs); + if (unlikely (!c->extend (glyphArray))) return_trace (false); + for (unsigned int i = 0; i < num_glyphs; i++) + glyphArray[i] = glyphs[i]; + glyphs.advance (num_glyphs); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphArray.sanitize (c)); + } + + inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { + return glyphs->has (glyphArray[index]); + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const { + unsigned int count = glyphArray.len; + for (unsigned int i = 0; i < count; i++) + glyphs->add (glyphArray[i]); + } + + public: + /* Older compilers need this to be public. */ + 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 hb_codepoint_t get_glyph (void) { return c->glyphArray[i]; } + inline unsigned int get_coverage (void) { return i; } + + private: + const struct CoverageFormat1 *c; + unsigned int i; + }; + private: + + protected: + USHORT coverageFormat; /* Format identifier--format = 1 */ + SortedArrayOf<GlyphID> + glyphArray; /* Array of GlyphIDs--in numerical order */ + public: + DEFINE_SIZE_ARRAY (4, glyphArray); +}; + +struct CoverageFormat2 +{ + friend struct Coverage; + + private: + inline unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + int i = rangeRecord.bsearch (glyph_id); + if (i != -1) { + const RangeRecord &range = rangeRecord[i]; + return (unsigned int) range.value + (glyph_id - range.start); + } + return NOT_COVERED; + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + + if (unlikely (!num_glyphs)) + { + rangeRecord.len.set (0); + return_trace (true); + } + + unsigned int num_ranges = 1; + for (unsigned int i = 1; i < num_glyphs; i++) + if (glyphs[i - 1] + 1 != glyphs[i]) + num_ranges++; + rangeRecord.len.set (num_ranges); + if (unlikely (!c->extend (rangeRecord))) return_trace (false); + + unsigned int range = 0; + rangeRecord[range].start = glyphs[0]; + rangeRecord[range].value.set (0); + for (unsigned int i = 1; i < num_glyphs; i++) + if (glyphs[i - 1] + 1 != glyphs[i]) { + range++; + rangeRecord[range].start = glyphs[i]; + rangeRecord[range].value.set (i); + rangeRecord[range].end = glyphs[i]; + } else { + rangeRecord[range].end = glyphs[i]; + } + glyphs.advance (num_glyphs); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (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; + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const { + unsigned int count = rangeRecord.len; + for (unsigned int i = 0; i < count; i++) + rangeRecord[i].add_coverage (glyphs); + } + + public: + /* Older compilers need this to be public. */ + 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) + { + if (j >= c->rangeRecord[i].end) + { + i++; + if (more ()) + { + j = c->rangeRecord[i].start; + coverage = c->rangeRecord[i].value; + } + return; + } + coverage++; + j++; + } + inline hb_codepoint_t get_glyph (void) { return j; } + inline unsigned int get_coverage (void) { return coverage; } + + private: + const struct CoverageFormat2 *c; + unsigned int i, j, coverage; + }; + private: + + protected: + USHORT coverageFormat; /* Format identifier--format = 2 */ + SortedArrayOf<RangeRecord> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID. rangeCount entries + * long */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); +}; + +struct Coverage +{ + inline unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_coverage(glyph_id); + case 2: return u.format2.get_coverage(glyph_id); + default:return NOT_COVERED; + } + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + unsigned int num_ranges = 1; + for (unsigned int i = 1; i < num_glyphs; i++) + if (glyphs[i - 1] + 1 != glyphs[i]) + num_ranges++; + u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2); + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs)); + case 2: return_trace (u.format2.serialize (c, glyphs, num_glyphs)); + default:return_trace (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + default:return_trace (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.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); + default:return false; + } + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const { + switch (u.format) { + case 1: u.format1.add_coverage (glyphs); break; + case 2: u.format2.add_coverage (glyphs); break; + default: break; + } + } + + struct Iter { + Iter (void) : format (0) {}; + inline void init (const Coverage &c_) { + format = c_.u.format; + switch (format) { + case 1: u.format1.init (c_.u.format1); return; + case 2: u.format2.init (c_.u.format2); return; + default: return; + } + } + inline bool more (void) { + switch (format) { + case 1: return u.format1.more (); + case 2: return u.format2.more (); + default:return false; + } + } + inline void next (void) { + switch (format) { + case 1: u.format1.next (); break; + case 2: u.format2.next (); break; + default: break; + } + } + inline hb_codepoint_t get_glyph (void) { + switch (format) { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); + default:return 0; + } + } + inline unsigned int get_coverage (void) { + switch (format) { + case 1: return u.format1.get_coverage (); + case 2: return u.format2.get_coverage (); + default:return -1; + } + } + + private: + unsigned int format; + union { + CoverageFormat1::Iter format1; + CoverageFormat2::Iter format2; + } u; + }; + + protected: + union { + USHORT format; /* Format identifier */ + CoverageFormat1 format1; + CoverageFormat2 format2; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * Class Definition Table + */ + +struct ClassDefFormat1 +{ + friend struct ClassDef; + + private: + inline unsigned int get_class (hb_codepoint_t glyph_id) const + { + unsigned int i = (unsigned int) (glyph_id - startGlyph); + if (unlikely (i < classValue.len)) + return classValue[i]; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classValue.sanitize (c)); + } + + template <typename set_t> + inline void add_class (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->add (startGlyph + i); + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { + unsigned int count = classValue.len; + if (klass == 0) + { + /* Match if there's any glyph that is not listed! */ + hb_codepoint_t g = -1; + if (!hb_set_next (glyphs, &g)) + return false; + if (g < startGlyph) + return true; + g = startGlyph + count - 1; + if (hb_set_next (glyphs, &g)) + return true; + /* Fall through. */ + } + for (unsigned int i = 0; i < count; i++) + if (classValue[i] == klass && glyphs->has (startGlyph + i)) + return true; + return false; + } + + protected: + USHORT classFormat; /* Format identifier--format = 1 */ + GlyphID startGlyph; /* First GlyphID of the classValueArray */ + ArrayOf<USHORT> + classValue; /* Array of Class Values--one per GlyphID */ + public: + DEFINE_SIZE_ARRAY (6, classValue); +}; + +struct ClassDefFormat2 +{ + friend struct ClassDef; + + private: + inline unsigned int get_class (hb_codepoint_t glyph_id) const + { + int i = rangeRecord.bsearch (glyph_id); + if (unlikely (i != -1)) + return rangeRecord[i].value; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + template <typename set_t> + inline void add_class (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].add_coverage (glyphs); + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { + unsigned int count = rangeRecord.len; + if (klass == 0) + { + /* Match if there's any glyph that is not listed! */ + hb_codepoint_t g = (hb_codepoint_t) -1; + for (unsigned int i = 0; i < count; i++) + { + if (!hb_set_next (glyphs, &g)) + break; + if (g < rangeRecord[i].start) + return true; + g = rangeRecord[i].end; + } + if (g != (hb_codepoint_t) -1 && hb_set_next (glyphs, &g)) + return true; + /* Fall through. */ + } + for (unsigned int i = 0; i < count; i++) + if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs)) + return true; + return false; + } + + protected: + USHORT classFormat; /* Format identifier--format = 2 */ + SortedArrayOf<RangeRecord> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); +}; + +struct ClassDef +{ + inline unsigned int get_class (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_class(glyph_id); + case 2: return u.format2.get_class(glyph_id); + default:return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + default:return_trace (true); + } + } + + inline void add_class (hb_set_t *glyphs, unsigned int klass) const { + switch (u.format) { + case 1: u.format1.add_class (glyphs, klass); return; + case 2: u.format2.add_class (glyphs, klass); return; + default:return; + } + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { + switch (u.format) { + case 1: return u.format1.intersects_class (glyphs, klass); + case 2: return u.format2.intersects_class (glyphs, klass); + default:return false; + } + } + + protected: + union { + USHORT format; /* Format identifier */ + ClassDefFormat1 format1; + ClassDefFormat2 format2; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * Item Variation Store + */ + +struct VarRegionAxis +{ + inline float evaluate (int coord) const + { + int start = startCoord, peak = peakCoord, end = endCoord; + + /* TODO Move these to sanitize(). */ + if (unlikely (start > peak || peak > end)) + return 1.; + if (unlikely (start < 0 && end > 0 && peak != 0)) + return 1.; + + if (peak == 0 || coord == peak) + return 1.; + + if (coord <= start || end <= coord) + return 0.; + + /* Interpolate */ + if (coord < peak) + return float (coord - start) / (peak - start); + else + return float (end - coord) / (end - peak); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + /* TODO Handle invalid start/peak/end configs, so we don't + * have to do that at runtime. */ + } + + public: + F2DOT14 startCoord; + F2DOT14 peakCoord; + F2DOT14 endCoord; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct VarRegionList +{ + inline float evaluate (unsigned int region_index, + int *coords, unsigned int coord_len) const + { + if (unlikely (region_index >= regionCount)) + return 0.; + + const VarRegionAxis *axes = axesZ + (region_index * axisCount); + + float v = 1.; + unsigned int count = MIN (coord_len, (unsigned int) axisCount); + for (unsigned int i = 0; i < count; i++) + { + float factor = axes[i].evaluate (coords[i]); + if (factor == 0.) + return 0.; + v *= factor; + } + return v; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_array (axesZ, axesZ[0].static_size, + (unsigned int) axisCount * (unsigned int) regionCount)); + } + + protected: + USHORT axisCount; + USHORT regionCount; + VarRegionAxis axesZ[VAR]; + public: + DEFINE_SIZE_ARRAY (4, axesZ); +}; + +struct VarData +{ + inline unsigned int get_row_size (void) const + { return shortCount + regionIndices.len; } + + inline unsigned int get_size (void) const + { return itemCount * get_row_size (); } + + inline float get_delta (unsigned int inner, + int *coords, unsigned int coord_count, + const VarRegionList ®ions) const + { + if (unlikely (inner >= itemCount)) + return 0.; + + unsigned int count = regionIndices.len; + unsigned int scount = shortCount; + + const BYTE *bytes = &StructAfter<BYTE> (regionIndices); + const BYTE *row = bytes + inner * (scount + count); + + float delta = 0.; + unsigned int i = 0; + + const SHORT *scursor = reinterpret_cast<const SHORT *> (row); + for (; i < scount; i++) + { + float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count); + delta += scalar * *scursor++; + } + const INT8 *bcursor = reinterpret_cast<const INT8 *> (scursor); + for (; i < count; i++) + { + float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count); + delta += scalar * *bcursor++; + } + + return delta; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + regionIndices.sanitize(c) && + shortCount <= regionIndices.len && + c->check_array (&StructAfter<BYTE> (regionIndices), + get_row_size (), itemCount)); + } + + protected: + USHORT itemCount; + USHORT shortCount; + ArrayOf<USHORT> regionIndices; + BYTE bytesX[VAR]; + public: + DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX); +}; + +struct VariationStore +{ + inline float get_delta (unsigned int outer, unsigned int inner, + int *coords, unsigned int coord_count) const + { + if (unlikely (outer >= dataSets.len)) + return 0.; + + return (this+dataSets[outer]).get_delta (inner, + coords, coord_count, + this+regions); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + format == 1 && + regions.sanitize (c, this) && + dataSets.sanitize (c, this)); + } + + protected: + USHORT format; + OffsetTo<VarRegionList, ULONG> regions; + OffsetArrayOf<VarData, ULONG> dataSets; + public: + DEFINE_SIZE_ARRAY (8, dataSets); +}; + +/* + * Feature Variations + */ + +struct ConditionFormat1 +{ + friend struct Condition; + + private: + inline bool evaluate (const int *coords, unsigned int coord_len) const + { + int coord = axisIndex < coord_len ? coords[axisIndex] : 0; + return filterRangeMinValue <= coord && coord <= filterRangeMaxValue; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + USHORT axisIndex; + F2DOT14 filterRangeMinValue; + F2DOT14 filterRangeMaxValue; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct Condition +{ + inline bool evaluate (const int *coords, unsigned int coord_len) const + { + switch (u.format) { + case 1: return u.format1.evaluate (coords, coord_len); + default:return false; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + ConditionFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct ConditionSet +{ + inline bool evaluate (const int *coords, unsigned int coord_len) const + { + unsigned int count = conditions.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+conditions.array[i]).evaluate (coords, coord_len)) + return false; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, this)); + } + + protected: + OffsetArrayOf<Condition, ULONG> conditions; + public: + DEFINE_SIZE_ARRAY (2, conditions); +}; + +struct FeatureTableSubstitutionRecord +{ + friend struct FeatureTableSubstitution; + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && feature.sanitize (c, base)); + } + + protected: + USHORT featureIndex; + OffsetTo<Feature, ULONG> feature; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct FeatureTableSubstitution +{ + inline const Feature *find_substitute (unsigned int feature_index) const + { + unsigned int count = substitutions.len; + for (unsigned int i = 0; i < count; i++) + { + const FeatureTableSubstitutionRecord &record = substitutions.array[i]; + if (record.featureIndex == feature_index) + return &(this+record.feature); + } + return NULL; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + substitutions.sanitize (c, this)); + } + + protected: + FixedVersion<> version; /* Version--0x00010000u */ + ArrayOf<FeatureTableSubstitutionRecord> + substitutions; + public: + DEFINE_SIZE_ARRAY (6, substitutions); +}; + +struct FeatureVariationRecord +{ + friend struct FeatureVariations; + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, base) && + substitutions.sanitize (c, base)); + } + + protected: + OffsetTo<ConditionSet, ULONG> + conditions; + OffsetTo<FeatureTableSubstitution, ULONG> + substitutions; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct FeatureVariations +{ + static const unsigned int NOT_FOUND_INDEX = 0xFFFFFFFFu; + + inline bool find_index (const int *coords, unsigned int coord_len, + unsigned int *index) const + { + unsigned int count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + const FeatureVariationRecord &record = varRecords.array[i]; + if ((this+record.conditions).evaluate (coords, coord_len)) + { + *index = i; + return true; + } + } + *index = NOT_FOUND_INDEX; + return false; + } + + inline const Feature *find_substitute (unsigned int variations_index, + unsigned int feature_index) const + { + const FeatureVariationRecord &record = varRecords[variations_index]; + return (this+record.substitutions).find_substitute (feature_index); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + varRecords.sanitize (c, this)); + } + + protected: + FixedVersion<> version; /* Version--0x00010000u */ + ArrayOf<FeatureVariationRecord, ULONG> + varRecords; + public: + DEFINE_SIZE_ARRAY (8, varRecords); +}; + + +/* + * Device Tables + */ + +struct HintingDevice +{ + friend struct Device; + + private: + + inline hb_position_t get_x_delta (hb_font_t *font) const + { return get_delta (font->x_ppem, font->x_scale); } + + inline hb_position_t get_y_delta (hb_font_t *font) const + { return get_delta (font->y_ppem, font->y_scale); } + + inline unsigned int get_size (void) const + { + unsigned int f = deltaFormat; + if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size; + return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f))); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_range (this, this->get_size ())); + } + + private: + + inline int get_delta (unsigned int ppem, int scale) const + { + if (!ppem) return 0; + + int pixels = get_delta_pixels (ppem); + + if (!pixels) return 0; + + return (int) (pixels * (int64_t) scale / ppem); + } + inline int get_delta_pixels (unsigned int ppem_size) const + { + unsigned int f = deltaFormat; + if (unlikely (f < 1 || f > 3)) + return 0; + + if (ppem_size < startSize || ppem_size > endSize) + return 0; + + unsigned int s = ppem_size - startSize; + + unsigned int byte = deltaValue[s >> (4 - f)]; + unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); + unsigned int mask = (0xFFFFu >> (16 - (1 << f))); + + int delta = bits & mask; + + if ((unsigned int) delta >= ((mask + 1) >> 1)) + delta -= mask + 1; + + return delta; + } + + protected: + USHORT startSize; /* Smallest size to correct--in ppem */ + USHORT endSize; /* Largest size to correct--in ppem */ + USHORT deltaFormat; /* Format of DeltaValue array data: 1, 2, or 3 + * 1 Signed 2-bit value, 8 values per uint16 + * 2 Signed 4-bit value, 4 values per uint16 + * 3 Signed 8-bit value, 2 values per uint16 + */ + USHORT deltaValue[VAR]; /* Array of compressed data */ + public: + DEFINE_SIZE_ARRAY (6, deltaValue); +}; + +struct VariationDevice +{ + friend struct Device; + + private: + + inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const + { return font->em_scalef_x (get_delta (font, store)); } + + inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const + { return font->em_scalef_y (get_delta (font, store)); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + + inline float get_delta (hb_font_t *font, const VariationStore &store) const + { + return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords); + } + + protected: + USHORT outerIndex; + USHORT innerIndex; + USHORT deltaFormat; /* Format identifier for this table: 0x0x8000 */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DeviceHeader +{ + protected: + USHORT reserved1; + USHORT reserved2; + public: + USHORT format; /* Format identifier */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct Device +{ + inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const + { + switch (u.b.format) + { + case 1: case 2: case 3: + return u.hinting.get_x_delta (font); + case 0x8000: + return u.variation.get_x_delta (font, store); + default: + return 0; + } + } + inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const + { + switch (u.b.format) + { + case 1: case 2: case 3: + return u.hinting.get_y_delta (font); + case 0x8000: + return u.variation.get_y_delta (font, store); + default: + return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.b.format.sanitize (c)) return_trace (false); + switch (u.b.format) { + case 1: case 2: case 3: + return_trace (u.hinting.sanitize (c)); + case 0x8000: + return_trace (u.variation.sanitize (c)); + default: + return_trace (true); + } + } + + protected: + union { + DeviceHeader b; + HintingDevice hinting; + VariationDevice variation; + } u; + public: + DEFINE_SIZE_UNION (6, b); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh new file mode 100644 index 000000000..b70cbb7a3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh @@ -0,0 +1,459 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#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" + + +namespace OT { + + +/* + * Attachment List Table + */ + +typedef ArrayOf<USHORT> AttachPoint; /* Array of contour point indices--in + * increasing numerical order */ + +struct AttachList +{ + inline unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (point_count) + *point_count = 0; + return 0; + } + + const AttachPoint &points = this+attachPoint[index]; + + if (point_count) { + const USHORT *array = points.sub_array (start_offset, point_count); + unsigned int count = *point_count; + for (unsigned int i = 0; i < count; i++) + point_array[i] = array[i]; + } + + return points.len; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); + } + + protected: + OffsetTo<Coverage> + coverage; /* Offset to Coverage table -- from + * beginning of AttachList table */ + OffsetArrayOf<AttachPoint> + attachPoint; /* Array of AttachPoint tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, attachPoint); +}; + +/* + * Ligature Caret Table + */ + +struct CaretValueFormat1 +{ + friend struct CaretValue; + + private: + inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT caretValueFormat; /* Format identifier--format = 1 */ + SHORT coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat2 +{ + friend struct CaretValue; + + private: + inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const + { + hb_position_t x, y; + if (font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y)) + return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y; + else + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT caretValueFormat; /* Format identifier--format = 2 */ + USHORT caretValuePoint; /* Contour point index on glyph */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat3 +{ + friend struct CaretValue; + + inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, const VariationStore &var_store) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? + font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : + font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, this)); + } + + protected: + USHORT caretValueFormat; /* Format identifier--format = 3 */ + SHORT coordinate; /* X or Y value, in design units */ + OffsetTo<Device> + deviceTable; /* Offset to Device table for X or Y + * value--from beginning of CaretValue + * table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct CaretValue +{ + inline hb_position_t get_caret_value (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const + { + switch (u.format) { + case 1: return u.format1.get_caret_value (font, direction); + case 2: return u.format2.get_caret_value (font, direction, glyph_id); + case 3: return u.format3.get_caret_value (font, direction, var_store); + default:return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + CaretValueFormat1 format1; + CaretValueFormat2 format2; + CaretValueFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct LigGlyph +{ + inline unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + if (caret_count) { + const OffsetTo<CaretValue> *array = carets.sub_array (start_offset, caret_count); + unsigned int count = *caret_count; + for (unsigned int i = 0; i < count; i++) + caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store); + } + + return carets.len; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (carets.sanitize (c, this)); + } + + protected: + OffsetArrayOf<CaretValue> + carets; /* Offset array of CaretValue tables + * --from beginning of LigGlyph table + * --in increasing coordinate order */ + public: + DEFINE_SIZE_ARRAY (2, carets); +}; + +struct LigCaretList +{ + inline unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (caret_count) + *caret_count = 0; + return 0; + } + const LigGlyph &lig_glyph = this+ligGlyph[index]; + return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); + } + + protected: + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of LigCaretList table */ + OffsetArrayOf<LigGlyph> + ligGlyph; /* Array of LigGlyph tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, ligGlyph); +}; + + +struct MarkGlyphSetsFormat1 +{ + inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + ArrayOf<OffsetTo<Coverage, ULONG> > + coverage; /* Array of long offsets to mark set + * coverage tables */ + public: + DEFINE_SIZE_ARRAY (4, coverage); +}; + +struct MarkGlyphSets +{ + inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.covers (set_index, glyph_id); + default:return false; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + MarkGlyphSetsFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * GDEF -- The Glyph Definition Table + */ + +struct GDEF +{ + static const hb_tag_t tableTag = HB_OT_TAG_GDEF; + + enum GlyphClasses { + UnclassifiedGlyph = 0, + BaseGlyph = 1, + LigatureGlyph = 2, + MarkGlyph = 3, + ComponentGlyph = 4 + }; + + inline bool has_glyph_classes (void) const { return glyphClassDef != 0; } + inline unsigned int get_glyph_class (hb_codepoint_t glyph) const + { return (this+glyphClassDef).get_class (glyph); } + inline void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const + { (this+glyphClassDef).add_class (glyphs, klass); } + + inline bool has_mark_attachment_types (void) const { return markAttachClassDef != 0; } + inline unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const + { return (this+markAttachClassDef).get_class (glyph); } + + inline bool has_attach_points (void) const { return attachList != 0; } + inline unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { return (this+attachList).get_attach_points (glyph_id, start_offset, point_count, point_array); } + + inline bool has_lig_carets (void) const { return ligCaretList != 0; } + inline unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { return (this+ligCaretList).get_lig_carets (font, + direction, glyph_id, get_var_store(), + start_offset, caret_count, caret_array); } + + inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef != 0; } + inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef).covers (set_index, glyph_id); } + + inline bool has_var_store (void) const { return version.to_int () >= 0x00010003u && varStore != 0; } + inline const VariationStore &get_var_store (void) const + { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (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 () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) && + (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); + } + + /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing + * glyph class and other bits, and high 8-bit gthe mark attachment type (if any). + * Not to be confused with lookup_props which is very similar. */ + inline unsigned int get_glyph_props (hb_codepoint_t glyph) const + { + unsigned int klass = get_glyph_class (glyph); + + ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs); + ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures); + ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks); + + switch (klass) { + default: return 0; + case BaseGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH; + case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; + case MarkGlyph: + klass = get_mark_attachment_type (glyph); + return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8); + } + } + + + protected: + FixedVersion<>version; /* Version of the GDEF table--currently + * 0x00010003u */ + OffsetTo<ClassDef> + glyphClassDef; /* Offset to class definition table + * for glyph type--from beginning of + * GDEF header (may be Null) */ + OffsetTo<AttachList> + attachList; /* Offset to list of glyphs with + * attachment points--from beginning + * of GDEF header (may be Null) */ + OffsetTo<LigCaretList> + ligCaretList; /* Offset to list of positioning points + * for ligature carets--from beginning + * of GDEF header (may be Null) */ + OffsetTo<ClassDef> + markAttachClassDef; /* Offset to class definition table for + * mark attachment type--from beginning + * of GDEF header (may be Null) */ + OffsetTo<MarkGlyphSets> + markGlyphSetsDef; /* Offset to the table of mark set + * definitions--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010002. */ + OffsetTo<VariationStore, ULONG> + varStore; /* Offset to the table of Item Variation + * Store--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010003. */ + public: + DEFINE_SIZE_MIN (12); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh new file mode 100644 index 000000000..952fd60fe --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh @@ -0,0 +1,1654 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2010,2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_GPOS_TABLE_HH +#define HB_OT_LAYOUT_GPOS_TABLE_HH + +#include "hb-ot-layout-gsubgpos-private.hh" + + +namespace OT { + + +/* buffer **position** var allocations */ +#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ +#define attach_type() var.u8[2] /* attachment type */ +/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */ + +enum attach_type_t { + ATTACH_TYPE_NONE = 0X00, + + /* Each attachment should be either a mark or a cursive; can't be both. */ + ATTACH_TYPE_MARK = 0X01, + ATTACH_TYPE_CURSIVE = 0X02, +}; + + +/* Shared Tables: ValueRecord, Anchor Table, and MarkArray */ + +typedef USHORT Value; + +typedef Value ValueRecord[VAR]; + +struct ValueFormat : USHORT +{ + enum Flags { + xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */ + yPlacement = 0x0002u, /* Includes vertical adjustment for placement */ + xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */ + yAdvance = 0x0008u, /* Includes vertical adjustment for advance */ + xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */ + yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */ + xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */ + yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */ + ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */ + reserved = 0xF000u, /* For future use */ + + devices = 0x00F0u /* Mask for having any Device table */ + }; + +/* All fields are options. Only those available advance the value pointer. */ +#if 0 + SHORT xPlacement; /* Horizontal adjustment for + * placement--in design units */ + SHORT yPlacement; /* Vertical adjustment for + * placement--in design units */ + SHORT xAdvance; /* Horizontal adjustment for + * advance--in design units (only used + * for horizontal writing) */ + SHORT yAdvance; /* Vertical adjustment for advance--in + * design units (only used for vertical + * writing) */ + Offset xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset yAdvDevice; /* Offset to Device table for vertical + * advance--measured from beginning of + * PosTable (may be NULL) */ +#endif + + inline unsigned int get_len (void) const + { return _hb_popcount32 ((unsigned int) *this); } + inline unsigned int get_size (void) const + { return get_len () * Value::static_size; } + + void apply_value (hb_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const + { + unsigned int format = *this; + if (!format) return; + + hb_font_t *font = c->font; + hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction); + + if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++)); + if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++)); + if (format & xAdvance) { + if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values)); + values++; + } + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (format & yAdvance) { + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values)); + values++; + } + + if (!has_device ()) return; + + bool use_x_device = font->x_ppem || font->num_coords; + bool use_y_device = font->y_ppem || font->num_coords; + + if (!use_x_device && !use_y_device) return; + + const VariationStore &store = c->var_store; + + /* pixel -> fractional pixel */ + if (format & xPlaDevice) { + if (use_x_device) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font, store); + values++; + } + if (format & yPlaDevice) { + if (use_y_device) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font, store); + values++; + } + if (format & xAdvDevice) { + if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font, store); + values++; + } + if (format & yAdvDevice) { + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font, store); + values++; + } + } + + private: + inline bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + unsigned int format = *this; + + if (format & xPlacement) values++; + if (format & yPlacement) values++; + if (format & xAdvance) values++; + if (format & yAdvance) values++; + + if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + + return true; + } + + static inline OffsetTo<Device>& get_device (Value* value) + { return *CastP<OffsetTo<Device> > (value); } + static inline const OffsetTo<Device>& get_device (const Value* value) + { return *CastP<OffsetTo<Device> > (value); } + + static inline const SHORT& get_short (const Value* value) + { return *CastP<SHORT> (value); } + + public: + + inline bool has_device (void) const { + unsigned int format = *this; + return (format & devices) != 0; + } + + inline bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); + return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); + } + + inline bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const + { + TRACE_SANITIZE (this); + unsigned int len = get_len (); + + if (!c->check_array (values, get_size (), count)) return_trace (false); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += len; + } + + return_trace (true); + } + + /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ + inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const + { + TRACE_SANITIZE (this); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += stride; + } + + return_trace (true); + } +}; + + +struct AnchorFormat1 +{ + inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + hb_position_t *x, hb_position_t *y) const + { + hb_font_t *font = c->font; + *x = font->em_scale_x (xCoordinate); + *y = font->em_scale_y (yCoordinate); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + SHORT xCoordinate; /* Horizontal value--in design units */ + SHORT yCoordinate; /* Vertical value--in design units */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct AnchorFormat2 +{ + inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id, + hb_position_t *x, hb_position_t *y) const + { + hb_font_t *font = c->font; + unsigned int x_ppem = font->x_ppem; + unsigned int y_ppem = font->y_ppem; + hb_position_t cx, cy; + hb_bool_t ret; + + ret = (x_ppem || y_ppem) && + font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); + *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate); + *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT format; /* Format identifier--format = 2 */ + SHORT xCoordinate; /* Horizontal value--in design units */ + SHORT yCoordinate; /* Vertical value--in design units */ + USHORT anchorPoint; /* Index to glyph contour point */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct AnchorFormat3 +{ + inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + hb_position_t *x, hb_position_t *y) const + { + hb_font_t *font = c->font; + *x = font->em_scale_x (xCoordinate); + *y = font->em_scale_y (yCoordinate); + + if (font->x_ppem || font->num_coords) + *x += (this+xDeviceTable).get_x_delta (font, c->var_store); + if (font->y_ppem || font->num_coords) + *y += (this+yDeviceTable).get_y_delta (font, c->var_store); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 3 */ + SHORT xCoordinate; /* Horizontal value--in design units */ + SHORT yCoordinate; /* Vertical value--in design units */ + OffsetTo<Device> + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + OffsetTo<Device> + yDeviceTable; /* Offset to Device table for Y + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct Anchor +{ + inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id, + hb_position_t *x, hb_position_t *y) const + { + *x = *y = 0; + switch (u.format) { + case 1: u.format1.get_anchor (c, glyph_id, x, y); return; + case 2: u.format2.get_anchor (c, glyph_id, x, y); return; + case 3: u.format3.get_anchor (c, glyph_id, x, y); return; + default: return; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + AnchorFormat1 format1; + AnchorFormat2 format2; + AnchorFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct AnchorMatrix +{ + inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const { + *found = false; + if (unlikely (row >= rows || col >= cols)) return Null(Anchor); + *found = !matrixZ[row * cols + col].is_null (); + return this+matrixZ[row * cols + col]; + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + if (unlikely (_hb_unsigned_int_mul_overflows (rows, cols))) return_trace (false); + unsigned int count = rows * cols; + if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (!matrixZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + USHORT rows; /* Number of rows */ + protected: + OffsetTo<Anchor> + matrixZ[VAR]; /* Matrix of offsets to Anchor tables-- + * from beginning of AnchorMatrix table */ + public: + DEFINE_SIZE_ARRAY (2, matrixZ); +}; + + +struct MarkRecord +{ + friend struct MarkArray; + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); + } + + protected: + USHORT klass; /* Class defined for this mark */ + OffsetTo<Anchor> + markAnchor; /* Offset to Anchor table--from + * beginning of MarkArray table */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */ +{ + inline bool apply (hb_apply_context_t *c, + unsigned int mark_index, unsigned int glyph_index, + const AnchorMatrix &anchors, unsigned int class_count, + unsigned int glyph_pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index); + unsigned int mark_class = record.klass; + + const Anchor& mark_anchor = this + record.markAnchor; + bool found; + const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found); + /* If this subtable doesn't have an anchor for this base and this class, + * return false such that the subsequent subtables have a chance at it. */ + if (unlikely (!found)) return_trace (false); + + hb_position_t mark_x, mark_y, base_x, base_y; + + mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + + hb_glyph_position_t &o = buffer->cur_pos(); + o.x_offset = base_x - mark_x; + o.y_offset = base_y - mark_y; + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + buffer->idx++; + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ArrayOf<MarkRecord>::sanitize (c, this)); + } +}; + + +/* Lookups */ + +struct SinglePosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + + buffer->idx++; + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_value (c, this, values)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + ValueRecord values; /* Defines positioning + * value(s)--applied to all glyphs in + * the Coverage table */ + public: + DEFINE_SIZE_ARRAY (6, values); +}; + +struct SinglePosFormat2 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (likely (index >= valueCount)) return_trace (false); + + valueFormat.apply_value (c, this, + &values[index * valueFormat.get_len ()], + buffer->cur_pos()); + + buffer->idx++; + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_values (c, this, values, valueCount)); + } + + protected: + USHORT format; /* Format identifier--format = 2 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + USHORT valueCount; /* Number of ValueRecords */ + ValueRecord values; /* Array of ValueRecords--positioning + * values applied to glyphs */ + public: + DEFINE_SIZE_ARRAY (8, values); +}; + +struct SinglePos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + case 2: return_trace (c->dispatch (u.format2)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + SinglePosFormat1 format1; + SinglePosFormat2 format2; + } u; +}; + + +struct PairValueRecord +{ + friend struct PairSet; + + protected: + GlyphID secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (2, values); +}; + +struct PairSet +{ + friend struct PairPosFormat1; + + inline void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = USHORT::static_size * (1 + len1 + len2); + + const PairValueRecord *record = CastP<PairValueRecord> (arrayZ); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + c->input->add (record->secondGlyph); + record = &StructAtOffset<PairValueRecord> (record, record_size); + } + } + + inline bool apply (hb_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = USHORT::static_size * (1 + len1 + len2); + + const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ); + unsigned int count = len; + + /* Hand-coded bsearch. */ + if (unlikely (!count)) + return_trace (false); + hb_codepoint_t x = buffer->info[pos].codepoint; + int min = 0, max = (int) count - 1; + while (min <= max) + { + int mid = (min + max) / 2; + const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid); + hb_codepoint_t mid_x = record->secondGlyph; + if (x < mid_x) + max = mid - 1; + else if (x > mid_x) + min = mid + 1; + else + { + valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + if (len2) + pos++; + buffer->idx = pos; + return_trace (true); + } + } + + return_trace (false); + } + + struct sanitize_closure_t { + const void *base; + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* 1 + len1 + len2 */ + }; + + inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = CastP<PairValueRecord> (arrayZ); + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); + } + + protected: + USHORT len; /* Number of PairValueRecords */ + USHORT arrayZ[VAR]; /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_ARRAY (2, arrayZ); +}; + +struct PairPosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + unsigned int count = pairSet.len; + for (unsigned int i = 0; i < count; i++) + (this+pairSet[i]).collect_glyphs (c, valueFormat); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + if (!skippy_iter.next ()) return_trace (false); + + return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + if (!c->check_struct (this)) return_trace (false); + + unsigned int len1 = valueFormat[0].get_len (); + unsigned int len2 = valueFormat[1].get_len (); + PairSet::sanitize_closure_t closure = { + this, + valueFormat, + len1, + 1 + len1 + len2 + }; + + return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat[2]; /* [0] Defines the types of data in + * ValueRecord1--for the first glyph + * in the pair--may be zero (0) */ + /* [1] Defines the types of data in + * ValueRecord2--for the second glyph + * in the pair--may be zero (0) */ + OffsetArrayOf<PairSet> + pairSet; /* Array of PairSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (10, pairSet); +}; + +struct PairPosFormat2 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + + unsigned int count1 = class1Count; + const ClassDef &klass1 = this+classDef1; + for (unsigned int i = 0; i < count1; i++) + klass1.add_class (c->input, i); + + unsigned int count2 = class2Count; + const ClassDef &klass2 = this+classDef2; + for (unsigned int i = 0; i < count2; i++) + klass2.add_class (c->input, i); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + if (!skippy_iter.next ()) return_trace (false); + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int record_len = len1 + len2; + + unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); + unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false); + + const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; + valueFormat1.apply_value (c, this, v, buffer->cur_pos()); + valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + + buffer->idx = skippy_iter.idx; + if (len2) + buffer->idx++; + + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && coverage.sanitize (c, this) + && classDef1.sanitize (c, this) + && classDef2.sanitize (c, this))) return_trace (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_trace (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)); + } + + protected: + USHORT format; /* Format identifier--format = 2 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat1; /* ValueRecord definition--for the + * first glyph of the pair--may be zero + * (0) */ + ValueFormat valueFormat2; /* ValueRecord definition--for the + * second glyph of the pair--may be + * zero (0) */ + OffsetTo<ClassDef> + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + OffsetTo<ClassDef> + classDef2; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the second glyph of the pair */ + USHORT class1Count; /* Number of classes in ClassDef1 + * table--includes Class0 */ + USHORT class2Count; /* Number of classes in ClassDef2 + * table--includes Class0 */ + ValueRecord values; /* Matrix of value pairs: + * class1-major, class2-minor, + * Each entry has value1 and value2 */ + public: + DEFINE_SIZE_ARRAY (16, values); +}; + +struct PairPos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + case 2: return_trace (c->dispatch (u.format2)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + PairPosFormat1 format1; + PairPosFormat2 format2; + } u; +}; + + +struct EntryExitRecord +{ + friend struct CursivePosFormat1; + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); + } + + protected: + OffsetTo<Anchor> + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + OffsetTo<Anchor> + exitAnchor; /* Offset to ExitAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent); + +struct CursivePosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; + if (!this_record.exitAnchor) return_trace (false); + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + if (!skippy_iter.next ()) return_trace (false); + + const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; + if (!next_record.entryAnchor) return_trace (false); + + unsigned int i = 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, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); + + hb_glyph_position_t *pos = buffer->pos; + + hb_position_t d; + /* Main-direction adjustment */ + switch (c->direction) { + case HB_DIRECTION_LTR: + pos[i].x_advance = exit_x + pos[i].x_offset; + + d = entry_x + pos[j].x_offset; + pos[j].x_advance -= d; + pos[j].x_offset -= d; + break; + case HB_DIRECTION_RTL: + d = exit_x + pos[i].x_offset; + pos[i].x_advance -= d; + pos[i].x_offset -= d; + + pos[j].x_advance = entry_x + pos[j].x_offset; + break; + case HB_DIRECTION_TTB: + pos[i].y_advance = exit_y + pos[i].y_offset; + + d = entry_y + pos[j].y_offset; + pos[j].y_advance -= d; + pos[j].y_offset -= d; + break; + case HB_DIRECTION_BTT: + d = exit_y + pos[i].y_offset; + pos[i].y_advance -= d; + pos[i].y_offset -= d; + + pos[j].y_advance = entry_y; + break; + case HB_DIRECTION_INVALID: + default: + break; + } + + /* Cross-direction adjustment */ + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabinc. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = entry_x - exit_x; + hb_position_t y_offset = entry_y - exit_y; + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; + } + + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; + pos[child].attach_chain() = (int) parent - (int) child; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + + buffer->idx = j; + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ArrayOf<EntryExitRecord> + entryExitRecord; /* Array of EntryExit records--in + * Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (6, entryExitRecord); +}; + +struct CursivePos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + CursivePosFormat1 format1; + } u; +}; + + +typedef AnchorMatrix BaseArray; /* base-major-- + * in order of BaseCoverage Index--, + * mark-minor-- + * ordered by class--zero-based. */ + +struct MarkBasePosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+markCoverage).add_coverage (c->input); + (this+baseCoverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+markCoverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + do { + if (!skippy_iter.prev ()) return_trace (false); + /* We only want to attach to the first of a MultipleSubst sequence. Reject others. */ + if (0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx])) break; + skippy_iter.reject (); + } while (1); + + /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); + if (base_index == NOT_COVERED) return_trace (false); + + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + baseArray.sanitize (c, this, (unsigned int) classCount)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + OffsetTo<Coverage> + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + USHORT classCount; /* Number of classes defined for marks */ + OffsetTo<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + OffsetTo<BaseArray> + baseArray; /* Offset to BaseArray table--from + * beginning of MarkBasePos subtable */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct MarkBasePos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + MarkBasePosFormat1 format1; + } u; +}; + + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +typedef OffsetListOf<LigatureAttach> LigatureArray; + /* Array of LigatureAttach + * tables ordered by + * LigatureCoverage Index */ + +struct MarkLigPosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+markCoverage).add_coverage (c->input); + (this+ligatureCoverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+markCoverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + if (!skippy_iter.prev ()) return_trace (false); + + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int j = skippy_iter.idx; + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); + if (lig_index == NOT_COVERED) return_trace (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_trace (false); + + /* 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. */ + unsigned int comp_index; + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (lig_id && lig_id == mark_id && mark_comp > 0) + comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + else + comp_index = comp_count - 1; + + return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + ligatureArray.sanitize (c, this, (unsigned int) classCount)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + OffsetTo<Coverage> + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + USHORT classCount; /* Number of defined mark classes */ + OffsetTo<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + OffsetTo<LigatureArray> + ligatureArray; /* Offset to LigatureArray table--from + * beginning of MarkLigPos subtable */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct MarkLigPos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + MarkLigPosFormat1 format1; + } u; +}; + + +typedef AnchorMatrix Mark2Array; /* mark2-major-- + * in order of Mark2Coverage Index--, + * mark1-minor-- + * ordered by class--zero-based. */ + +struct MarkMarkPosFormat1 +{ + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+mark1Coverage).add_coverage (c->input); + (this+mark2Coverage).add_coverage (c->input); + } + + inline const Coverage &get_coverage (void) const + { + return this+mark1Coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return_trace (false); + + /* now we search backwards for a suitable mark glyph until a non-mark glyph */ + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags); + if (!skippy_iter.prev ()) return_trace (false); + + if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int j = skippy_iter.idx; + + unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); + unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); + + if (likely (id1 == id2)) { + if (id1 == 0) /* Marks belonging to the same base. */ + goto good; + else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ + goto good; + } else { + /* If ligature ids don't match, it may be the case that one of the marks + * itself is a ligature. In which case match. */ + if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) + goto good; + } + + /* Didn't match. */ + return_trace (false); + + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); + if (mark2_index == NOT_COVERED) return_trace (false); + + return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && + mark1Array.sanitize (c, this) && + mark2Array.sanitize (c, this, (unsigned int) classCount)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + OffsetTo<Coverage> + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + USHORT classCount; /* Number of defined mark classes */ + OffsetTo<MarkArray> + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + OffsetTo<Mark2Array> + mark2Array; /* Offset to Mark2Array table--from + * beginning of MarkMarkPos subtable */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct MarkMarkPos +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + MarkMarkPosFormat1 format1; + } u; +}; + + +struct ContextPos : Context {}; + +struct ChainContextPos : ChainContext {}; + +struct ExtensionPos : Extension<ExtensionPos> +{ + typedef struct PosLookupSubTable LookupSubTable; +}; + + + +/* + * PosLookup + */ + + +struct PosLookupSubTable +{ + friend struct PosLookup; + + enum Type { + Single = 1, + Pair = 2, + Cursive = 3, + MarkBase = 4, + MarkLig = 5, + MarkMark = 6, + Context = 7, + ChainContext = 8, + Extension = 9 + }; + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + { + TRACE_DISPATCH (this, lookup_type); + if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ()); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c)); + case Pair: return_trace (u.pair.dispatch (c)); + case Cursive: return_trace (u.cursive.dispatch (c)); + case MarkBase: return_trace (u.markBase.dispatch (c)); + case MarkLig: return_trace (u.markLig.dispatch (c)); + case MarkMark: return_trace (u.markMark.dispatch (c)); + case Context: return_trace (u.context.dispatch (c)); + case ChainContext: return_trace (u.chainContext.dispatch (c)); + case Extension: return_trace (u.extension.dispatch (c)); + default: return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT sub_format; + SinglePos single; + PairPos pair; + CursivePos cursive; + MarkBasePos markBase; + MarkLigPos markLig; + MarkMarkPos markMark; + ContextPos context; + ChainContextPos chainContext; + ExtensionPos extension; + } u; + public: + DEFINE_SIZE_UNION (2, sub_format); +}; + + +struct PosLookup : Lookup +{ + inline const PosLookupSubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable<PosLookupSubTable> (i); } + + inline bool is_reverse (void) const + { + return false; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + return_trace (dispatch (c)); + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const + { + hb_add_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } + + static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index); + + template <typename context_t> + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { return Lookup::dispatch<PosLookupSubTable> (c); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!Lookup::sanitize (c))) return_trace (false); + return_trace (dispatch (c)); + } +}; + +typedef OffsetListOf<PosLookup> PosLookupList; + +/* + * GPOS -- The Glyph Positioning Table + */ + +struct GPOS : GSUBGPOS +{ + static const hb_tag_t tableTag = HB_OT_TAG_GPOS; + + inline const PosLookup& get_lookup (unsigned int i) const + { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); } + + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false); + const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList); + return_trace (list.sanitize (c, this)); + } +}; + + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) +{ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].attach_chain() = -chain; + pos[j].attach_type() = type; +} +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction) +{ + /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate + * offset of glyph they are attached to. */ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain)) + return; + + unsigned int j = (int) i + chain; + + pos[i].attach_chain() = 0; + + propagate_attachment_offsets (pos, j, direction); + + assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE)); + + if (type & ATTACH_TYPE_CURSIVE) + { + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; + } + else /*if (type & ATTACH_TYPE_MARK)*/ + { + pos[i].x_offset += pos[j].x_offset; + pos[i].y_offset += pos[j].y_offset; + + assert (j < i); + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } +} + +void +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; +} + +void +GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + //_hb_buffer_assert_gsubgpos_vars (buffer); +} + +void +GPOS::position_finish_offsets (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int len; + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); + hb_direction_t direction = buffer->props.direction; + + /* Handle attachments */ + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) + for (unsigned int i = 0; i < len; i++) + propagate_attachment_offsets (pos, i, direction); +} + + +/* Out-of-class implementation for methods recursing */ + +template <typename context_t> +/*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +{ + const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); + const PosLookup &l = gpos.get_lookup (lookup_index); + return l.dispatch (c); +} + +/*static*/ inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) +{ + const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); + const PosLookup &l = gpos.get_lookup (lookup_index); + unsigned int saved_lookup_props = c->lookup_props; + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); + bool ret = l.dispatch (c); + c->set_lookup_index (saved_lookup_index); + c->set_lookup_props (saved_lookup_props); + return ret; +} + + +#undef attach_chain +#undef attach_type + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh new file mode 100644 index 000000000..66fcb3f3a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh @@ -0,0 +1,1369 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2010,2012,2013 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" + + +namespace OT { + + +struct SingleSubstFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + /* TODO Switch to range-based API to work around malicious fonts. + * https://github.com/behdad/harfbuzz/issues/363 */ + hb_codepoint_t glyph_id = iter.get_glyph (); + if (c->glyphs->has (glyph_id)) + c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu); + } + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + /* TODO Switch to range-based API to work around malicious fonts. + * https://github.com/behdad/harfbuzz/issues/363 */ + hb_codepoint_t glyph_id = iter.get_glyph (); + c->input->add (glyph_id); + c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu); + } + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + /* According to the Adobe Annotated OpenType Suite, result is always + * limited to 16bit. */ + glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu; + c->replace_glyph (glyph_id); + + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + unsigned int num_glyphs, + int delta) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); + deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */ + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); + } + + protected: + 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 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + Coverage::Iter iter; + unsigned int count = substitute.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + if (c->glyphs->has (iter.get_glyph ())) + c->glyphs->add (substitute[iter.get_coverage ()]); + } + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + unsigned int count = substitute.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + c->input->add (iter.get_glyph ()); + c->output->add (substitute[iter.get_coverage ()]); + } + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= substitute.len)) return_trace (false); + + glyph_id = substitute[index]; + c->replace_glyph (glyph_id); + + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<GlyphID> &substitutes, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return_trace (false); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && substitute.sanitize (c)); + } + + protected: + 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 +{ + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<GlyphID> &substitutes, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 2; + int delta = 0; + if (num_glyphs) { + format = 1; + /* TODO(serialize) check for wrap-around */ + delta = substitutes[0] - glyphs[0]; + for (unsigned int i = 1; i < num_glyphs; i++) + if (delta != substitutes[i] - glyphs[i]) { + format = 2; + break; + } + } + u.format.set (format); + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs, delta)); + case 2: return_trace (u.format2.serialize (c, glyphs, substitutes, num_glyphs)); + default:return_trace (false); + } + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + case 2: return_trace (c->dispatch (u.format2)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + SingleSubstFormat1 format1; + SingleSubstFormat2 format2; + } u; +}; + + +struct Sequence +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + unsigned int count = substitute.len; + for (unsigned int i = 0; i < count; i++) + c->glyphs->add (substitute[i]); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int count = substitute.len; + for (unsigned int i = 0; i < count; i++) + c->output->add (substitute[i]); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = substitute.len; + + /* Special-case to make it in-place and not consider this + * as a "multiplied" substitution. */ + if (unlikely (count == 1)) + { + c->replace_glyph (substitute.array[0]); + return_trace (true); + } + /* Spec disallows this, but Uniscribe allows it. + * https://github.com/behdad/harfbuzz/issues/253 */ + else if (unlikely (count == 0)) + { + c->buffer->delete_glyph (); + return_trace (true); + } + + unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; + + for (unsigned int i = 0; i < count; i++) { + _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); + c->output_glyph_for_component (substitute.array[i], klass); + } + c->buffer->skip_glyph (); + + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + ArrayOf<GlyphID> + substitute; /* String of GlyphIDs to substitute */ + public: + DEFINE_SIZE_ARRAY (2, substitute); +}; + +struct MultipleSubstFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + Coverage::Iter iter; + unsigned int count = sequence.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + if (c->glyphs->has (iter.get_glyph ())) + (this+sequence[iter.get_coverage ()]).closure (c); + } + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + unsigned int count = sequence.len; + for (unsigned int i = 0; i < count; i++) + (this+sequence[i]).collect_glyphs (c); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+sequence[index]).apply (c)); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &substitute_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &substitute_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!sequence.serialize (c, num_glyphs))) return_trace (false); + for (unsigned int i = 0; i < num_glyphs; i++) + if (unlikely (!sequence[i].serialize (c, this).serialize (c, + substitute_glyphs_list, + substitute_len_list[i]))) return_trace (false); + substitute_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this)); + } + + protected: + 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 +{ + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &substitute_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &substitute_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format.set (format); + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list)); + default:return_trace (false); + } + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + MultipleSubstFormat1 format1; + } u; +}; + + +typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in + * arbitrary order */ + +struct AlternateSubstFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + Coverage::Iter iter; + unsigned int count = alternateSet.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + 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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + unsigned int count = alternateSet.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + c->input->add (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->output->add (alt_set[i]); + } + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + const AlternateSet &alt_set = this+alternateSet[index]; + + if (unlikely (!alt_set.len)) return_trace (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 (false); + + glyph_id = alt_set[alt_index - 1]; + + c->replace_glyph (glyph_id); + + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &alternate_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!alternateSet.serialize (c, num_glyphs))) return_trace (false); + for (unsigned int i = 0; i < num_glyphs; i++) + if (unlikely (!alternateSet[i].serialize (c, this).serialize (c, + alternate_glyphs_list, + alternate_len_list[i]))) return_trace (false); + alternate_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); + } + + protected: + 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 +{ + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &alternate_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format.set (format); + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list)); + default:return_trace (false); + } + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + AlternateSubstFormat1 format1; + } u; +}; + + +struct Ligature +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + 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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int count = component.len; + for (unsigned int i = 1; i < count; i++) + c->input->add (component[i]); + c->output->add (ligGlyph); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + if (c->len != component.len) + return_trace (false); + + for (unsigned int i = 1; i < c->len; i++) + if (likely (c->glyphs[i] != component[i])) + return_trace (false); + + return_trace (true); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = component.len; + + if (unlikely (!count)) return_trace (false); + + /* Special-case to make it in-place and not consider this + * as a "ligated" substitution. */ + if (unlikely (count == 1)) + { + c->replace_glyph (ligGlyph); + return_trace (true); + } + + bool is_mark_ligature = false; + unsigned int total_component_count = 0; + + unsigned int match_length = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + + if (likely (!match_input (c, count, + &component[1], + match_glyph, + NULL, + &match_length, + match_positions, + &is_mark_ligature, + &total_component_count))) + return_trace (false); + + ligate_input (c, + count, + match_positions, + match_length, + ligGlyph, + is_mark_ligature, + total_component_count); + + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + GlyphID ligature, + Supplier<GlyphID> &components, /* Starting from second */ + unsigned int num_components /* Including first component */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + ligGlyph = ligature; + if (unlikely (!component.serialize (c, components, num_components))) return_trace (false); + return_trace (true); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); + } + + protected: + 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 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + (this+ligature[i]).closure (c); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + (this+ligature[i]).collect_glyphs (c); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + 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 (c)) + return_trace (true); + } + return_trace (false); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + 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 (true); + } + + return_trace (false); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &ligatures, + Supplier<unsigned int> &component_count_list, + unsigned int num_ligatures, + Supplier<GlyphID> &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!ligature.serialize (c, num_ligatures))) return_trace (false); + for (unsigned int i = 0; i < num_ligatures; i++) + if (unlikely (!ligature[i].serialize (c, this).serialize (c, + ligatures[i], + component_list, + component_count_list[i]))) return_trace (false); + ligatures.advance (num_ligatures); + component_count_list.advance (num_ligatures); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligature.sanitize (c, this)); + } + + protected: + OffsetArrayOf<Ligature> + ligature; /* Array LigatureSet tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, ligature); +}; + +struct LigatureSubstFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + Coverage::Iter iter; + unsigned int count = ligatureSet.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + if (c->glyphs->has (iter.get_glyph ())) + (this+ligatureSet[iter.get_coverage ()]).closure (c); + } + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + unsigned int count = ligatureSet.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + c->input->add (iter.get_glyph ()); + (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c); + } + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); + if (likely (index == NOT_COVERED)) return_trace (false); + + const LigatureSet &lig_set = this+ligatureSet[index]; + return_trace (lig_set.would_apply (c)); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + const LigatureSet &lig_set = this+ligatureSet[index]; + return_trace (lig_set.apply (c)); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &first_glyphs, + Supplier<unsigned int> &ligature_per_first_glyph_count_list, + unsigned int num_first_glyphs, + Supplier<GlyphID> &ligatures_list, + Supplier<unsigned int> &component_count_list, + Supplier<GlyphID> &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return_trace (false); + for (unsigned int i = 0; i < num_first_glyphs; i++) + if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c, + ligatures_list, + component_count_list, + ligature_per_first_glyph_count_list[i], + component_list))) return_trace (false); + ligature_per_first_glyph_count_list.advance (num_first_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); + } + + protected: + 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 +{ + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &first_glyphs, + Supplier<unsigned int> &ligature_per_first_glyph_count_list, + unsigned int num_first_glyphs, + Supplier<GlyphID> &ligatures_list, + Supplier<unsigned int> &component_count_list, + Supplier<GlyphID> &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format.set (format); + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + num_first_glyphs, + ligatures_list, + component_count_list, + component_list)); + default:return_trace (false); + } + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + LigatureSubstFormat1 format1; + } u; +}; + + +struct ContextSubst : Context {}; + +struct ChainContextSubst : ChainContext {}; + +struct ExtensionSubst : Extension<ExtensionSubst> +{ + typedef struct SubstLookupSubTable LookupSubTable; + + inline bool is_reverse (void) const; +}; + + +struct ReverseChainSingleSubstFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + 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; + count = substitute.len; + for (iter.init (this+coverage); iter.more (); iter.next ()) + { + if (unlikely (iter.get_coverage () >= count)) + break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */ + if (c->glyphs->has (iter.get_glyph ())) + c->glyphs->add (substitute[iter.get_coverage ()]); + } + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + + unsigned int count; + + (this+coverage).add_coverage (c->input); + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + (this+backtrack[i]).add_coverage (c->before); + + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + (this+lookahead[i]).add_coverage (c->after); + + const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); + count = substitute.len; + for (unsigned int i = 0; i < count; i++) + c->output->add (substitute[i]); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) + return_trace (false); /* No chaining to this type */ + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (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->replace_glyph_inplace (substitute[index]); + /* Note: We DON'T decrease buffer->idx. The main loop does it + * for us. This is useful for preventing surprises if someone + * calls us through a Context lookup. */ + return_trace (true); + } + + return_trace (false); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) + return_trace (false); + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + if (!lookahead.sanitize (c, this)) + return_trace (false); + const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); + return_trace (substitute.sanitize (c)); + } + + protected: + 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 +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + 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 + }; + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + { + TRACE_DISPATCH (this, lookup_type); + if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ()); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c)); + case Multiple: return_trace (u.multiple.dispatch (c)); + case Alternate: return_trace (u.alternate.dispatch (c)); + case Ligature: return_trace (u.ligature.dispatch (c)); + case Context: return_trace (u.context.dispatch (c)); + case ChainContext: return_trace (u.chainContext.dispatch (c)); + case Extension: return_trace (u.extension.dispatch (c)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c)); + default: return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT sub_format; + SingleSubst single; + MultipleSubst multiple; + AlternateSubst alternate; + LigatureSubst ligature; + ContextSubst context; + 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 Lookup::get_subtable<SubstLookupSubTable> (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 (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + c->set_recurse_func (dispatch_recurse_func<hb_closure_context_t>); + return_trace (dispatch (c)); + } + + inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>); + return_trace (dispatch (c)); + } + + template <typename set_t> + inline void add_coverage (set_t *glyphs) const + { + hb_add_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } + + inline bool would_apply (hb_would_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t *accel) const + { + TRACE_WOULD_APPLY (this); + if (unlikely (!c->len)) return_trace (false); + if (!accel->may_have (c->glyphs[0])) return_trace (false); + return_trace (dispatch (c)); + } + + static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index); + + inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c, + unsigned int i) + { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); } + + inline bool serialize_single (hb_serialize_context_t *c, + uint32_t lookup_props, + Supplier<GlyphID> &glyphs, + Supplier<GlyphID> &substitutes, + unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return_trace (false); + return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs)); + } + + inline bool serialize_multiple (hb_serialize_context_t *c, + uint32_t lookup_props, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &substitute_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &substitute_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return_trace (false); + return_trace (serialize_subtable (c, 0).u.multiple.serialize (c, + glyphs, + substitute_len_list, + num_glyphs, + substitute_glyphs_list)); + } + + inline bool serialize_alternate (hb_serialize_context_t *c, + uint32_t lookup_props, + Supplier<GlyphID> &glyphs, + Supplier<unsigned int> &alternate_len_list, + unsigned int num_glyphs, + Supplier<GlyphID> &alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return_trace (false); + return_trace (serialize_subtable (c, 0).u.alternate.serialize (c, + glyphs, + alternate_len_list, + num_glyphs, + alternate_glyphs_list)); + } + + inline bool serialize_ligature (hb_serialize_context_t *c, + uint32_t lookup_props, + Supplier<GlyphID> &first_glyphs, + Supplier<unsigned int> &ligature_per_first_glyph_count_list, + unsigned int num_first_glyphs, + Supplier<GlyphID> &ligatures_list, + Supplier<unsigned int> &component_count_list, + Supplier<GlyphID> &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return_trace (false); + return_trace (serialize_subtable (c, 0).u.ligature.serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + num_first_glyphs, + ligatures_list, + component_count_list, + component_list)); + } + + template <typename context_t> + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { return Lookup::dispatch<SubstLookupSubTable> (c); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!Lookup::sanitize (c))) return_trace (false); + if (unlikely (!dispatch (c))) return_trace (false); + + if (unlikely (get_type () == SubstLookupSubTable::Extension)) + { + /* The spec says all subtables of an Extension lookup should + * have the same type. This is specially important if one has + * a reverse type! */ + 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_trace (false); + } + return_trace (true); + } +}; + +typedef OffsetListOf<SubstLookup> SubstLookupList; + +/* + * GSUB -- The Glyph Substitution Table + */ + +struct GSUB : GSUBGPOS +{ + static const hb_tag_t tableTag = HB_OT_TAG_GSUB; + + inline const SubstLookup& get_lookup (unsigned int i) const + { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } + + static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer); + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false); + const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); + return_trace (list.sanitize (c, this)); + } +}; + + +void +GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint)); + _hb_glyph_info_clear_lig_props (&buffer->info[i]); + buffer->info[i].syllable() = 0; + } +} + + +/* Out-of-class implementation for methods recursing */ + +/*static*/ inline bool ExtensionSubst::is_reverse (void) const +{ + unsigned int type = get_type (); + if (unlikely (type == SubstLookupSubTable::Extension)) + return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse (); + return SubstLookup::lookup_type_is_reverse (type); +} + +template <typename context_t> +/*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +{ + const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); + const SubstLookup &l = gsub.get_lookup (lookup_index); + return l.dispatch (c); +} + +/*static*/ inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) +{ + const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); + const SubstLookup &l = gsub.get_lookup (lookup_index); + unsigned int saved_lookup_props = c->lookup_props; + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); + bool ret = l.dispatch (c); + c->set_lookup_index (saved_lookup_index); + c->set_lookup_props (saved_lookup_props); + return ret; +} + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh new file mode 100644 index 000000000..c4de40386 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh @@ -0,0 +1,2329 @@ +/* + * 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_GSUBGPOS_PRIVATE_HH +#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH + +#include "hb-buffer-private.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-set-private.hh" + + +namespace OT { + + +#ifndef HB_DEBUG_CLOSURE +#define HB_DEBUG_CLOSURE (HB_DEBUG+0) +#endif + +#define TRACE_CLOSURE(this) \ + hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + ""); + +struct hb_closure_context_t : + hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE> +{ + inline const char *get_name (void) { return "CLOSURE"; } + typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index); + template <typename T> + inline return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; } + static return_t default_return_value (void) { return HB_VOID; } + bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; } + return_t recurse (unsigned int lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return default_return_value (); + + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + return HB_VOID; + } + + hb_face_t *face; + hb_set_t *glyphs; + recurse_func_t recurse_func; + 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_ = HB_MAX_NESTING_LEVEL) : + face (face_), + glyphs (glyphs_), + recurse_func (NULL), + nesting_level_left (nesting_level_left_), + debug_depth (0) {} + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } +}; + + + +#ifndef HB_DEBUG_WOULD_APPLY +#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) +#endif + +#define TRACE_WOULD_APPLY(this) \ + hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "%d glyphs", c->len); + +struct hb_would_apply_context_t : + hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY> +{ + inline const char *get_name (void) { return "WOULD_APPLY"; } + template <typename T> + inline return_t dispatch (const T &obj) { return obj.would_apply (this); } + static return_t default_return_value (void) { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + hb_face_t *face; + const hb_codepoint_t *glyphs; + unsigned int len; + bool zero_context; + unsigned int debug_depth; + + hb_would_apply_context_t (hb_face_t *face_, + const hb_codepoint_t *glyphs_, + unsigned int len_, + bool zero_context_) : + face (face_), + glyphs (glyphs_), + len (len_), + zero_context (zero_context_), + debug_depth (0) {} +}; + + + +#ifndef HB_DEBUG_COLLECT_GLYPHS +#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0) +#endif + +#define TRACE_COLLECT_GLYPHS(this) \ + hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + ""); + +struct hb_collect_glyphs_context_t : + hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_void_t, HB_DEBUG_COLLECT_GLYPHS> +{ + inline const char *get_name (void) { return "COLLECT_GLYPHS"; } + typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index); + template <typename T> + inline return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; } + static return_t default_return_value (void) { return HB_VOID; } + bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; } + return_t recurse (unsigned int lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return default_return_value (); + + /* Note that GPOS sets recurse_func to NULL already, so it doesn't get + * past the previous check. For GSUB, we only want to collect the output + * glyphs in the recursion. If output is not requested, we can go home now. + * + * Note further, that the above is not exactly correct. A recursed lookup + * is allowed to match input that is not matched in the context, but that's + * not how most fonts are built. It's possible to relax that and recurse + * with all sets here if it proves to be an issue. + */ + + if (output == hb_set_get_empty ()) + return HB_VOID; + + /* Return if new lookup was recursed to before. */ + if (recursed_lookups.has (lookup_index)) + return HB_VOID; + + hb_set_t *old_before = before; + hb_set_t *old_input = input; + hb_set_t *old_after = after; + before = input = after = hb_set_get_empty (); + + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + + before = old_before; + input = old_input; + after = old_after; + + recursed_lookups.add (lookup_index); + + return HB_VOID; + } + + hb_face_t *face; + hb_set_t *before; + hb_set_t *input; + hb_set_t *after; + hb_set_t *output; + recurse_func_t recurse_func; + hb_set_t recursed_lookups; + unsigned int nesting_level_left; + unsigned int debug_depth; + + hb_collect_glyphs_context_t (hb_face_t *face_, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output, /* OUT. May be NULL */ + unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + before (glyphs_before ? glyphs_before : hb_set_get_empty ()), + input (glyphs_input ? glyphs_input : hb_set_get_empty ()), + after (glyphs_after ? glyphs_after : hb_set_get_empty ()), + output (glyphs_output ? glyphs_output : hb_set_get_empty ()), + recurse_func (NULL), + recursed_lookups (), + nesting_level_left (nesting_level_left_), + debug_depth (0) + { + recursed_lookups.init (); + } + ~hb_collect_glyphs_context_t (void) + { + recursed_lookups.fini (); + } + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } +}; + + + +#ifndef HB_DEBUG_GET_COVERAGE +#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) +#endif + +/* XXX Can we remove this? */ + +template <typename set_t> +struct hb_add_coverage_context_t : + hb_dispatch_context_t<hb_add_coverage_context_t<set_t>, const Coverage &, HB_DEBUG_GET_COVERAGE> +{ + inline const char *get_name (void) { return "GET_COVERAGE"; } + typedef const Coverage &return_t; + template <typename T> + inline return_t dispatch (const T &obj) { return obj.get_coverage (); } + static return_t default_return_value (void) { return Null(Coverage); } + bool stop_sublookup_iteration (return_t r) const + { + r.add_coverage (set); + return false; + } + + hb_add_coverage_context_t (set_t *set_) : + set (set_), + debug_depth (0) {} + + set_t *set; + unsigned int debug_depth; +}; + + + +#ifndef HB_DEBUG_APPLY +#define HB_DEBUG_APPLY (HB_DEBUG+0) +#endif + +#define TRACE_APPLY(this) \ + hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "idx %d gid %u lookup %d", \ + c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index); + +struct hb_apply_context_t : + hb_dispatch_context_t<hb_apply_context_t, bool, HB_DEBUG_APPLY> +{ + struct matcher_t + { + inline matcher_t (void) : + lookup_props (0), + ignore_zwnj (false), + ignore_zwj (false), + mask (-1), +#define arg1(arg) (arg) /* Remove the macro to see why it's needed! */ + syllable arg1(0), +#undef arg1 + match_func (NULL), + match_data (NULL) {}; + + typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data); + + inline void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; } + inline void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; } + inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; } + inline void set_mask (hb_mask_t mask_) { mask = mask_; } + inline void set_syllable (uint8_t syllable_) { syllable = syllable_; } + inline void set_match_func (match_func_t match_func_, + const void *match_data_) + { match_func = match_func_; match_data = match_data_; } + + enum may_match_t { + MATCH_NO, + MATCH_YES, + MATCH_MAYBE + }; + + inline may_match_t may_match (const hb_glyph_info_t &info, + const USHORT *glyph_data) const + { + if (!(info.mask & mask) || + (syllable && syllable != info.syllable ())) + return MATCH_NO; + + if (match_func) + return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO; + + return MATCH_MAYBE; + } + + enum may_skip_t { + SKIP_NO, + SKIP_YES, + SKIP_MAYBE + }; + + inline may_skip_t + may_skip (const hb_apply_context_t *c, + const hb_glyph_info_t &info) const + { + if (!c->check_glyph_property (&info, lookup_props)) + return SKIP_YES; + + if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_fvs (&info) && + (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)))) + return SKIP_MAYBE; + + return SKIP_NO; + } + + protected: + unsigned int lookup_props; + bool ignore_zwnj; + bool ignore_zwj; + hb_mask_t mask; + uint8_t syllable; + match_func_t match_func; + const void *match_data; + }; + + struct skipping_iterator_t + { + inline void init (hb_apply_context_t *c_, bool context_match = false) + { + c = c_; + match_glyph_data = NULL, + matcher.set_match_func (NULL, NULL); + matcher.set_lookup_props (c->lookup_props); + /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */ + matcher.set_ignore_zwnj (context_match || c->table_index == 1); + /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */ + matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj); + matcher.set_mask (context_match ? -1 : c->lookup_mask); + } + inline void set_lookup_props (unsigned int lookup_props) + { + matcher.set_lookup_props (lookup_props); + } + inline void set_match_func (matcher_t::match_func_t match_func_, + const void *match_data_, + const USHORT glyph_data[]) + { + matcher.set_match_func (match_func_, match_data_); + match_glyph_data = glyph_data; + } + + inline void reset (unsigned int start_index_, + unsigned int num_items_) + { + idx = start_index_; + num_items = num_items_; + end = c->buffer->len; + matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); + } + + inline void reject (void) { num_items++; match_glyph_data--; } + + inline bool next (void) + { + assert (num_items > 0); + while (idx + num_items < end) + { + idx++; + const hb_glyph_info_t &info = c->buffer->info[idx]; + + matcher_t::may_skip_t skip = matcher.may_skip (c, info); + if (unlikely (skip == matcher_t::SKIP_YES)) + continue; + + matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data); + if (match == matcher_t::MATCH_YES || + (match == matcher_t::MATCH_MAYBE && + skip == matcher_t::SKIP_NO)) + { + num_items--; + match_glyph_data++; + return true; + } + + if (skip == matcher_t::SKIP_NO) + return false; + } + return false; + } + inline bool prev (void) + { + assert (num_items > 0); + while (idx >= num_items) + { + idx--; + const hb_glyph_info_t &info = c->buffer->out_info[idx]; + + matcher_t::may_skip_t skip = matcher.may_skip (c, info); + if (unlikely (skip == matcher_t::SKIP_YES)) + continue; + + matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data); + if (match == matcher_t::MATCH_YES || + (match == matcher_t::MATCH_MAYBE && + skip == matcher_t::SKIP_NO)) + { + num_items--; + match_glyph_data++; + return true; + } + + if (skip == matcher_t::SKIP_NO) + return false; + } + return false; + } + + unsigned int idx; + protected: + hb_apply_context_t *c; + matcher_t matcher; + const USHORT *match_glyph_data; + + unsigned int num_items; + unsigned int end; + }; + + + inline const char *get_name (void) { return "APPLY"; } + typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index); + template <typename T> + inline return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value (void) { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + return_t recurse (unsigned int lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return default_return_value (); + + nesting_level_left--; + bool ret = recurse_func (this, lookup_index); + nesting_level_left++; + return ret; + } + + unsigned int table_index; /* GSUB/GPOS */ + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_direction_t direction; + hb_mask_t lookup_mask; + bool auto_zwj; + recurse_func_t recurse_func; + unsigned int nesting_level_left; + unsigned int lookup_props; + const GDEF &gdef; + bool has_glyph_classes; + const VariationStore &var_store; + skipping_iterator_t iter_input, iter_context; + unsigned int lookup_index; + unsigned int debug_depth; + + + hb_apply_context_t (unsigned int table_index_, + hb_font_t *font_, + hb_buffer_t *buffer_) : + table_index (table_index_), + font (font_), face (font->face), buffer (buffer_), + direction (buffer_->props.direction), + lookup_mask (1), + auto_zwj (true), + recurse_func (NULL), + nesting_level_left (HB_MAX_NESTING_LEVEL), + lookup_props (0), + gdef (*hb_ot_layout_from_face (face)->gdef), + has_glyph_classes (gdef.has_glyph_classes ()), + var_store (gdef.get_var_store ()), + iter_input (), + iter_context (), + lookup_index ((unsigned int) -1), + debug_depth (0) {} + + inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; } + inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; } + inline void set_recurse_func (recurse_func_t func) { recurse_func = func; } + inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } + inline void set_lookup_props (unsigned int lookup_props_) + { + lookup_props = lookup_props_; + iter_input.init (this, false); + iter_context.init (this, true); + } + + inline bool + match_properties_mark (hb_codepoint_t glyph, + unsigned int glyph_props, + unsigned int match_props) const + { + /* If using mark filtering sets, the high short of + * match_props has the set index. + */ + if (match_props & LookupFlag::UseMarkFilteringSet) + return gdef.mark_set_covers (match_props >> 16, glyph); + + /* The second byte of match_props has the meaning + * "ignore marks of attachment type different than + * the attachment type specified." + */ + if (match_props & LookupFlag::MarkAttachmentType) + return (match_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType); + + return true; + } + + inline bool + check_glyph_property (const hb_glyph_info_t *info, + unsigned int match_props) const + { + hb_codepoint_t glyph = info->codepoint; + unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info); + + /* Not covered, if, for example, glyph class is ligature and + * match_props includes LookupFlags::IgnoreLigatures + */ + if (glyph_props & match_props & LookupFlag::IgnoreFlags) + return false; + + if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) + return match_properties_mark (glyph, glyph_props, match_props); + + return true; + } + + inline void _set_glyph_props (hb_codepoint_t glyph_index, + unsigned int class_guess = 0, + bool ligature = false, + bool component = false) const + { + unsigned int add_in = _hb_glyph_info_get_glyph_props (&buffer->cur()) & + HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE; + add_in |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED; + if (ligature) + { + add_in |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED; + /* In the only place that the MULTIPLIED bit is used, Uniscribe + * seems to only care about the "last" transformation between + * Ligature and Multiple substitions. Ie. if you ligate, expand, + * and ligate again, it forgives the multiplication and acts as + * if only ligation happened. As such, clear MULTIPLIED bit. + */ + add_in &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; + } + if (component) + add_in |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; + if (likely (has_glyph_classes)) + _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | gdef.get_glyph_props (glyph_index)); + else if (class_guess) + _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | class_guess); + } + + inline void replace_glyph (hb_codepoint_t glyph_index) const + { + _set_glyph_props (glyph_index); + buffer->replace_glyph (glyph_index); + } + inline void replace_glyph_inplace (hb_codepoint_t glyph_index) const + { + _set_glyph_props (glyph_index); + buffer->cur().codepoint = glyph_index; + } + inline void replace_glyph_with_ligature (hb_codepoint_t glyph_index, + unsigned int class_guess) const + { + _set_glyph_props (glyph_index, class_guess, true); + buffer->replace_glyph (glyph_index); + } + inline void output_glyph_for_component (hb_codepoint_t glyph_index, + unsigned int class_guess) const + { + _set_glyph_props (glyph_index, class_guess, false, true); + buffer->output_glyph (glyph_index); + } +}; + + + +typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data); +typedef void (*collect_glyphs_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); + +struct ContextClosureFuncs +{ + intersects_func_t intersects; +}; +struct ContextCollectGlyphsFuncs +{ + collect_glyphs_func_t collect; +}; +struct ContextApplyFuncs +{ + match_func_t match; +}; + + +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 void collect_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED) +{ + glyphs->add (value); +} +static inline void collect_class (hb_set_t *glyphs, const USHORT &value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + class_def.add_class (glyphs, value); +} +static inline void collect_coverage (hb_set_t *glyphs, const USHORT &value, const void *data) +{ + const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value; + (data+coverage).add_coverage (glyphs); +} +static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, + hb_set_t *glyphs, + unsigned int count, + const USHORT values[], + collect_glyphs_func_t collect_func, + const void *collect_data) +{ + for (unsigned int i = 0; i < count; i++) + collect_func (glyphs, values[i], collect_data); +} + + +static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED) +{ + return glyph_id == value; +} +static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + return class_def.get_class (glyph_id) == value; +} +static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data) +{ + const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value; + return (data+coverage).get_coverage (glyph_id) != NOT_COVERED; +} + +static inline bool would_match_input (hb_would_apply_context_t *c, + unsigned int count, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + match_func_t match_func, + const void *match_data) +{ + if (count != c->len) + return false; + + for (unsigned int i = 1; i < count; i++) + if (likely (!match_func (c->glyphs[i], input[i - 1], match_data))) + return false; + + return true; +} +static inline bool match_input (hb_apply_context_t *c, + unsigned int count, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + match_func_t match_func, + const void *match_data, + unsigned int *end_offset, + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], + bool *p_is_mark_ligature = NULL, + unsigned int *p_total_component_count = NULL) +{ + TRACE_APPLY (NULL); + + if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); + + hb_buffer_t *buffer = c->buffer; + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, count - 1); + skippy_iter.set_match_func (match_func, match_data, input); + + /* + * This is perhaps the trickiest part of OpenType... Remarks: + * + * - If all components of the ligature were marks, we call this a mark ligature. + * + * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize + * it as a ligature glyph. + * + * - Ligatures cannot be formed across glyphs attached to different components + * of previous ligatures. Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and + * LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother. + * However, it would be wrong to ligate that SHADDA,FATHA sequence.o + * There is an exception to this: If a ligature tries ligating with marks that + * belong to it itself, go ahead, assuming that the font designer knows what + * they are doing (otherwise it can break Indic stuff when a matra wants to + * ligate with a conjunct...) + */ + + bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->cur()); + + unsigned int total_component_count = 0; + total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + + unsigned int first_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int first_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + + match_positions[0] = buffer->idx; + for (unsigned int i = 1; i < count; i++) + { + if (!skippy_iter.next ()) return_trace (false); + + match_positions[i] = skippy_iter.idx; + + unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]); + unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]); + + if (first_lig_id && first_lig_comp) { + /* If first component was attached to a previous ligature component, + * all subsequent components should be attached to the same ligature + * component, otherwise we shouldn't ligate them. */ + if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp) + return_trace (false); + } else { + /* If first component was NOT attached to a previous ligature component, + * all subsequent components should also NOT be attached to any ligature + * component, unless they are attached to the first component itself! */ + if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id)) + return_trace (false); + } + + is_mark_ligature = is_mark_ligature && _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]); + total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]); + } + + *end_offset = skippy_iter.idx - buffer->idx + 1; + + if (p_is_mark_ligature) + *p_is_mark_ligature = is_mark_ligature; + + if (p_total_component_count) + *p_total_component_count = total_component_count; + + return_trace (true); +} +static inline bool ligate_input (hb_apply_context_t *c, + unsigned int count, /* Including the first glyph */ + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + unsigned int match_length, + hb_codepoint_t lig_glyph, + bool is_mark_ligature, + unsigned int total_component_count) +{ + TRACE_APPLY (NULL); + + hb_buffer_t *buffer = c->buffer; + + buffer->merge_clusters (buffer->idx, buffer->idx + match_length); + + /* + * - If it *is* a mark ligature, we don't allocate a new ligature id, and leave + * the ligature to keep its old ligature id. This will allow it to attach to + * a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH, + * and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA wit a + * ligature id and component value of 2. Then if SHADDA,FATHA form a ligature + * later, we don't want them to lose their ligature id/component, otherwise + * GPOS will fail to correctly position the mark ligature on top of the + * LAM,LAM,HEH ligature. See: + * https://bugzilla.gnome.org/show_bug.cgi?id=676343 + * + * - If a ligature is formed of components that some of which are also ligatures + * themselves, and those ligature components had marks attached to *their* + * components, we have to attach the marks to the new ligature component + * positions! Now *that*'s tricky! And these marks may be following the + * last component of the whole sequence, so we should loop forward looking + * for them and update them. + * + * Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a + * 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature + * id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature + * form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to + * the new ligature with a component value of 2. + * + * This in fact happened to a font... See: + * https://bugzilla.gnome.org/show_bug.cgi?id=437633 + */ + + unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; + unsigned int lig_id = is_mark_ligature ? 0 : _hb_allocate_lig_id (buffer); + unsigned int last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + unsigned int components_so_far = last_num_components; + + if (!is_mark_ligature) + { + _hb_glyph_info_set_lig_props_for_ligature (&buffer->cur(), lig_id, total_component_count); + if (_hb_glyph_info_get_general_category (&buffer->cur()) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + { + _hb_glyph_info_set_general_category (&buffer->cur(), HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER); + } + } + c->replace_glyph_with_ligature (lig_glyph, klass); + + for (unsigned int i = 1; i < count; i++) + { + while (buffer->idx < match_positions[i] && !buffer->in_error) + { + if (!is_mark_ligature) { + unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (this_comp == 0) + this_comp = last_num_components; + unsigned int new_lig_comp = components_so_far - last_num_components + + MIN (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp); + } + buffer->next_glyph (); + } + + last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + components_so_far += last_num_components; + + /* Skip the base glyph */ + buffer->idx++; + } + + if (!is_mark_ligature && last_lig_id) { + /* Re-adjust components for any marks following. */ + for (unsigned int i = buffer->idx; i < buffer->len; i++) { + if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) { + unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); + if (!this_comp) + break; + unsigned int new_lig_comp = components_so_far - last_num_components + + MIN (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); + } else + break; + } + } + return_trace (true); +} + +static inline bool match_backtrack (hb_apply_context_t *c, + unsigned int count, + const USHORT backtrack[], + match_func_t match_func, + const void *match_data) +{ + TRACE_APPLY (NULL); + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + skippy_iter.reset (c->buffer->backtrack_len (), count); + skippy_iter.set_match_func (match_func, match_data, backtrack); + + for (unsigned int i = 0; i < count; i++) + if (!skippy_iter.prev ()) + return_trace (false); + + return_trace (true); +} + +static inline bool match_lookahead (hb_apply_context_t *c, + unsigned int count, + const USHORT lookahead[], + match_func_t match_func, + const void *match_data, + unsigned int offset) +{ + TRACE_APPLY (NULL); + + hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + skippy_iter.reset (c->buffer->idx + offset - 1, count); + skippy_iter.set_match_func (match_func, match_data, lookahead); + + for (unsigned int i = 0; i < count; i++) + if (!skippy_iter.next ()) + return_trace (false); + + return_trace (true); +} + + + +struct LookupRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + USHORT sequenceIndex; /* Index into current glyph + * sequence--first glyph = 0 */ + USHORT lookupListIndex; /* Lookup to apply to that + * position--zero--based */ + public: + DEFINE_SIZE_STATIC (4); +}; + + +template <typename context_t> +static inline void recurse_lookups (context_t *c, + unsigned int lookupCount, + const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */) +{ + for (unsigned int i = 0; i < lookupCount; i++) + c->recurse (lookupRecord[i].lookupListIndex); +} + +static inline bool apply_lookup (hb_apply_context_t *c, + unsigned int count, /* Including the first glyph */ + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ + unsigned int match_length) +{ + TRACE_APPLY (NULL); + + hb_buffer_t *buffer = c->buffer; + int end; + + /* All positions are distance from beginning of *output* buffer. + * Adjust. */ + { + unsigned int bl = buffer->backtrack_len (); + end = bl + match_length; + + int delta = bl - buffer->idx; + /* Convert positions to new indexing. */ + for (unsigned int j = 0; j < count; j++) + match_positions[j] += delta; + } + + for (unsigned int i = 0; i < lookupCount && !buffer->in_error; i++) + { + unsigned int idx = lookupRecord[i].sequenceIndex; + if (idx >= count) + continue; + + /* Don't recurse to ourself at same position. + * Note that this test is too naive, it doesn't catch longer loops. */ + if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index) + continue; + + buffer->move_to (match_positions[idx]); + + unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len (); + if (!c->recurse (lookupRecord[i].lookupListIndex)) + continue; + + unsigned int new_len = buffer->backtrack_len () + buffer->lookahead_len (); + int delta = new_len - orig_len; + + if (!delta) + continue; + + /* Recursed lookup changed buffer len. Adjust. */ + + end += delta; + if (end <= int (match_positions[idx])) + { + /* End might end up being smaller than match_positions[idx] if the recursed + * lookup ended up removing many items, more than we have had matched. + * Just never rewind end back and get out of here. + * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 */ + end = match_positions[idx]; + /* There can't be any further changes. */ + break; + } + + unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */ + + if (delta > 0) + { + if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH)) + break; + } + else + { + /* NOTE: delta is negative. */ + delta = MAX (delta, (int) next - (int) count); + next -= delta; + } + + /* Shift! */ + memmove (match_positions + next + delta, match_positions + next, + (count - next) * sizeof (match_positions[0])); + next += delta; + count += delta; + + /* Fill in new entries. */ + for (unsigned int j = idx + 1; j < next; j++) + match_positions[j] = match_positions[j - 1] + 1; + + /* And fixup the rest. */ + for (; next < count; next++) + match_positions[next] += delta; + } + + buffer->move_to (end); + + return_trace (true); +} + + + +/* Contextual lookups */ + +struct ContextClosureLookupContext +{ + ContextClosureFuncs funcs; + const void *intersects_data; +}; + +struct ContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data; +}; + +struct ContextApplyLookupContext +{ + ContextApplyFuncs funcs; + const void *match_data; +}; + +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)) + recurse_lookups (c, + lookupCount, lookupRecord); +} + +static inline void context_collect_glyphs_lookup (hb_collect_glyphs_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[], + ContextCollectGlyphsLookupContext &lookup_context) +{ + collect_array (c, c->input, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.collect, lookup_context.collect_data); + recurse_lookups (c, + lookupCount, lookupRecord); +} + +static inline bool context_would_apply_lookup (hb_would_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 HB_UNUSED, + const LookupRecord lookupRecord[] HB_UNUSED, + ContextApplyLookupContext &lookup_context) +{ + return would_match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data); +} +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) +{ + unsigned int match_length = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + return match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data, + &match_length, match_positions) + && apply_lookup (c, + inputCount, match_positions, + lookupCount, lookupRecord, + match_length); +} + +struct Rule +{ + inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); + context_closure_lookup (c, + inputCount, inputZ, + lookupCount, lookupRecord, + lookup_context); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const + { + TRACE_COLLECT_GLYPHS (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); + context_collect_glyphs_lookup (c, + inputCount, inputZ, + lookupCount, lookupRecord, + lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const + { + TRACE_WOULD_APPLY (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); + return_trace (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context)); + } + + inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); + return_trace (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context)); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return inputCount.sanitize (c) + && lookupCount.sanitize (c) + && c->check_range (inputZ, + inputZ[0].static_size * inputCount + + lookupRecordX[0].static_size * lookupCount); + } + + protected: + USHORT inputCount; /* Total number of glyphs in input + * glyph sequence--includes the first + * glyph */ + USHORT lookupCount; /* Number of LookupRecords */ + USHORT inputZ[VAR]; /* Array of match inputs--start with + * second glyph */ + LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in + * design order */ + public: + DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX); +}; + +struct RuleSet +{ + inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).closure (c, lookup_context); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const + { + TRACE_WOULD_APPLY (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + { + if ((this+rule[i]).would_apply (c, lookup_context)) + return_trace (true); + } + return_trace (false); + } + + inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + { + if ((this+rule[i]).apply (c, lookup_context)) + return_trace (true); + } + return_trace (false); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rule.sanitize (c, this)); + } + + protected: + OffsetArrayOf<Rule> + rule; /* Array of Rule tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, rule); +}; + + +struct ContextFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + + const Coverage &cov = (this+coverage); + + struct ContextClosureLookupContext lookup_context = { + {intersects_glyph}, + 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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_glyph}, + NULL + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + (this+ruleSet[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; + struct ContextApplyLookupContext lookup_context = { + {match_glyph}, + NULL + }; + return_trace (rule_set.would_apply (c, lookup_context)); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) + return_trace (false); + + const RuleSet &rule_set = this+ruleSet[index]; + struct ContextApplyLookupContext lookup_context = { + {match_glyph}, + NULL + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + OffsetArrayOf<RuleSet> + ruleSet; /* Array of RuleSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, ruleSet); +}; + + +struct ContextFormat2 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + if (!(this+coverage).intersects (c->glyphs)) + return; + + const ClassDef &class_def = this+classDef; + + struct ContextClosureLookupContext lookup_context = { + {intersects_class}, + &class_def + }; + + 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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + + const ClassDef &class_def = this+classDef; + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_class}, + &class_def + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + (this+ruleSet[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const ClassDef &class_def = this+classDef; + unsigned int index = class_def.get_class (c->glyphs[0]); + const RuleSet &rule_set = this+ruleSet[index]; + struct ContextApplyLookupContext lookup_context = { + {match_class}, + &class_def + }; + return_trace (rule_set.would_apply (c, lookup_context)); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const ClassDef &class_def = this+classDef; + index = class_def.get_class (c->buffer->cur().codepoint); + const RuleSet &rule_set = this+ruleSet[index]; + struct ContextApplyLookupContext lookup_context = { + {match_class}, + &class_def + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 2 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + OffsetTo<ClassDef> + classDef; /* Offset to glyph ClassDef table--from + * beginning of table */ + OffsetArrayOf<RuleSet> + ruleSet; /* Array of RuleSet tables + * ordered by class */ + public: + DEFINE_SIZE_ARRAY (8, ruleSet); +}; + + +struct ContextFormat3 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + if (!(this+coverageZ[0]).intersects (c->glyphs)) + return; + + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); + struct ContextClosureLookupContext lookup_context = { + {intersects_coverage}, + this + }; + context_closure_lookup (c, + glyphCount, (const USHORT *) (coverageZ + 1), + lookupCount, lookupRecord, + lookup_context); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverageZ[0]).add_coverage (c->input); + + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_coverage}, + this + }; + + context_collect_glyphs_lookup (c, + glyphCount, (const USHORT *) (coverageZ + 1), + lookupCount, lookupRecord, + lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); + struct ContextApplyLookupContext lookup_context = { + {match_coverage}, + this + }; + return_trace (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context)); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverageZ[0]; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); + struct ContextApplyLookupContext lookup_context = { + {match_coverage}, + this + }; + return_trace (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + unsigned int count = glyphCount; + if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */ + if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (!coverageZ[i].sanitize (c, this)) return_trace (false); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count); + return_trace (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount)); + } + + protected: + USHORT format; /* Format identifier--format = 3 */ + USHORT glyphCount; /* Number of glyphs in the input glyph + * sequence */ + USHORT lookupCount; /* Number of LookupRecords */ + OffsetTo<Coverage> + coverageZ[VAR]; /* Array of offsets to Coverage + * table in glyph sequence order */ + LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in + * design order */ + public: + DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX); +}; + +struct Context +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + case 2: return_trace (c->dispatch (u.format2)); + case 3: return_trace (c->dispatch (u.format3)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + ContextFormat1 format1; + ContextFormat2 format2; + ContextFormat3 format3; + } u; +}; + + +/* Chaining Contextual lookups */ + +struct ChainContextClosureLookupContext +{ + ContextClosureFuncs funcs; + const void *intersects_data[3]; +}; + +struct ChainContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data[3]; +}; + +struct ChainContextApplyLookupContext +{ + ContextApplyFuncs funcs; + const void *match_data[3]; +}; + +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) +{ + 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])) + recurse_lookups (c, + lookupCount, lookupRecord); +} + +static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_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[], + ChainContextCollectGlyphsLookupContext &lookup_context) +{ + collect_array (c, c->before, + backtrackCount, backtrack, + lookup_context.funcs.collect, lookup_context.collect_data[0]); + collect_array (c, c->input, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.collect, lookup_context.collect_data[1]); + collect_array (c, c->after, + lookaheadCount, lookahead, + lookup_context.funcs.collect, lookup_context.collect_data[2]); + recurse_lookups (c, + lookupCount, lookupRecord); +} + +static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c, + unsigned int backtrackCount, + const USHORT backtrack[] HB_UNUSED, + 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[] HB_UNUSED, + unsigned int lookupCount HB_UNUSED, + const LookupRecord lookupRecord[] HB_UNUSED, + ChainContextApplyLookupContext &lookup_context) +{ + return (c->zero_context ? !backtrackCount && !lookaheadCount : true) + && would_match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data[1]); +} + +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 match_length = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + return match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data[1], + &match_length, match_positions) + && match_backtrack (c, + backtrackCount, backtrack, + lookup_context.funcs.match, lookup_context.match_data[0]) + && match_lookahead (c, + lookaheadCount, lookahead, + lookup_context.funcs.match, lookup_context.match_data[2], + match_length) + && apply_lookup (c, + inputCount, match_positions, + lookupCount, lookupRecord, + match_length); +} + +struct ChainRule +{ + inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (this); + 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 void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const + { + TRACE_COLLECT_GLYPHS (this); + 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_collect_glyphs_lookup (c, + backtrack.len, backtrack.array, + input.len, input.array, + lookahead.len, lookahead.array, + lookup.len, lookup.array, + lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + { + TRACE_WOULD_APPLY (this); + 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_trace (chain_context_would_apply_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 (this); + 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_trace (chain_context_apply_lookup (c, + backtrack.len, backtrack.array, + input.len, input.array, + lookahead.len, lookahead.array, lookup.len, + lookup.array, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!backtrack.sanitize (c)) return_trace (false); + const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); + if (!input.sanitize (c)) return_trace (false); + const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); + if (!lookahead.sanitize (c)) return_trace (false); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + return_trace (lookup.sanitize (c)); + } + + protected: + ArrayOf<USHORT> + backtrack; /* Array of backtracking values + * (to be matched before the input + * sequence) */ + HeadlessArrayOf<USHORT> + inputX; /* Array of input values (start with + * second glyph) */ + ArrayOf<USHORT> + lookaheadX; /* Array of lookahead values's (to be + * matched after the input sequence) */ + ArrayOf<LookupRecord> + lookupX; /* Array of LookupRecords--in + * design order) */ + public: + DEFINE_SIZE_MIN (8); +}; + +struct ChainRuleSet +{ + inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).closure (c, lookup_context); + } + + inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const + { + TRACE_COLLECT_GLYPHS (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + { + TRACE_WOULD_APPLY (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + if ((this+rule[i]).would_apply (c, lookup_context)) + return_trace (true); + + return_trace (false); + } + + inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + if ((this+rule[i]).apply (c, lookup_context)) + return_trace (true); + + return_trace (false); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rule.sanitize (c, this)); + } + + protected: + OffsetArrayOf<ChainRule> + rule; /* Array of ChainRule tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, rule); +}; + +struct ChainContextFormat1 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + const Coverage &cov = (this+coverage); + + struct ChainContextClosureLookupContext lookup_context = { + {intersects_glyph}, + {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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_glyph}, + {NULL, NULL, NULL} + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + (this+ruleSet[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; + struct ChainContextApplyLookupContext lookup_context = { + {match_glyph}, + {NULL, NULL, NULL} + }; + return_trace (rule_set.would_apply (c, lookup_context)); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const ChainRuleSet &rule_set = this+ruleSet[index]; + struct ChainContextApplyLookupContext lookup_context = { + {match_glyph}, + {NULL, NULL, NULL} + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + OffsetArrayOf<ChainRuleSet> + ruleSet; /* Array of ChainRuleSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, ruleSet); +}; + +struct ChainContextFormat2 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + 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}, + {&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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + (this+coverage).add_coverage (c->input); + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_class}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + (this+ruleSet[i]).collect_glyphs (c, lookup_context); + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + unsigned int index = input_class_def.get_class (c->glyphs[0]); + const ChainRuleSet &rule_set = this+ruleSet[index]; + struct ChainContextApplyLookupContext lookup_context = { + {match_class}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + return_trace (rule_set.would_apply (c, lookup_context)); + } + + inline const Coverage &get_coverage (void) const + { + return this+coverage; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (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.get_class (c->buffer->cur().codepoint); + const ChainRuleSet &rule_set = this+ruleSet[index]; + struct ChainContextApplyLookupContext lookup_context = { + {match_class}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && + backtrackClassDef.sanitize (c, this) && + inputClassDef.sanitize (c, this) && + lookaheadClassDef.sanitize (c, this) && + ruleSet.sanitize (c, this)); + } + + protected: + USHORT format; /* Format identifier--format = 2 */ + OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + OffsetTo<ClassDef> + backtrackClassDef; /* Offset to glyph ClassDef table + * containing backtrack sequence + * data--from beginning of table */ + OffsetTo<ClassDef> + inputClassDef; /* Offset to glyph ClassDef + * table containing input sequence + * data--from beginning of table */ + OffsetTo<ClassDef> + lookaheadClassDef; /* Offset to glyph ClassDef table + * containing lookahead sequence + * data--from beginning of table */ + OffsetArrayOf<ChainRuleSet> + ruleSet; /* Array of ChainRuleSet tables + * ordered by class */ + public: + DEFINE_SIZE_ARRAY (12, ruleSet); +}; + +struct ChainContextFormat3 +{ + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (this); + 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}, + {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 void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + + (this+input[0]).add_coverage (c->input); + + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_coverage}, + {this, this, this} + }; + chain_context_collect_glyphs_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 would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + struct ChainContextApplyLookupContext lookup_context = { + {match_coverage}, + {this, this, this} + }; + return_trace (chain_context_would_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 const Coverage &get_coverage (void) const + { + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + return this+input[0]; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + + unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + struct ChainContextApplyLookupContext lookup_context = { + {match_coverage}, + {this, this, this} + }; + return_trace (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) const + { + TRACE_SANITIZE (this); + if (!backtrack.sanitize (c, this)) return_trace (false); + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + if (!input.sanitize (c, this)) return_trace (false); + if (!input.len) return_trace (false); /* To be consistent with Context. */ + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); + if (!lookahead.sanitize (c, this)) return_trace (false); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + return_trace (lookup.sanitize (c)); + } + + protected: + USHORT format; /* Format identifier--format = 3 */ + OffsetArrayOf<Coverage> + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + OffsetArrayOf<Coverage> + inputX ; /* Array of coverage + * tables in input sequence, in glyph + * sequence order */ + OffsetArrayOf<Coverage> + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + ArrayOf<LookupRecord> + lookupX; /* Array of LookupRecords--in + * design order) */ + public: + DEFINE_SIZE_MIN (10); +}; + +struct ChainContext +{ + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1)); + case 2: return_trace (c->dispatch (u.format2)); + case 3: return_trace (c->dispatch (u.format3)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + ChainContextFormat1 format1; + ChainContextFormat2 format2; + ChainContextFormat3 format3; + } u; +}; + + +template <typename T> +struct ExtensionFormat1 +{ + inline unsigned int get_type (void) const { return extensionLookupType; } + + template <typename X> + inline const X& get_subtable (void) const + { + unsigned int offset = extensionOffset; + if (unlikely (!offset)) return Null(typename T::LookupSubTable); + return StructAtOffset<typename T::LookupSubTable> (this, offset); + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, format); + if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ()); + return_trace (get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ())); + } + + /* This is called from may_dispatch() above with hb_sanitize_context_t. */ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && extensionOffset != 0); + } + + protected: + USHORT format; /* Format identifier. Set to 1. */ + USHORT extensionLookupType; /* Lookup type of subtable referenced + * by ExtensionOffset (i.e. the + * extension subtable). */ + ULONG extensionOffset; /* Offset to the extension subtable, + * of lookup type subtable. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +template <typename T> +struct Extension +{ + inline unsigned int get_type (void) const + { + switch (u.format) { + case 1: return u.format1.get_type (); + default:return 0; + } + } + template <typename X> + inline const X& get_subtable (void) const + { + switch (u.format) { + case 1: return u.format1.template get_subtable<typename T::LookupSubTable> (); + default:return Null(typename T::LookupSubTable); + } + } + + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (u.format1.dispatch (c)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + ExtensionFormat1<T> format1; + } u; +}; + + +/* + * GSUB/GPOS Common + */ + +struct GSUBGPOS +{ + static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB; + static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS; + + inline unsigned int get_script_count (void) const + { return (this+scriptList).len; } + inline const Tag& get_script_tag (unsigned int i) const + { return (this+scriptList).get_tag (i); } + inline unsigned int get_script_tags (unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) const + { return (this+scriptList).get_tags (start_offset, script_count, script_tags); } + inline const Script& get_script (unsigned int i) const + { return (this+scriptList)[i]; } + inline bool find_script_index (hb_tag_t tag, unsigned int *index) const + { return (this+scriptList).find_index (tag, index); } + + inline unsigned int get_feature_count (void) const + { return (this+featureList).len; } + inline hb_tag_t get_feature_tag (unsigned int i) const + { return i == Index::NOT_FOUND_INDEX ? HB_TAG_NONE : (this+featureList).get_tag (i); } + inline unsigned int get_feature_tags (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) const + { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); } + inline const Feature& get_feature (unsigned int i) const + { return (this+featureList)[i]; } + inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const + { return (this+featureList).find_index (tag, index); } + + inline unsigned int get_lookup_count (void) const + { return (this+lookupList).len; } + inline const Lookup& get_lookup (unsigned int i) const + { return (this+lookupList)[i]; } + + inline bool find_variations_index (const int *coords, unsigned int num_coords, + unsigned int *index) const + { return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations)) + .find_index (coords, num_coords, index); } + inline const Feature& get_feature_variation (unsigned int feature_index, + unsigned int variations_index) const + { + if (FeatureVariations::NOT_FOUND_INDEX != variations_index && + version.to_int () >= 0x00010001u) + { + const Feature *feature = (this+featureVars).find_substitute (variations_index, + feature_index); + if (feature) + return *feature; + } + return get_feature (feature_index); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + scriptList.sanitize (c, this) && + featureList.sanitize (c, this) && + lookupList.sanitize (c, this) && + (version.to_int () < 0x00010001u || featureVars.sanitize (c, this))); + } + + protected: + FixedVersion<>version; /* Version of the GSUB/GPOS table--initially set + * to 0x00010000u */ + OffsetTo<ScriptList> + scriptList; /* ScriptList table */ + OffsetTo<FeatureList> + featureList; /* FeatureList table */ + OffsetTo<LookupList> + lookupList; /* LookupList table */ + OffsetTo<FeatureVariations, ULONG> + featureVars; /* Offset to Feature Variations + table--from beginning of table + * (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (10); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh new file mode 100644 index 000000000..c3068491b --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh @@ -0,0 +1,234 @@ +/* + * Copyright © 2013 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_LAYOUT_JSTF_TABLE_HH +#define HB_OT_LAYOUT_JSTF_TABLE_HH + +#include "hb-open-type-private.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +/* + * JstfModList -- Justification Modification List Tables + */ + +typedef IndexArray JstfModList; + + +/* + * JstfMax -- Justification Maximum Table + */ + +typedef OffsetListOf<PosLookup> JstfMax; + + +/* + * JstfPriority -- Justification Priority Table + */ + +struct JstfPriority +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + shrinkageEnableGSUB.sanitize (c, this) && + shrinkageDisableGSUB.sanitize (c, this) && + shrinkageEnableGPOS.sanitize (c, this) && + shrinkageDisableGPOS.sanitize (c, this) && + shrinkageJstfMax.sanitize (c, this) && + extensionEnableGSUB.sanitize (c, this) && + extensionDisableGSUB.sanitize (c, this) && + extensionEnableGPOS.sanitize (c, this) && + extensionDisableGPOS.sanitize (c, this) && + extensionJstfMax.sanitize (c, this)); + } + + protected: + OffsetTo<JstfModList> + shrinkageEnableGSUB; /* Offset to Shrinkage Enable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + shrinkageDisableGSUB; /* Offset to Shrinkage Disable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + shrinkageEnableGPOS; /* Offset to Shrinkage Enable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + shrinkageDisableGPOS; /* Offset to Shrinkage Disable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfMax> + shrinkageJstfMax; /* Offset to Shrinkage JstfMax table-- + * from beginning of JstfPriority table + * --may be NULL */ + OffsetTo<JstfModList> + extensionEnableGSUB; /* Offset to Extension Enable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + extensionDisableGSUB; /* Offset to Extension Disable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + extensionEnableGPOS; /* Offset to Extension Enable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfModList> + extensionDisableGPOS; /* Offset to Extension Disable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + OffsetTo<JstfMax> + extensionJstfMax; /* Offset to Extension JstfMax table-- + * from beginning of JstfPriority table + * --may be NULL */ + + public: + DEFINE_SIZE_STATIC (20); +}; + + +/* + * JstfLangSys -- Justification Language System Table + */ + +struct JstfLangSys : OffsetListOf<JstfPriority> +{ + inline bool sanitize (hb_sanitize_context_t *c, + const Record<JstfLangSys>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); + return_trace (OffsetListOf<JstfPriority>::sanitize (c)); + } +}; + + +/* + * ExtenderGlyphs -- Extender Glyph Table + */ + +typedef SortedArrayOf<GlyphID> ExtenderGlyphs; + + +/* + * JstfScript -- The Justification Table + */ + +struct JstfScript +{ + inline unsigned int get_lang_sys_count (void) const + { return langSys.len; } + inline const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + inline unsigned int get_lang_sys_tags (unsigned int start_offset, + unsigned int *lang_sys_count /* IN/OUT */, + hb_tag_t *lang_sys_tags /* OUT */) const + { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } + inline const JstfLangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } + inline const JstfLangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record<JstfScript>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); + return_trace (extenderGlyphs.sanitize (c, this) && + defaultLangSys.sanitize (c, this) && + langSys.sanitize (c, this)); + } + + protected: + OffsetTo<ExtenderGlyphs> + extenderGlyphs; /* Offset to ExtenderGlyph table--from beginning + * of JstfScript table-may be NULL */ + OffsetTo<JstfLangSys> + defaultLangSys; /* Offset to DefaultJstfLangSys table--from + * beginning of JstfScript table--may be Null */ + RecordArrayOf<JstfLangSys> + langSys; /* Array of JstfLangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY (6, langSys); +}; + + +/* + * JSTF -- The Justification Table + */ + +struct JSTF +{ + static const hb_tag_t tableTag = HB_OT_TAG_JSTF; + + inline unsigned int get_script_count (void) const + { return scriptList.len; } + inline const Tag& get_script_tag (unsigned int i) const + { return scriptList.get_tag (i); } + inline unsigned int get_script_tags (unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) const + { return scriptList.get_tags (start_offset, script_count, script_tags); } + inline const JstfScript& get_script (unsigned int i) const + { return this+scriptList[i].offset; } + inline bool find_script_index (hb_tag_t tag, unsigned int *index) const + { return scriptList.find_index (tag, index); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + scriptList.sanitize (c, this)); + } + + protected: + FixedVersion<>version; /* Version of the JSTF table--initially set + * to 0x00010000u */ + RecordArrayOf<JstfScript> + scriptList; /* Array of JstfScripts--listed + * alphabetically by ScriptTag */ + public: + DEFINE_SIZE_ARRAY (6, scriptList); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_JSTF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-math-table.hh b/gfx/harfbuzz/src/hb-ot-layout-math-table.hh new file mode 100644 index 000000000..b52b1215d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-math-table.hh @@ -0,0 +1,722 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#ifndef HB_OT_LAYOUT_MATH_TABLE_HH +#define HB_OT_LAYOUT_MATH_TABLE_HH + +#include "hb-open-type-private.hh" +#include "hb-ot-layout-common-private.hh" +#include "hb-ot-math.h" + +namespace OT { + + +struct MathValueRecord +{ + inline hb_position_t get_x_value (hb_font_t *font, const void *base) const + { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); } + inline hb_position_t get_y_value (hb_font_t *font, const void *base) const + { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, base)); + } + + protected: + SHORT value; /* The X or Y value in design units */ + OffsetTo<Device> deviceTable; /* Offset to the device table - from the + * beginning of parent table. May be NULL. + * Suggested format for device table is 1. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct MathConstants +{ + inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int count = ARRAY_LENGTH (mathValueRecords); + for (unsigned int i = 0; i < count; i++) + if (!mathValueRecords[i].sanitize (c, this)) + return_trace (false); + + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && sanitize_math_value_records(c)); + } + + inline hb_position_t get_value (hb_ot_math_constant_t constant, + hb_font_t *font) const + { + switch (constant) { + + case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: + case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: + return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN]; + + case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: + case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: + return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]); + + case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: + case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: + case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: + case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: + return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value(font, this); + + case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: + case HB_OT_MATH_CONSTANT_AXIS_HEIGHT: + case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: + case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: + case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: + case HB_OT_MATH_CONSTANT_MATH_LEADING: + case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: + case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: + case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_STACK_GAP_MIN: + case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: + case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: + case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: + case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: + case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: + case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: + return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value(font, this); + + case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: + return radicalDegreeBottomRaisePercent; + + default: + return 0; + } + } + + protected: + SHORT percentScaleDown[2]; + USHORT minHeight[2]; + MathValueRecord mathValueRecords[51]; + SHORT radicalDegreeBottomRaisePercent; + + public: + DEFINE_SIZE_STATIC (214); +}; + +struct MathItalicsCorrectionInfo +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + italicsCorrection.sanitize (c, this)); + } + + inline hb_position_t get_value (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int index = (this+coverage).get_coverage (glyph); + return italicsCorrection[index].get_x_value (font, this); + } + + protected: + OffsetTo<Coverage> coverage; /* Offset to Coverage table - + * from the beginning of + * MathItalicsCorrectionInfo + * table. */ + ArrayOf<MathValueRecord> italicsCorrection; /* Array of MathValueRecords + * defining italics correction + * values for each + * covered glyph. */ + + public: + DEFINE_SIZE_ARRAY (4, italicsCorrection); +}; + +struct MathTopAccentAttachment +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + topAccentCoverage.sanitize (c, this) && + topAccentAttachment.sanitize (c, this)); + } + + inline hb_position_t get_value (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int index = (this+topAccentCoverage).get_coverage (glyph); + if (index == NOT_COVERED) + return font->get_glyph_h_advance (glyph) / 2; + return topAccentAttachment[index].get_x_value(font, this); + } + + protected: + OffsetTo<Coverage> topAccentCoverage; /* Offset to Coverage table - + * from the beginning of + * MathTopAccentAttachment + * table. */ + ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords + * defining top accent + * attachment points for each + * covered glyph. */ + + public: + DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment); +}; + +struct MathKern +{ + inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + unsigned int count = 2 * heightCount + 1; + for (unsigned int i = 0; i < count; i++) + if (!mathValueRecords[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_array (mathValueRecords, + mathValueRecords[0].static_size, + 2 * heightCount + 1) && + sanitize_math_value_records (c)); + } + + inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const + { + const MathValueRecord* correctionHeight = mathValueRecords; + const MathValueRecord* kernValue = mathValueRecords + heightCount; + int sign = font->y_scale < 0 ? -1 : +1; + + /* The description of the MathKern table is a ambiguous, but interpreting + * "between the two heights found at those indexes" for 0 < i < len as + * + * correctionHeight[i-1] < correction_height <= correctionHeight[i] + * + * makes the result consistent with the limit cases and we can just use the + * binary search algorithm of std::upper_bound: + */ + unsigned int i = 0; + unsigned int count = heightCount; + while (count > 0) + { + unsigned int half = count / 2; + hb_position_t height = correctionHeight[i + half].get_y_value(font, this); + if (sign * height < sign * correction_height) + { + i += half + 1; + count -= half + 1; + } else + count = half; + } + return kernValue[i].get_x_value(font, this); + } + + protected: + USHORT heightCount; + MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at + * which the kern value changes. + * Sorted by the height value in + * design units (heightCount entries), + * Followed by: + * Array of kern values corresponding + * to heights. (heightCount+1 entries). + */ + + public: + DEFINE_SIZE_ARRAY (2, mathValueRecords); +}; + +struct MathKernInfoRecord +{ + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + + unsigned int count = ARRAY_LENGTH (mathKern); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!mathKern[i].sanitize (c, base))) + return_trace (false); + + return_trace (true); + } + + inline hb_position_t get_kerning (hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font, + const void *base) const + { + unsigned int idx = kern; + if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0; + return (base+mathKern[idx]).get_value (correction_height, font); + } + + protected: + /* Offset to MathKern table for each corner - + * from the beginning of MathKernInfo table. May be NULL. */ + OffsetTo<MathKern> mathKern[4]; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct MathKernInfo +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mathKernCoverage.sanitize (c, this) && + mathKernInfoRecords.sanitize (c, this)); + } + + inline hb_position_t get_kerning (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font) const + { + unsigned int index = (this+mathKernCoverage).get_coverage (glyph); + return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this); + } + + protected: + OffsetTo<Coverage> mathKernCoverage; /* Offset to Coverage table - + * from the beginning of the + * MathKernInfo table. */ + ArrayOf<MathKernInfoRecord> mathKernInfoRecords; /* Array of + * MathKernInfoRecords, + * per-glyph information for + * mathematical positioning + * of subscripts and + * superscripts. */ + + public: + DEFINE_SIZE_ARRAY (4, mathKernInfoRecords); +}; + +struct MathGlyphInfo +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mathItalicsCorrectionInfo.sanitize (c, this) && + mathTopAccentAttachment.sanitize (c, this) && + extendedShapeCoverage.sanitize (c, this) && + mathKernInfo.sanitize(c, this)); + } + + inline hb_position_t + get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const + { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); } + + inline hb_position_t + get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const + { return (this+mathTopAccentAttachment).get_value (glyph, font); } + + inline bool is_extended_shape (hb_codepoint_t glyph) const + { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; } + + inline hb_position_t get_kerning (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font) const + { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); } + + protected: + /* Offset to MathItalicsCorrectionInfo table - + * from the beginning of MathGlyphInfo table. */ + OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo; + + /* Offset to MathTopAccentAttachment table - + * from the beginning of MathGlyphInfo table. */ + OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment; + + /* Offset to coverage table for Extended Shape glyphs - + * from the beginning of MathGlyphInfo table. When the left or right glyph of + * a box is an extended shape variant, the (ink) box (and not the default + * position defined by values in MathConstants table) should be used for + * vertical positioning purposes. May be NULL.. */ + OffsetTo<Coverage> extendedShapeCoverage; + + /* Offset to MathKernInfo table - + * from the beginning of MathGlyphInfo table. */ + OffsetTo<MathKernInfo> mathKernInfo; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct MathGlyphVariantRecord +{ + friend struct MathGlyphConstruction; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + GlyphID variantGlyph; /* Glyph ID for the variant. */ + USHORT advanceMeasurement; /* Advance width/height, in design units, of the + * variant, in the direction of requested + * glyph extension. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct PartFlags : USHORT +{ + enum Flags { + Extender = 0x0001u, /* If set, the part can be skipped or repeated. */ + + Defined = 0x0001u, /* All defined flags. */ + }; + + public: + DEFINE_SIZE_STATIC (2); +}; + +struct MathGlyphPartRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void extract (hb_ot_math_glyph_part_t &out, + int scale, + hb_font_t *font) const + { + out.glyph = glyph; + + out.start_connector_length = font->em_scale (startConnectorLength, scale); + out.end_connector_length = font->em_scale (endConnectorLength, scale); + out.full_advance = font->em_scale (fullAdvance, scale); + + ASSERT_STATIC ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER == + (unsigned int) PartFlags::Extender); + + out.flags = (hb_ot_math_glyph_part_flags_t) + (unsigned int) + (partFlags & PartFlags::Defined); + } + + protected: + GlyphID glyph; /* Glyph ID for the part. */ + USHORT startConnectorLength; /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the beginning of the glyph, in the + * direction of the extension. */ + USHORT endConnectorLength; /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the end of the glyph, in the direction of + * the extension. */ + USHORT fullAdvance; /* Full advance width/height for this part, + * in the direction of the extension. + * In design units. */ + PartFlags partFlags; /* Part qualifiers. */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +struct MathGlyphAssembly +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + italicsCorrection.sanitize(c, this) && + partRecords.sanitize(c)); + } + + inline unsigned int get_parts (hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const + { + if (parts_count) + { + int scale = font->dir_scale (direction); + const MathGlyphPartRecord *arr = + partRecords.sub_array (start_offset, parts_count); + unsigned int count = *parts_count; + for (unsigned int i = 0; i < count; i++) + arr[i].extract (parts[i], scale, font); + } + + if (italics_correction) + *italics_correction = italicsCorrection.get_x_value (font, this); + + return partRecords.len; + } + + protected: + MathValueRecord italicsCorrection; /* Italics correction of this + * MathGlyphAssembly. Should not + * depend on the assembly size. */ + ArrayOf<MathGlyphPartRecord> partRecords; /* Array of part records, from + * left to right and bottom to + * top. */ + + public: + DEFINE_SIZE_ARRAY (6, partRecords); +}; + +struct MathGlyphConstruction +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + glyphAssembly.sanitize(c, this) && + mathGlyphVariantRecord.sanitize(c)); + } + + inline const MathGlyphAssembly &get_assembly (void) const + { return this+glyphAssembly; } + + inline unsigned int get_variants (hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) const + { + if (variants_count) + { + int scale = font->dir_scale (direction); + const MathGlyphVariantRecord *arr = + mathGlyphVariantRecord.sub_array (start_offset, variants_count); + unsigned int count = *variants_count; + for (unsigned int i = 0; i < count; i++) + { + variants[i].glyph = arr[i].variantGlyph; + variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale); + } + } + return mathGlyphVariantRecord.len; + } + + protected: + /* Offset to MathGlyphAssembly table for this shape - from the beginning of + MathGlyphConstruction table. May be NULL. */ + OffsetTo<MathGlyphAssembly> glyphAssembly; + + /* MathGlyphVariantRecords for alternative variants of the glyphs. */ + ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord; + + public: + DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord); +}; + +struct MathVariants +{ + inline bool sanitize_offsets (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + unsigned int count = vertGlyphCount + horizGlyphCount; + for (unsigned int i = 0; i < count; i++) + if (!glyphConstruction[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + vertGlyphCoverage.sanitize (c, this) && + horizGlyphCoverage.sanitize (c, this) && + c->check_array (glyphConstruction, + glyphConstruction[0].static_size, + vertGlyphCount + horizGlyphCount) && + sanitize_offsets (c)); + } + + inline hb_position_t get_min_connector_overlap (hb_direction_t direction, + hb_font_t *font) const + { return font->em_scale_dir (minConnectorOverlap, direction); } + + inline unsigned int get_glyph_variants (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) const + { return get_glyph_construction (glyph, direction, font) + .get_variants (direction, font, start_offset, variants_count, variants); } + + inline unsigned int get_glyph_parts (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const + { return get_glyph_construction (glyph, direction, font) + .get_assembly () + .get_parts (direction, font, + start_offset, parts_count, parts, + italics_correction); } + + private: + inline const MathGlyphConstruction & + get_glyph_construction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font) const + { + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + unsigned int count = vertical ? vertGlyphCount : horizGlyphCount; + const OffsetTo<Coverage> &coverage = vertical ? vertGlyphCoverage + : horizGlyphCoverage; + + unsigned int index = (this+coverage).get_coverage (glyph); + if (unlikely (index >= count)) return Null(MathGlyphConstruction); + + if (!vertical) + index += vertGlyphCount; + + return this+glyphConstruction[index]; + } + + protected: + USHORT minConnectorOverlap; /* Minimum overlap of connecting + * glyphs during glyph construction, + * in design units. */ + OffsetTo<Coverage> vertGlyphCoverage; /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + OffsetTo<Coverage> horizGlyphCoverage; /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + USHORT vertGlyphCount; /* Number of glyphs for which + * information is provided for + * vertically growing variants. */ + USHORT horizGlyphCount; /* Number of glyphs for which + * information is provided for + * horizontally growing variants. */ + + /* Array of offsets to MathGlyphConstruction tables - from the beginning of + the MathVariants table, for shapes growing in vertical/horizontal + direction. */ + OffsetTo<MathGlyphConstruction> glyphConstruction[VAR]; + + public: + DEFINE_SIZE_ARRAY (10, glyphConstruction); +}; + + +/* + * MATH -- The MATH Table + */ + +struct MATH +{ + static const hb_tag_t tableTag = HB_OT_TAG_MATH; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + mathConstants.sanitize (c, this) && + mathGlyphInfo.sanitize (c, this) && + mathVariants.sanitize (c, this)); + } + + inline hb_position_t get_constant (hb_ot_math_constant_t constant, + hb_font_t *font) const + { return (this+mathConstants).get_value (constant, font); } + + inline const MathGlyphInfo &get_math_glyph_info (void) const + { return this+mathGlyphInfo; } + + inline const MathVariants &get_math_variants (void) const + { return this+mathVariants; } + + protected: + FixedVersion<>version; /* Version of the MATH table + * initially set to 0x00010000u */ + OffsetTo<MathConstants> mathConstants;/* MathConstants table */ + OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */ + OffsetTo<MathVariants> mathVariants; /* MathVariants table */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* mathspace OT */ + + +#endif /* HB_OT_LAYOUT_MATH_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-private.hh b/gfx/harfbuzz/src/hb-ot-layout-private.hh new file mode 100644 index 000000000..a4272de63 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh @@ -0,0 +1,621 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 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_PRIVATE_HH +#define HB_OT_LAYOUT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" +#include "hb-set-private.hh" + + +/* Private API corresponding to hb-ot-layout.h: */ + +HB_INTERNAL hb_bool_t +hb_ot_layout_table_find_feature (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t feature_tag, + unsigned int *feature_index); + + +/* + * GDEF + */ + +enum hb_ot_layout_glyph_props_flags_t +{ + /* The following three match LookupFlags::Ignore* numbers. */ + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH = 0x02u, + HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE = 0x04u, + HB_OT_LAYOUT_GLYPH_PROPS_MARK = 0x08u, + + /* The following are used internally; not derived from GDEF. */ + HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED = 0x10u, + HB_OT_LAYOUT_GLYPH_PROPS_LIGATED = 0x20u, + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED = 0x40u, + + HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE = HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED | + HB_OT_LAYOUT_GLYPH_PROPS_LIGATED | + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED +}; +HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t); + + +/* + * GSUB/GPOS + */ + +HB_INTERNAL hb_bool_t +hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context); + + +/* Should be called before all the substitute_lookup's are done. */ +HB_INTERNAL void +hb_ot_layout_substitute_start (hb_font_t *font, + hb_buffer_t *buffer); + + +struct hb_ot_layout_lookup_accelerator_t; + +namespace OT { + struct hb_apply_context_t; + struct SubstLookup; +} + +HB_INTERNAL void +hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, + const OT::SubstLookup &lookup, + const hb_ot_layout_lookup_accelerator_t &accel); + + +/* Should be called before all the position_lookup's are done. */ +HB_INTERNAL void +hb_ot_layout_position_start (hb_font_t *font, + hb_buffer_t *buffer); + +/* Should be called after all the position_lookup's are done, to finish advances. */ +HB_INTERNAL void +hb_ot_layout_position_finish_advances (hb_font_t *font, + hb_buffer_t *buffer); + +/* Should be called after hb_ot_layout_position_finish_advances, to finish offsets. */ +HB_INTERNAL void +hb_ot_layout_position_finish_offsets (hb_font_t *font, + hb_buffer_t *buffer); + + + +/* + * hb_ot_layout_t + */ + +namespace OT { + struct GDEF; + struct GSUB; + struct GPOS; + struct MATH; +} + +struct hb_ot_layout_lookup_accelerator_t +{ + template <typename TLookup> + inline void init (const TLookup &lookup) + { + digest.init (); + lookup.add_coverage (&digest); + } + + inline void fini (void) + { + } + + inline bool may_have (hb_codepoint_t g) const { + return digest.may_have (g); + } + + private: + hb_set_digest_t digest; +}; + +struct hb_ot_layout_t +{ + hb_blob_t *gdef_blob; + hb_blob_t *gsub_blob; + hb_blob_t *gpos_blob; + hb_blob_t *math_blob; + + const struct OT::GDEF *gdef; + const struct OT::GSUB *gsub; + const struct OT::GPOS *gpos; + const struct OT::MATH *math; + + unsigned int gsub_lookup_count; + unsigned int gpos_lookup_count; + + hb_ot_layout_lookup_accelerator_t *gsub_accels; + hb_ot_layout_lookup_accelerator_t *gpos_accels; +}; + + +HB_INTERNAL hb_ot_layout_t * +_hb_ot_layout_create (hb_face_t *face); + +HB_INTERNAL void +_hb_ot_layout_destroy (hb_ot_layout_t *layout); + + +#define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot) + + +/* + * Buffer var routines. + */ + +/* buffer var allocations, used during the entire shaping process */ +#define unicode_props() var2.u16[0] + +/* buffer var allocations, used during the GSUB/GPOS processing */ +#define glyph_props() var1.u16[0] /* GDEF glyph properties */ +#define lig_props() var1.u8[2] /* GSUB/GPOS ligature tracking */ +#define syllable() var1.u8[3] /* GSUB/GPOS shaping boundaries */ + + +/* loop over syllables */ + +#define foreach_syllable(buffer, start, end) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? _next_syllable (buffer, 0) : 0; \ + start < _count; \ + start = end, end = _next_syllable (buffer, start)) + +static inline unsigned int +_next_syllable (hb_buffer_t *buffer, unsigned int start) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + + unsigned int syllable = info[start].syllable(); + while (++start < count && syllable == info[start].syllable()) + ; + + return start; +} + + +/* unicode_props */ + +/* Design: + * unicode_props() is a two-byte number. The low byte includes: + * - General_Category: 5 bits. + * - A bit each for: + * * Is it Default_Ignorable(); we have a modified Default_Ignorable(). + * * Whether it's one of the three Mongolian Free Variation Selectors. + * * One free bit right now. + * + * The high-byte has different meanings, switched by the Gen-Cat: + * - For Mn,Mc,Me: the modified Combining_Class. + * - For Cf: whether it's ZWJ, ZWNJ, or something else. + * - For Ws: index of which space character this is, if space fallback + * is needed, ie. we don't set this by default, only if asked to. + */ + +enum hb_unicode_props_flags_t { + UPROPS_MASK_GEN_CAT = 0x001Fu, + UPROPS_MASK_IGNORABLE = 0x0020u, + UPROPS_MASK_FVS = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3 */ + + /* If GEN_CAT=FORMAT, top byte masks: */ + UPROPS_MASK_Cf_ZWJ = 0x0100u, + UPROPS_MASK_Cf_ZWNJ = 0x0200u +}; +HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t); + +static inline void +_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) +{ + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int u = info->codepoint; + unsigned int gen_cat = (unsigned int) unicode->general_category (u); + unsigned int props = gen_cat; + + if (u >= 0x80) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; + if (unlikely (unicode->is_default_ignorable (u))) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES; + props |= UPROPS_MASK_IGNORABLE; + if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ; + if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ; + /* Mongolian Free Variation Selectors need to be remembered + * because although we need to hide them like default-ignorables, + * they need to non-ignorable during shaping. This is similar to + * what we do for joiners in Indic-like shapers, but since the + * FVSes are GC=Mn, we have use a separate bit to remember them. + * Fixes: + * https://github.com/behdad/harfbuzz/issues/234 + */ + if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_FVS; + } + else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL (gen_cat))) + { + /* The above check is just an optimization to let in only things we need further + * processing on. */ + + /* Only Mn and Mc can have non-zero ccc: + * http://www.unicode.org/policies/stability_policy.html#Property_Value + * """ + * Canonical_Combining_Class, General_Category + * All characters other than those with General_Category property values + * Spacing_Mark (Mc) and Nonspacing_Mark (Mn) have the Canonical_Combining_Class + * property value 0. + * 1.1.5+ + * """ + * + * Also, all Mn's that are Default_Ignorable, have ccc=0, hence + * the "else if". + */ + props |= unicode->modified_combining_class (info->codepoint)<<8; + + /* Recategorize emoji skin-tone modifiers as Unicode mark, so they + * behave correctly in non-native directionality. They originally + * are MODIFIER_SYMBOL. Fixes: + * https://github.com/behdad/harfbuzz/issues/169 + */ + if (unlikely (hb_in_range (u, 0x1F3FBu, 0x1F3FFu))) + { + props = gen_cat = HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + } + } + } + + info->unicode_props() = props; +} + +static inline void +_hb_glyph_info_set_general_category (hb_glyph_info_t *info, + hb_unicode_general_category_t gen_cat) +{ + /* Clears top-byte. */ + info->unicode_props() = (unsigned int) gen_cat | (info->unicode_props() & (0xFF & ~UPROPS_MASK_GEN_CAT)); +} + +static inline hb_unicode_general_category_t +_hb_glyph_info_get_general_category (const hb_glyph_info_t *info) +{ + return (hb_unicode_general_category_t) (info->unicode_props() & UPROPS_MASK_GEN_CAT); +} + +static inline bool +_hb_glyph_info_is_unicode_mark (const hb_glyph_info_t *info) +{ + return HB_UNICODE_GENERAL_CATEGORY_IS_MARK (info->unicode_props() & UPROPS_MASK_GEN_CAT); +} +static inline void +_hb_glyph_info_set_modified_combining_class (hb_glyph_info_t *info, + unsigned int modified_class) +{ + if (unlikely (!_hb_glyph_info_is_unicode_mark (info))) + return; + info->unicode_props() = (modified_class<<8) | (info->unicode_props() & 0xFF); +} +static inline unsigned int +_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0; +} + +static inline bool +_hb_glyph_info_is_unicode_space (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_get_general_category (info) == + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; +} +static inline void +_hb_glyph_info_set_unicode_space_fallback_type (hb_glyph_info_t *info, hb_unicode_funcs_t::space_t s) +{ + if (unlikely (!_hb_glyph_info_is_unicode_space (info))) + return; + info->unicode_props() = (((unsigned int) s)<<8) | (info->unicode_props() & 0xFF); +} +static inline hb_unicode_funcs_t::space_t +_hb_glyph_info_get_unicode_space_fallback_type (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_space (info) ? + (hb_unicode_funcs_t::space_t) (info->unicode_props()>>8) : + hb_unicode_funcs_t::NOT_SPACE; +} + +static inline bool _hb_glyph_info_ligated (const hb_glyph_info_t *info); + +static inline hb_bool_t +_hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info) +{ + return (info->unicode_props() & UPROPS_MASK_IGNORABLE) && + !_hb_glyph_info_ligated (info); +} +static inline hb_bool_t +_hb_glyph_info_is_default_ignorable_and_not_fvs (const hb_glyph_info_t *info) +{ + return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_FVS)) + == UPROPS_MASK_IGNORABLE) && + !_hb_glyph_info_ligated (info); +} + +static inline bool +_hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_get_general_category (info) == + HB_UNICODE_GENERAL_CATEGORY_FORMAT; +} +static inline hb_bool_t +_hb_glyph_info_is_zwnj (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWNJ); +} +static inline hb_bool_t +_hb_glyph_info_is_zwj (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ); +} +static inline hb_bool_t +_hb_glyph_info_is_joiner (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ)); +} +static inline void +_hb_glyph_info_flip_joiners (hb_glyph_info_t *info) +{ + if (!_hb_glyph_info_is_unicode_format (info)) + return; + info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; +} + +/* lig_props: aka lig_id / lig_comp + * + * When a ligature is formed: + * + * - The ligature glyph and any marks in between all the same newly allocated + * lig_id, + * - The ligature glyph will get lig_num_comps set to the number of components + * - The marks get lig_comp > 0, reflecting which component of the ligature + * they were applied to. + * - This is used in GPOS to attach marks to the right component of a ligature + * in MarkLigPos, + * - Note that when marks are ligated together, much of the above is skipped + * and the current lig_id reused. + * + * When a multiple-substitution is done: + * + * - All resulting glyphs will have lig_id = 0, + * - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively. + * - This is used in GPOS to attach marks to the first component of a + * multiple substitution in MarkBasePos. + * + * The numbers are also used in GPOS to do mark-to-mark positioning only + * to marks that belong to the same component of the same ligature. + */ + +static inline void +_hb_glyph_info_clear_lig_props (hb_glyph_info_t *info) +{ + info->lig_props() = 0; +} + +#define IS_LIG_BASE 0x10 + +static inline void +_hb_glyph_info_set_lig_props_for_ligature (hb_glyph_info_t *info, + unsigned int lig_id, + unsigned int lig_num_comps) +{ + info->lig_props() = (lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F); +} + +static inline void +_hb_glyph_info_set_lig_props_for_mark (hb_glyph_info_t *info, + unsigned int lig_id, + unsigned int lig_comp) +{ + info->lig_props() = (lig_id << 5) | (lig_comp & 0x0F); +} + +static inline void +_hb_glyph_info_set_lig_props_for_component (hb_glyph_info_t *info, unsigned int comp) +{ + _hb_glyph_info_set_lig_props_for_mark (info, 0, comp); +} + +static inline unsigned int +_hb_glyph_info_get_lig_id (const hb_glyph_info_t *info) +{ + return info->lig_props() >> 5; +} + +static inline bool +_hb_glyph_info_ligated_internal (const hb_glyph_info_t *info) +{ + return !!(info->lig_props() & IS_LIG_BASE); +} + +static inline unsigned int +_hb_glyph_info_get_lig_comp (const hb_glyph_info_t *info) +{ + if (_hb_glyph_info_ligated_internal (info)) + return 0; + else + return info->lig_props() & 0x0F; +} + +static inline unsigned int +_hb_glyph_info_get_lig_num_comps (const hb_glyph_info_t *info) +{ + if ((info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE) && + _hb_glyph_info_ligated_internal (info)) + return info->lig_props() & 0x0F; + else + return 1; +} + +static inline uint8_t +_hb_allocate_lig_id (hb_buffer_t *buffer) { + uint8_t lig_id = buffer->next_serial () & 0x07; + if (unlikely (!lig_id)) + lig_id = _hb_allocate_lig_id (buffer); /* in case of overflow */ + return lig_id; +} + +/* glyph_props: */ + +static inline void +_hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props) +{ + info->glyph_props() = props; +} + +static inline unsigned int +_hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info) +{ + return info->glyph_props(); +} + +static inline bool +_hb_glyph_info_is_base_glyph (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH); +} + +static inline bool +_hb_glyph_info_is_ligature (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE); +} + +static inline bool +_hb_glyph_info_is_mark (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK); +} + +static inline bool +_hb_glyph_info_substituted (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); +} + +static inline bool +_hb_glyph_info_ligated (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED); +} + +static inline bool +_hb_glyph_info_multiplied (const hb_glyph_info_t *info) +{ + return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED); +} + +static inline bool +_hb_glyph_info_ligated_and_didnt_multiply (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_ligated (info) && !_hb_glyph_info_multiplied (info); +} + +static inline void +_hb_glyph_info_clear_ligated_and_multiplied (hb_glyph_info_t *info) +{ + info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_LIGATED | + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED); +} + +static inline void +_hb_glyph_info_clear_substituted (hb_glyph_info_t *info) +{ + info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); +} + + +/* Allocation / deallocation. */ + +static inline void +_hb_buffer_allocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_deallocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_assert_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ASSERT_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_allocate_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props); + HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); +} + +static inline void +_hb_buffer_deallocate_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); + HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props); + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props); +} + +static inline void +_hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ASSERT_VAR (buffer, glyph_props); + HB_BUFFER_ASSERT_VAR (buffer, lig_props); + HB_BUFFER_ASSERT_VAR (buffer, syllable); +} + +/* Make sure no one directly touches our props... */ +#undef unicode_props0 +#undef unicode_props1 +#undef lig_props +#undef glyph_props + + +#endif /* HB_OT_LAYOUT_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout.cc b/gfx/harfbuzz/src/hb-ot-layout.cc new file mode 100644 index 000000000..f5a4988a6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -0,0 +1,1237 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2006 Behdad Esfahbod + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-open-type-private.hh" +#include "hb-ot-layout-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-layout-jstf-table.hh" + +#include "hb-ot-map-private.hh" + +#include <stdlib.h> +#include <string.h> + + +HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) + +hb_ot_layout_t * +_hb_ot_layout_create (hb_face_t *face) +{ + hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); + if (unlikely (!layout)) + return NULL; + + layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); + layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); + + layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); + layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); + + layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); + layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); + + /* The MATH table is rarely used, so only try and load it in _get_math. */ + layout->math_blob = NULL; + layout->math = NULL; + + { + /* + * The ugly business of blacklisting individual fonts' tables happen here! + * See this thread for why we finally had to bend in and do this: + * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html + */ + unsigned int gdef_len = hb_blob_get_length (layout->gdef_blob); + unsigned int gsub_len = hb_blob_get_length (layout->gsub_blob); + unsigned int gpos_len = hb_blob_get_length (layout->gpos_blob); + if (0 + /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ + || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len) + /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ + || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len) + /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ + || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len) + /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ + || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len) + /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ + || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len) + /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ + || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len) + ) + { + /* In certain versions of Times New Roman Italic and Bold Italic, + * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong + * glyph class 3 (mark) in GDEF. Nuke the GDEF to avoid zero-width + * double-quote. See: + * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html + */ + if (3 == layout->gdef->get_glyph_class (5)) + layout->gdef = &OT::Null(OT::GDEF); + } + else if (0 + /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ + || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len) + /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ + || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len) + /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ + || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len) + /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ + || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len) + /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ + || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len) + /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ + || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len) + /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ + || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len) + /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ + || (1018 == gdef_len && 62834 == gpos_len && 24572 == gsub_len) + /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ + || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len) + /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ + || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len) + /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ + || (180 == gdef_len && 7254 == gpos_len && 13054 == gsub_len) + /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ + || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len) + /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ + || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len) + /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ + /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ + || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len) + /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ + /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ + || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len) + /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ + || (1046 == gdef_len && 17112 == gpos_len && 71788 == gsub_len) + /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ + || (1058 == gdef_len && 17514 == gpos_len && 71794 == gsub_len) + /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ + || (1330 == gdef_len && 57938 == gpos_len && 109904 == gsub_len) + /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ + || (1330 == gdef_len && 58972 == gpos_len && 109904 == gsub_len) + ) + { + /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks + * such as certain IPA symbols as glyph class 3. So do older versions of Microsoft Himalaya, + * and the version of Cantarell shipped by Ubuntu 16.04. + * Nuke the GDEF tables of these fonts to avoid unwanted width-zeroing. + * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 + * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 + * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 + */ + layout->gdef = &OT::Null(OT::GDEF); + } + } + + layout->gsub_lookup_count = layout->gsub->get_lookup_count (); + layout->gpos_lookup_count = layout->gpos->get_lookup_count (); + + layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); + layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); + + if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || + (layout->gpos_lookup_count && !layout->gpos_accels))) + { + _hb_ot_layout_destroy (layout); + return NULL; + } + + for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) + layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); + for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) + layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); + + return layout; +} + +void +_hb_ot_layout_destroy (hb_ot_layout_t *layout) +{ + for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) + layout->gsub_accels[i].fini (); + for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) + layout->gpos_accels[i].fini (); + + free (layout->gsub_accels); + free (layout->gpos_accels); + + hb_blob_destroy (layout->gdef_blob); + hb_blob_destroy (layout->gsub_blob); + hb_blob_destroy (layout->gpos_blob); + hb_blob_destroy (layout->math_blob); + + free (layout); +} + +static inline const OT::GDEF& +_get_gdef (hb_face_t *face) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); + return *hb_ot_layout_from_face (face)->gdef; +} +static inline const OT::GSUB& +_get_gsub (hb_face_t *face) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); + return *hb_ot_layout_from_face (face)->gsub; +} +static inline const OT::GPOS& +_get_gpos (hb_face_t *face) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); + return *hb_ot_layout_from_face (face)->gpos; +} + +/* + * GDEF + */ + +hb_bool_t +hb_ot_layout_has_glyph_classes (hb_face_t *face) +{ + return _get_gdef (face).has_glyph_classes (); +} + +/** + * hb_ot_layout_get_glyph_class: + * + * Since: 0.9.7 + **/ +hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph) +{ + return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); +} + +/** + * hb_ot_layout_get_glyphs_in_class: + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, + hb_ot_layout_glyph_class_t klass, + hb_set_t *glyphs /* OUT */) +{ + return _get_gdef (face).get_glyphs_in_class (klass, glyphs); +} + +unsigned int +hb_ot_layout_get_attach_points (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) +{ + return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); +} + +unsigned int +hb_ot_layout_get_ligature_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + int *caret_array /* OUT */) +{ + return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); +} + + +/* + * GSUB/GPOS + */ + +static const OT::GSUBGPOS& +get_gsubgpos_table (hb_face_t *face, + hb_tag_t table_tag) +{ + switch (table_tag) { + case HB_OT_TAG_GSUB: return _get_gsub (face); + case HB_OT_TAG_GPOS: return _get_gpos (face); + default: return OT::Null(OT::GSUBGPOS); + } +} + + +unsigned int +hb_ot_layout_table_get_script_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.get_script_tags (start_offset, script_count, script_tags); +} + +#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') + +hb_bool_t +hb_ot_layout_table_find_script (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t script_tag, + unsigned int *script_index) +{ + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + if (g.find_script_index (script_tag, script_index)) + return true; + + /* try finding 'DFLT' */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) + return false; + + /* try with 'dflt'; MS site has had typos and many fonts use it now :(. + * including many versions of DejaVu Sans Mono! */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) + return false; + + /* try with 'latn'; some old fonts put their features there even though + they're really trying to support Thai, for example :( */ + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) + return false; + + if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; + return false; +} + +hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script) +{ + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + while (*script_tags) + { + if (g.find_script_index (*script_tags, script_index)) { + if (chosen_script) + *chosen_script = *script_tags; + return true; + } + script_tags++; + } + + /* try finding 'DFLT' */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { + if (chosen_script) + *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; + 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; + } + + /* try with 'latn'; some old fonts put their features there even though + they're really trying to support Thai, for example :( */ + 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; +} + +unsigned int +hb_ot_layout_table_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.get_feature_tags (start_offset, feature_count, feature_tags); +} + +hb_bool_t +hb_ot_layout_table_find_feature (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t feature_tag, + unsigned int *feature_index) +{ + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + unsigned int num_features = g.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) + { + if (feature_tag == g.get_feature_tag (i)) { + if (feature_index) *feature_index = i; + return true; + } + } + + if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; + return false; +} + + +unsigned int +hb_ot_layout_script_get_language_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int start_offset, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */) +{ + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + + return s.get_lang_sys_tags (start_offset, language_count, language_tags); +} + +hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index) +{ + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + + if (s.find_lang_sys_index (language_tag, language_index)) + return true; + + /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ + if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) + return false; + + if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; + return false; +} + +hb_bool_t +hb_ot_layout_language_get_required_feature_index (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index) +{ + return hb_ot_layout_language_get_required_feature (face, + table_tag, + script_index, + language_index, + feature_index, + NULL); +} + +/** + * hb_ot_layout_language_get_required_feature: + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_ot_layout_language_get_required_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index, + hb_tag_t *feature_tag) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + unsigned int index = l.get_required_feature_index (); + if (feature_index) *feature_index = index; + if (feature_tag) *feature_tag = g.get_feature_tag (index); + + return l.has_required_feature (); +} + +unsigned int +hb_ot_layout_language_get_feature_indexes (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + return l.get_feature_indexes (start_offset, feature_count, feature_indexes); +} + +unsigned int +hb_ot_layout_language_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); + unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); + + if (feature_tags) { + unsigned int count = *feature_count; + for (unsigned int i = 0; i < count; i++) + feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); + } + + return ret; +} + + +hb_bool_t +hb_ot_layout_language_find_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + hb_tag_t feature_tag, + unsigned int *feature_index) +{ + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + unsigned int num_features = l.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) { + unsigned int f_index = l.get_feature_index (i); + + if (feature_tag == g.get_feature_tag (f_index)) { + if (feature_index) *feature_index = f_index; + return true; + } + } + + if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; + return false; +} + +/** + * hb_ot_layout_feature_get_lookups: + * + * Since: 0.9.7 + **/ +unsigned int +hb_ot_layout_feature_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */) +{ + return hb_ot_layout_feature_with_variations_get_lookups (face, + table_tag, + feature_index, + HB_OT_LAYOUT_NO_VARIATIONS_INDEX, + start_offset, + lookup_count, + lookup_indexes); +} + +/** + * hb_ot_layout_table_get_lookup_count: + * + * Since: 0.9.22 + **/ +unsigned int +hb_ot_layout_table_get_lookup_count (hb_face_t *face, + hb_tag_t table_tag) +{ + switch (table_tag) + { + case HB_OT_TAG_GSUB: + { + return hb_ot_layout_from_face (face)->gsub_lookup_count; + } + case HB_OT_TAG_GPOS: + { + return hb_ot_layout_from_face (face)->gpos_lookup_count; + } + } + return 0; +} + +static void +_hb_ot_layout_collect_lookups_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_set_t *lookup_indexes /* OUT */) +{ + unsigned int lookup_indices[32]; + unsigned int offset, len; + + offset = 0; + do { + len = ARRAY_LENGTH (lookup_indices); + hb_ot_layout_feature_get_lookups (face, + table_tag, + feature_index, + offset, &len, + lookup_indices); + + for (unsigned int i = 0; i < len; i++) + lookup_indexes->add (lookup_indices[i]); + + offset += len; + } while (len == ARRAY_LENGTH (lookup_indices)); +} + +static void +_hb_ot_layout_collect_lookups_features (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */) +{ + if (!features) + { + unsigned int required_feature_index; + if (hb_ot_layout_language_get_required_feature (face, + table_tag, + script_index, + language_index, + &required_feature_index, + NULL)) + _hb_ot_layout_collect_lookups_lookups (face, + table_tag, + required_feature_index, + lookup_indexes); + + /* All features */ + unsigned int feature_indices[32]; + unsigned int offset, len; + + offset = 0; + do { + len = ARRAY_LENGTH (feature_indices); + hb_ot_layout_language_get_feature_indexes (face, + table_tag, + script_index, + language_index, + offset, &len, + feature_indices); + + for (unsigned int i = 0; i < len; i++) + _hb_ot_layout_collect_lookups_lookups (face, + table_tag, + feature_indices[i], + lookup_indexes); + + offset += len; + } while (len == ARRAY_LENGTH (feature_indices)); + } + else + { + for (; *features; features++) + { + unsigned int feature_index; + if (hb_ot_layout_language_find_feature (face, + table_tag, + script_index, + language_index, + *features, + &feature_index)) + _hb_ot_layout_collect_lookups_lookups (face, + table_tag, + feature_index, + lookup_indexes); + } + } +} + +static void +_hb_ot_layout_collect_lookups_languages (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */) +{ + _hb_ot_layout_collect_lookups_features (face, + table_tag, + script_index, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + features, + lookup_indexes); + + if (!languages) + { + /* All languages */ + unsigned int count = hb_ot_layout_script_get_language_tags (face, + table_tag, + script_index, + 0, NULL, NULL); + for (unsigned int language_index = 0; language_index < count; language_index++) + _hb_ot_layout_collect_lookups_features (face, + table_tag, + script_index, + language_index, + features, + lookup_indexes); + } + else + { + for (; *languages; languages++) + { + unsigned int language_index; + if (hb_ot_layout_script_find_language (face, + table_tag, + script_index, + *languages, + &language_index)) + _hb_ot_layout_collect_lookups_features (face, + table_tag, + script_index, + language_index, + features, + lookup_indexes); + } + } +} + +/** + * hb_ot_layout_collect_lookups: + * + * Since: 0.9.8 + **/ +void +hb_ot_layout_collect_lookups (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */) +{ + if (!scripts) + { + /* All scripts */ + unsigned int count = hb_ot_layout_table_get_script_tags (face, + table_tag, + 0, NULL, NULL); + for (unsigned int script_index = 0; script_index < count; script_index++) + _hb_ot_layout_collect_lookups_languages (face, + table_tag, + script_index, + languages, + features, + lookup_indexes); + } + else + { + for (; *scripts; scripts++) + { + unsigned int script_index; + if (hb_ot_layout_table_find_script (face, + table_tag, + *scripts, + &script_index)) + _hb_ot_layout_collect_lookups_languages (face, + table_tag, + script_index, + languages, + features, + lookup_indexes); + } + } +} + +/** + * hb_ot_layout_lookup_collect_glyphs: + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, + hb_tag_t table_tag, + unsigned int lookup_index, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output /* OUT. May be NULL */) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; + + OT::hb_collect_glyphs_context_t c (face, + glyphs_before, + glyphs_input, + glyphs_after, + glyphs_output); + + switch (table_tag) + { + case HB_OT_TAG_GSUB: + { + const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); + l.collect_glyphs (&c); + return; + } + case HB_OT_TAG_GPOS: + { + const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); + l.collect_glyphs (&c); + return; + } + } +} + + +/* Variations support */ + +hb_bool_t +hb_ot_layout_table_find_feature_variations (hb_face_t *face, + hb_tag_t table_tag, + const int *coords, + unsigned int num_coords, + unsigned int *variations_index /* out */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.find_variations_index (coords, num_coords, variations_index); +} + +unsigned int +hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int variations_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */) +{ + ASSERT_STATIC (OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + const OT::Feature &f = g.get_feature_variation (feature_index, variations_index); + + return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); +} + + +/* + * OT::GSUB + */ + +hb_bool_t +hb_ot_layout_has_substitution (hb_face_t *face) +{ + return &_get_gsub (face) != &OT::Null(OT::GSUB); +} + +/** + * hb_ot_layout_lookup_would_substitute: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_ot_layout_lookup_would_substitute (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; + return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); +} + +hb_bool_t +hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context) +{ + if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; + OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); + + const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); + + return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]); +} + +void +hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) +{ + OT::GSUB::substitute_start (font, buffer); +} + +/** + * hb_ot_layout_lookup_substitute_closure: + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_lookup_substitute_closure (hb_face_t *face, + unsigned int lookup_index, + hb_set_t *glyphs) +{ + OT::hb_closure_context_t c (face, glyphs); + + const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); + + l.closure (&c); +} + +/* + * OT::GPOS + */ + +hb_bool_t +hb_ot_layout_has_positioning (hb_face_t *face) +{ + return &_get_gpos (face) != &OT::Null(OT::GPOS); +} + +void +hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) +{ + OT::GPOS::position_start (font, buffer); +} + +void +hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) +{ + OT::GPOS::position_finish_advances (font, buffer); +} + +void +hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + OT::GPOS::position_finish_offsets (font, buffer); +} + +/** + * hb_ot_layout_get_size_params: + * + * Since: 0.9.10 + **/ +hb_bool_t +hb_ot_layout_get_size_params (hb_face_t *face, + unsigned int *design_size, /* OUT. May be NULL */ + unsigned int *subfamily_id, /* OUT. May be NULL */ + unsigned int *subfamily_name_id, /* OUT. May be NULL */ + unsigned int *range_start, /* OUT. May be NULL */ + unsigned int *range_end /* OUT. May be NULL */) +{ + const OT::GPOS &gpos = _get_gpos (face); + const hb_tag_t tag = HB_TAG ('s','i','z','e'); + + unsigned int num_features = gpos.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) + { + if (tag == gpos.get_feature_tag (i)) + { + const OT::Feature &f = gpos.get_feature (i); + const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); + + if (params.designSize) + { +#define PARAM(a, A) if (a) *a = params.A + PARAM (design_size, designSize); + PARAM (subfamily_id, subfamilyID); + PARAM (subfamily_name_id, subfamilyNameID); + PARAM (range_start, rangeStart); + PARAM (range_end, rangeEnd); +#undef PARAM + + return true; + } + } + } + +#define PARAM(a, A) if (a) *a = 0 + PARAM (design_size, designSize); + PARAM (subfamily_id, subfamilyID); + PARAM (subfamily_name_id, subfamilyNameID); + PARAM (range_start, rangeStart); + PARAM (range_end, rangeEnd); +#undef PARAM + + return false; +} + + +/* + * Parts of different types are implemented here such that they have direct + * access to GSUB/GPOS lookups. + */ + + +struct GSUBProxy +{ + static const unsigned int table_index = 0; + static const bool inplace = false; + typedef OT::SubstLookup Lookup; + + GSUBProxy (hb_face_t *face) : + table (*hb_ot_layout_from_face (face)->gsub), + accels (hb_ot_layout_from_face (face)->gsub_accels) {} + + const OT::GSUB &table; + const hb_ot_layout_lookup_accelerator_t *accels; +}; + +struct GPOSProxy +{ + static const unsigned int table_index = 1; + static const bool inplace = true; + typedef OT::PosLookup Lookup; + + GPOSProxy (hb_face_t *face) : + table (*hb_ot_layout_from_face (face)->gpos), + accels (hb_ot_layout_from_face (face)->gpos_accels) {} + + const OT::GPOS &table; + const hb_ot_layout_lookup_accelerator_t *accels; +}; + + +struct hb_get_subtables_context_t : + OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY> +{ + template <typename Type> + static inline bool apply_to (const void *obj, OT::hb_apply_context_t *c) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->apply (c); + } + + typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_apply_context_t *c); + + struct hb_applicable_t + { + inline void init (const void *obj_, hb_apply_func_t apply_func_) + { + obj = obj_; + apply_func = apply_func_; + } + + inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); } + + private: + const void *obj; + hb_apply_func_t apply_func; + }; + + typedef hb_auto_array_t<hb_applicable_t> array_t; + + /* Dispatch interface. */ + inline const char *get_name (void) { return "GET_SUBTABLES"; } + template <typename T> + inline return_t dispatch (const T &obj) + { + hb_applicable_t *entry = array.push(); + if (likely (entry)) + entry->init (&obj, apply_to<T>); + return HB_VOID; + } + static return_t default_return_value (void) { return HB_VOID; } + bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; } + + hb_get_subtables_context_t (array_t &array_) : + array (array_), + debug_depth (0) {} + + array_t &array; + unsigned int debug_depth; +}; + +static inline bool +apply_forward (OT::hb_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t &accel, + const hb_get_subtables_context_t::array_t &subtables) +{ + bool ret = false; + hb_buffer_t *buffer = c->buffer; + while (buffer->idx < buffer->len && !buffer->in_error) + { + bool applied = false; + if (accel.may_have (buffer->cur().codepoint) && + (buffer->cur().mask & c->lookup_mask) && + c->check_glyph_property (&buffer->cur(), c->lookup_props)) + { + for (unsigned int i = 0; i < subtables.len; i++) + if (subtables[i].apply (c)) + { + applied = true; + break; + } + } + + if (applied) + ret = true; + else + buffer->next_glyph (); + } + return ret; +} + +static inline bool +apply_backward (OT::hb_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t &accel, + const hb_get_subtables_context_t::array_t &subtables) +{ + bool ret = false; + hb_buffer_t *buffer = c->buffer; + do + { + if (accel.may_have (buffer->cur().codepoint) && + (buffer->cur().mask & c->lookup_mask) && + c->check_glyph_property (&buffer->cur(), c->lookup_props)) + { + for (unsigned int i = 0; i < subtables.len; i++) + if (subtables[i].apply (c)) + { + ret = true; + break; + } + } + /* The reverse lookup doesn't "advance" cursor (for good reason). */ + buffer->idx--; + + } + while ((int) buffer->idx >= 0); + return ret; +} + +template <typename Proxy> +static inline void +apply_string (OT::hb_apply_context_t *c, + const typename Proxy::Lookup &lookup, + const hb_ot_layout_lookup_accelerator_t &accel) +{ + hb_buffer_t *buffer = c->buffer; + + if (unlikely (!buffer->len || !c->lookup_mask)) + return; + + c->set_lookup_props (lookup.get_props ()); + + hb_get_subtables_context_t::array_t subtables; + hb_get_subtables_context_t c_get_subtables (subtables); + lookup.dispatch (&c_get_subtables); + + if (likely (!lookup.is_reverse ())) + { + /* in/out forward substitution/positioning */ + if (Proxy::table_index == 0) + buffer->clear_output (); + buffer->idx = 0; + + bool ret; + ret = apply_forward (c, accel, subtables); + if (ret) + { + if (!Proxy::inplace) + buffer->swap_buffers (); + else + assert (!buffer->has_separate_output ()); + } + } + else + { + /* in-place backward substitution/positioning */ + if (Proxy::table_index == 0) + buffer->remove_output (); + buffer->idx = buffer->len - 1; + + apply_backward (c, accel, subtables); + } +} + +template <typename Proxy> +inline void hb_ot_map_t::apply (const Proxy &proxy, + const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) const +{ + const unsigned int table_index = proxy.table_index; + unsigned int i = 0; + OT::hb_apply_context_t c (table_index, font, buffer); + c.set_recurse_func (Proxy::Lookup::apply_recurse_func); + + for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { + const stage_map_t *stage = &stages[table_index][stage_index]; + for (; i < stage->last_lookup; i++) + { + unsigned int lookup_index = lookups[table_index][i].index; + if (!buffer->message (font, "start lookup %d", lookup_index)) continue; + c.set_lookup_index (lookup_index); + c.set_lookup_mask (lookups[table_index][i].mask); + c.set_auto_zwj (lookups[table_index][i].auto_zwj); + apply_string<Proxy> (&c, + proxy.table.get_lookup (lookup_index), + proxy.accels[lookup_index]); + (void) buffer->message (font, "end lookup %d", lookup_index); + } + + if (stage->pause_func) + { + buffer->clear_output (); + stage->pause_func (plan, font, buffer); + } + } +} + +void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const +{ + GSUBProxy proxy (font->face); + apply (proxy, plan, font, buffer); +} + +void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const +{ + GPOSProxy proxy (font->face); + apply (proxy, plan, font, buffer); +} + +HB_INTERNAL void +hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, + const OT::SubstLookup &lookup, + const hb_ot_layout_lookup_accelerator_t &accel) +{ + apply_string<GSUBProxy> (c, lookup, accel); +} diff --git a/gfx/harfbuzz/src/hb-ot-layout.h b/gfx/harfbuzz/src/hb-ot-layout.h new file mode 100644 index 000000000..9861f0fc7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout.h @@ -0,0 +1,321 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H_IN +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_LAYOUT_H +#define HB_OT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot-tag.h" + +HB_BEGIN_DECLS + + +#define HB_OT_TAG_GDEF HB_TAG('G','D','E','F') +#define HB_OT_TAG_GSUB HB_TAG('G','S','U','B') +#define HB_OT_TAG_GPOS HB_TAG('G','P','O','S') +#define HB_OT_TAG_JSTF HB_TAG('J','S','T','F') + + +/* + * GDEF + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_glyph_classes (hb_face_t *face); + +typedef enum { + HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0, + HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 1, + HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE = 2, + HB_OT_LAYOUT_GLYPH_CLASS_MARK = 3, + HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT = 4 +} hb_ot_layout_glyph_class_t; + +HB_EXTERN hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph); + +HB_EXTERN void +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, + hb_ot_layout_glyph_class_t klass, + hb_set_t *glyphs /* OUT */); + + +/* Not that useful. Provides list of attach points for a glyph that a + * client may want to cache */ +HB_EXTERN unsigned int +hb_ot_layout_get_attach_points (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */); + +/* Ligature caret positions */ +HB_EXTERN unsigned int +hb_ot_layout_get_ligature_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */); + + +/* + * GSUB/GPOS feature query and enumeration interface + */ + +#define HB_OT_LAYOUT_NO_SCRIPT_INDEX 0xFFFFu +#define HB_OT_LAYOUT_NO_FEATURE_INDEX 0xFFFFu +#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX 0xFFFFu +#define HB_OT_LAYOUT_NO_VARIATIONS_INDEX 0xFFFFFFFFu + +HB_EXTERN unsigned int +hb_ot_layout_table_get_script_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_table_find_script (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t script_tag, + unsigned int *script_index); + +/* Like find_script, but takes zero-terminated array of scripts to test */ +HB_EXTERN hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script); + +HB_EXTERN unsigned int +hb_ot_layout_table_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_script_get_language_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int start_offset, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_get_required_feature_index (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_get_required_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index, + hb_tag_t *feature_tag); + +HB_EXTERN unsigned int +hb_ot_layout_language_get_feature_indexes (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_language_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_find_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + hb_tag_t feature_tag, + unsigned int *feature_index); + +HB_EXTERN unsigned int +hb_ot_layout_feature_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_table_get_lookup_count (hb_face_t *face, + hb_tag_t table_tag); + + +HB_EXTERN void +hb_ot_layout_collect_lookups (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */); + +HB_EXTERN void +hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, + hb_tag_t table_tag, + unsigned int lookup_index, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output /* OUT. May be NULL */); + +#ifdef HB_NOT_IMPLEMENTED +typedef struct +{ + const hb_codepoint_t *before, + unsigned int before_length, + const hb_codepoint_t *input, + unsigned int input_length, + const hb_codepoint_t *after, + unsigned int after_length, +} hb_ot_layout_glyph_sequence_t; + +typedef hb_bool_t +(*hb_ot_layout_glyph_sequence_func_t) (hb_font_t *font, + hb_tag_t table_tag, + unsigned int lookup_index, + const hb_ot_layout_glyph_sequence_t *sequence, + void *user_data); + +HB_EXTERN void +Xhb_ot_layout_lookup_enumerate_sequences (hb_face_t *face, + hb_tag_t table_tag, + unsigned int lookup_index, + hb_ot_layout_glyph_sequence_func_t callback, + void *user_data); +#endif + +/* Variations support */ + +HB_EXTERN hb_bool_t +hb_ot_layout_table_find_feature_variations (hb_face_t *face, + hb_tag_t table_tag, + const int *coords, + unsigned int num_coords, + unsigned int *variations_index /* out */); + +HB_EXTERN unsigned int +hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int variations_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */); + + +/* + * GSUB + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_substitution (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_would_substitute (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context); + +HB_EXTERN void +hb_ot_layout_lookup_substitute_closure (hb_face_t *face, + unsigned int lookup_index, + hb_set_t *glyphs + /*TODO , hb_bool_t inclusive */); + +#ifdef HB_NOT_IMPLEMENTED +/* Note: You better have GDEF when using this API, or marks won't do much. */ +HB_EXTERN hb_bool_t +Xhb_ot_layout_lookup_substitute (hb_font_t *font, + unsigned int lookup_index, + const hb_ot_layout_glyph_sequence_t *sequence, + unsigned int out_size, + hb_codepoint_t *glyphs_out, /* OUT */ + unsigned int *clusters_out, /* OUT */ + unsigned int *out_length /* OUT */); +#endif + + +/* + * GPOS + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_positioning (hb_face_t *face); + +#ifdef HB_NOT_IMPLEMENTED +/* Note: You better have GDEF when using this API, or marks won't do much. */ +HB_EXTERN hb_bool_t +Xhb_ot_layout_lookup_position (hb_font_t *font, + unsigned int lookup_index, + const hb_ot_layout_glyph_sequence_t *sequence, + hb_glyph_position_t *positions /* IN / OUT */); +#endif + +/* Optical 'size' feature info. Returns true if found. + * http://www.microsoft.com/typography/otspec/features_pt.htm#size */ +HB_EXTERN hb_bool_t +hb_ot_layout_get_size_params (hb_face_t *face, + unsigned int *design_size, /* OUT. May be NULL */ + unsigned int *subfamily_id, /* OUT. May be NULL */ + unsigned int *subfamily_name_id, /* OUT. May be NULL */ + unsigned int *range_start, /* OUT. May be NULL */ + unsigned int *range_end /* OUT. May be NULL */); + + +HB_END_DECLS + +#endif /* HB_OT_LAYOUT_H */ diff --git a/gfx/harfbuzz/src/hb-ot-map-private.hh b/gfx/harfbuzz/src/hb-ot-map-private.hh new file mode 100644 index 000000000..0395c9c22 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-map-private.hh @@ -0,0 +1,239 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_MAP_PRIVATE_HH +#define HB_OT_MAP_PRIVATE_HH + +#include "hb-buffer-private.hh" + + +struct hb_ot_shape_plan_t; + +static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS}; + +struct hb_ot_map_t +{ + friend struct hb_ot_map_builder_t; + + public: + + struct feature_map_t { + hb_tag_t tag; /* should be first for our bsearch to work */ + unsigned int index[2]; /* GSUB/GPOS */ + unsigned int stage[2]; /* GSUB/GPOS */ + unsigned int shift; + hb_mask_t mask; + hb_mask_t _1_mask; /* mask for value=1, for quick access */ + unsigned int needs_fallback : 1; + unsigned int auto_zwj : 1; + + static int cmp (const feature_map_t *a, const feature_map_t *b) + { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; } + }; + + struct lookup_map_t { + unsigned short index; + unsigned short auto_zwj : 1; + hb_mask_t mask; + + static int cmp (const lookup_map_t *a, const lookup_map_t *b) + { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; } + }; + + typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); + + struct stage_map_t { + unsigned int last_lookup; /* Cumulative */ + pause_func_t pause_func; + }; + + + hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); } + + inline hb_mask_t get_global_mask (void) const { return global_mask; } + + inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = NULL) const { + const feature_map_t *map = features.bsearch (&feature_tag); + if (shift) *shift = map ? map->shift : 0; + return map ? map->mask : 0; + } + + inline bool needs_fallback (hb_tag_t feature_tag) const { + const feature_map_t *map = features.bsearch (&feature_tag); + return map ? map->needs_fallback : false; + } + + inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const { + const feature_map_t *map = features.bsearch (&feature_tag); + return map ? map->_1_mask : 0; + } + + inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const { + const feature_map_t *map = features.bsearch (&feature_tag); + return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX; + } + + inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const { + const feature_map_t *map = features.bsearch (&feature_tag); + return map ? map->stage[table_index] : (unsigned int) -1; + } + + inline void get_stage_lookups (unsigned int table_index, unsigned int stage, + const struct lookup_map_t **plookups, unsigned int *lookup_count) const { + if (unlikely (stage == (unsigned int) -1)) { + *plookups = NULL; + *lookup_count = 0; + return; + } + assert (stage <= stages[table_index].len); + unsigned int start = stage ? stages[table_index][stage - 1].last_lookup : 0; + unsigned int end = stage < stages[table_index].len ? stages[table_index][stage].last_lookup : lookups[table_index].len; + *plookups = &lookups[table_index][start]; + *lookup_count = end - start; + } + + HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const; + template <typename Proxy> + HB_INTERNAL inline void apply (const Proxy &proxy, + const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + + inline void finish (void) { + features.finish (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + lookups[table_index].finish (); + stages[table_index].finish (); + } + } + + public: + hb_tag_t chosen_script[2]; + bool found_script[2]; + + private: + + hb_mask_t global_mask; + + hb_prealloced_array_t<feature_map_t, 8> features; + hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */ + hb_prealloced_array_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */ +}; + +enum hb_ot_map_feature_flags_t { + F_NONE = 0x0000u, + F_GLOBAL = 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */ + F_HAS_FALLBACK = 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */ + F_MANUAL_ZWJ = 0x0004u, /* Don't skip over ZWJ when matching. */ + F_GLOBAL_SEARCH = 0x0008u /* If feature not found in LangSys, look for it in global feature list and pick one. */ +}; +HB_MARK_AS_FLAG_T (hb_ot_map_feature_flags_t); +/* Macro version for where const is desired. */ +#define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r))) + + +struct hb_ot_map_builder_t +{ + public: + + HB_INTERNAL hb_ot_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t *props_); + + HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, + hb_ot_map_feature_flags_t flags); + + inline void add_global_bool_feature (hb_tag_t tag) + { add_feature (tag, 1, F_GLOBAL); } + + inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func) + { add_pause (0, pause_func); } + inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func) + { add_pause (1, pause_func); } + + HB_INTERNAL void compile (hb_ot_map_t &m, + const int *coords, + unsigned int num_coords); + + inline void finish (void) { + feature_infos.finish (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + stages[table_index].finish (); + } + } + + private: + + HB_INTERNAL void add_lookups (hb_ot_map_t &m, + hb_face_t *face, + unsigned int table_index, + unsigned int feature_index, + unsigned int variations_index, + hb_mask_t mask, + bool auto_zwj); + + struct feature_info_t { + hb_tag_t tag; + unsigned int seq; /* sequence#, used for stable sorting only */ + unsigned int max_value; + hb_ot_map_feature_flags_t flags; + unsigned int default_value; /* for non-global features, what should the unset glyphs take */ + unsigned int stage[2]; /* GSUB/GPOS */ + + static int cmp (const feature_info_t *a, const feature_info_t *b) + { return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) : + (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); } + }; + + struct stage_info_t { + unsigned int index; + hb_ot_map_t::pause_func_t pause_func; + }; + + HB_INTERNAL void add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func); + + public: + + hb_face_t *face; + hb_segment_properties_t props; + + hb_tag_t chosen_script[2]; + bool found_script[2]; + unsigned int script_index[2], language_index[2]; + + private: + + unsigned int current_stage[2]; /* GSUB/GPOS */ + hb_prealloced_array_t<feature_info_t, 32> feature_infos; + hb_prealloced_array_t<stage_info_t, 8> stages[2]; /* GSUB/GPOS */ +}; + + + +#endif /* HB_OT_MAP_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-map.cc b/gfx/harfbuzz/src/hb-ot-map.cc new file mode 100644 index 000000000..9b331d521 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-map.cc @@ -0,0 +1,328 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-map-private.hh" + +#include "hb-ot-layout-private.hh" + + +void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const +{ + for (unsigned int i = 0; i < lookups[table_index].len; i++) + hb_set_add (lookups_out, lookups[table_index][i].index); +} + + +hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t *props_) +{ + memset (this, 0, sizeof (*this)); + + face = face_; + props = *props_; + + + /* Fetch script/language indices for GSUB/GPOS. We need these later to skip + * features not available in either table and not waste precious bits for them. */ + + hb_tag_t script_tags[3] = {HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE}; + hb_tag_t language_tag; + + hb_ot_tags_from_script (props.script, &script_tags[0], &script_tags[1]); + language_tag = hb_ot_tag_from_language (props.language); + + for (unsigned int table_index = 0; table_index < 2; table_index++) { + hb_tag_t table_tag = table_tags[table_index]; + found_script[table_index] = (bool) hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index], &chosen_script[table_index]); + hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]); + } +} + +void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, + hb_ot_map_feature_flags_t flags) +{ + feature_info_t *info = feature_infos.push(); + if (unlikely (!info)) return; + if (unlikely (!tag)) return; + info->tag = tag; + info->seq = feature_infos.len; + info->max_value = value; + info->flags = flags; + info->default_value = (flags & F_GLOBAL) ? value : 0; + info->stage[0] = current_stage[0]; + info->stage[1] = current_stage[1]; +} + +void +hb_ot_map_builder_t::add_lookups (hb_ot_map_t &m, + hb_face_t *face, + unsigned int table_index, + unsigned int feature_index, + unsigned int variations_index, + hb_mask_t mask, + bool auto_zwj) +{ + unsigned int lookup_indices[32]; + unsigned int offset, len; + unsigned int table_lookup_count; + + table_lookup_count = hb_ot_layout_table_get_lookup_count (face, table_tags[table_index]); + + offset = 0; + do { + len = ARRAY_LENGTH (lookup_indices); + hb_ot_layout_feature_with_variations_get_lookups (face, + table_tags[table_index], + feature_index, + variations_index, + offset, &len, + lookup_indices); + + for (unsigned int i = 0; i < len; i++) + { + if (lookup_indices[i] >= table_lookup_count) + continue; + hb_ot_map_t::lookup_map_t *lookup = m.lookups[table_index].push (); + if (unlikely (!lookup)) + return; + lookup->mask = mask; + lookup->index = lookup_indices[i]; + lookup->auto_zwj = auto_zwj; + } + + offset += len; + } while (len == ARRAY_LENGTH (lookup_indices)); +} + + +void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func) +{ + stage_info_t *s = stages[table_index].push (); + if (likely (s)) { + s->index = current_stage[table_index]; + s->pause_func = pause_func; + } + + current_stage[table_index]++; +} + +void +hb_ot_map_builder_t::compile (hb_ot_map_t &m, + const int *coords, + unsigned int num_coords) +{ + m.global_mask = 1; + + unsigned int required_feature_index[2]; + hb_tag_t required_feature_tag[2]; + /* We default to applying required feature in stage 0. If the required + * feature has a tag that is known to the shaper, we apply required feature + * in the stage for that tag. + */ + unsigned int required_feature_stage[2] = {0, 0}; + + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + m.chosen_script[table_index] = chosen_script[table_index]; + m.found_script[table_index] = found_script[table_index]; + + hb_ot_layout_language_get_required_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + &required_feature_index[table_index], + &required_feature_tag[table_index]); + } + + if (!feature_infos.len) + return; + + /* Sort features and merge duplicates */ + { + feature_infos.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < feature_infos.len; i++) + if (feature_infos[i].tag != feature_infos[j].tag) + feature_infos[++j] = feature_infos[i]; + else { + if (feature_infos[i].flags & F_GLOBAL) { + feature_infos[j].flags |= F_GLOBAL; + feature_infos[j].max_value = feature_infos[i].max_value; + feature_infos[j].default_value = feature_infos[i].default_value; + } else { + feature_infos[j].flags &= ~F_GLOBAL; + feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value); + /* Inherit default_value from j */ + } + feature_infos[j].flags |= (feature_infos[i].flags & F_HAS_FALLBACK); + feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]); + feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]); + } + feature_infos.shrink (j + 1); + } + + + /* Allocate bits now */ + unsigned int next_bit = 1; + for (unsigned int i = 0; i < feature_infos.len; i++) + { + const feature_info_t *info = &feature_infos[i]; + + unsigned int bits_needed; + + if ((info->flags & F_GLOBAL) && info->max_value == 1) + /* Uses the global bit */ + bits_needed = 0; + else + /* Limit to 8 bits per feature. */ + bits_needed = MIN(8u, _hb_bit_storage (info->max_value)); + + if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t)) + continue; /* Feature disabled, or not enough bits. */ + + + hb_bool_t found = false; + unsigned int feature_index[2]; + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + if (required_feature_tag[table_index] == info->tag) + required_feature_stage[table_index] = info->stage[table_index]; + + found |= hb_ot_layout_language_find_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + info->tag, + &feature_index[table_index]); + } + if (!found && (info->flags & F_GLOBAL_SEARCH)) + { + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + found |= hb_ot_layout_table_find_feature (face, + table_tags[table_index], + info->tag, + &feature_index[table_index]); + } + } + if (!found && !(info->flags & F_HAS_FALLBACK)) + continue; + + + hb_ot_map_t::feature_map_t *map = m.features.push (); + if (unlikely (!map)) + break; + + map->tag = info->tag; + map->index[0] = feature_index[0]; + map->index[1] = feature_index[1]; + map->stage[0] = info->stage[0]; + map->stage[1] = info->stage[1]; + map->auto_zwj = !(info->flags & F_MANUAL_ZWJ); + if ((info->flags & F_GLOBAL) && info->max_value == 1) { + /* Uses the global bit */ + map->shift = 0; + map->mask = 1; + } else { + map->shift = next_bit; + map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit); + next_bit += bits_needed; + m.global_mask |= (info->default_value << map->shift) & map->mask; + } + map->_1_mask = (1u << map->shift) & map->mask; + map->needs_fallback = !found; + + } + feature_infos.shrink (0); /* Done with these */ + + + add_gsub_pause (NULL); + add_gpos_pause (NULL); + + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + /* Collect lookup indices for features */ + + unsigned int variations_index; + hb_ot_layout_table_find_feature_variations (face, + table_tags[table_index], + coords, + num_coords, + &variations_index); + + unsigned int stage_index = 0; + unsigned int last_num_lookups = 0; + for (unsigned stage = 0; stage < current_stage[table_index]; stage++) + { + if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX && + required_feature_stage[table_index] == stage) + add_lookups (m, face, table_index, + required_feature_index[table_index], + variations_index, + 1 /* mask */, + true /* auto_zwj */); + + for (unsigned i = 0; i < m.features.len; i++) + if (m.features[i].stage[table_index] == stage) + add_lookups (m, face, table_index, + m.features[i].index[table_index], + variations_index, + m.features[i].mask, + m.features[i].auto_zwj); + + /* Sort lookups and merge duplicates */ + if (last_num_lookups < m.lookups[table_index].len) + { + m.lookups[table_index].qsort (last_num_lookups, m.lookups[table_index].len); + + unsigned int j = last_num_lookups; + for (unsigned int i = j + 1; i < m.lookups[table_index].len; i++) + if (m.lookups[table_index][i].index != m.lookups[table_index][j].index) + m.lookups[table_index][++j] = m.lookups[table_index][i]; + else + { + m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask; + m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj; + } + m.lookups[table_index].shrink (j + 1); + } + + last_num_lookups = m.lookups[table_index].len; + + if (stage_index < stages[table_index].len && stages[table_index][stage_index].index == stage) { + hb_ot_map_t::stage_map_t *stage_map = m.stages[table_index].push (); + if (likely (stage_map)) { + stage_map->last_lookup = last_num_lookups; + stage_map->pause_func = stages[table_index][stage_index].pause_func; + } + + stage_index++; + } + } + } +} diff --git a/gfx/harfbuzz/src/hb-ot-math.cc b/gfx/harfbuzz/src/hb-ot-math.cc new file mode 100644 index 000000000..9ef21d299 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-math.cc @@ -0,0 +1,272 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#include "hb-open-type-private.hh" + +#include "hb-ot-layout-math-table.hh" + +HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) + +static inline const OT::MATH& +_get_math (hb_face_t *face) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH); + + hb_ot_layout_t * layout = hb_ot_layout_from_face (face); + +retry: + const OT::MATH *math = (const OT::MATH *) hb_atomic_ptr_get (&layout->math); + + if (unlikely (!math)) + { + hb_blob_t *blob = OT::Sanitizer<OT::MATH>::sanitize (face->reference_table (HB_OT_TAG_MATH)); + math = OT::Sanitizer<OT::MATH>::lock_instance (blob); + if (!hb_atomic_ptr_cmpexch (&layout->math, NULL, math)) + { + hb_blob_destroy (blob); + goto retry; + } + layout->math_blob = blob; + } + + return *math; +} + +/* + * OT::MATH + */ + +/** + * hb_ot_math_has_data: + * @face: #hb_face_t to test + * + * This function allows to verify the presence of an OpenType MATH table on the + * face. If so, such a table will be loaded into memory and sanitized. You can + * then safely call other functions for math layout and shaping. + * + * Return value: #TRUE if face has a MATH table and #FALSE otherwise + * + * Since: 1.3.3 + **/ +hb_bool_t +hb_ot_math_has_data (hb_face_t *face) +{ + return &_get_math (face) != &OT::Null(OT::MATH); +} + +/** + * hb_ot_math_get_constant: + * @font: #hb_font_t from which to retrieve the value + * @constant: #hb_ot_math_constant_t the constant to retrieve + * + * This function returns the requested math constants as a #hb_position_t. + * If the request constant is HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, + * HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or + * HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN then the return value is + * actually an integer between 0 and 100 representing that percentage. + * + * Return value: the requested constant or 0 + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_constant (hb_font_t *font, + hb_ot_math_constant_t constant) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_constant(constant, font); +} + +/** + * hb_ot_math_get_glyph_italics_correction: + * @font: #hb_font_t from which to retrieve the value + * @glyph: glyph index from which to retrieve the value + * + * Return value: the italics correction of the glyph or 0 + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_italics_correction (hb_font_t *font, + hb_codepoint_t glyph) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_glyph_info().get_italics_correction (glyph, font); +} + +/** + * hb_ot_math_get_glyph_top_accent_attachment: + * @font: #hb_font_t from which to retrieve the value + * @glyph: glyph index from which to retrieve the value + * + * Return value: the top accent attachment of the glyph or 0 + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, + hb_codepoint_t glyph) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_glyph_info().get_top_accent_attachment (glyph, font); +} + +/** + * hb_ot_math_is_glyph_extended_shape: + * @font: a #hb_font_t to test + * @glyph: a glyph index to test + * + * Return value: #TRUE if the glyph is an extended shape and #FALSE otherwise + * + * Since: 1.3.3 + **/ +hb_bool_t +hb_ot_math_is_glyph_extended_shape (hb_face_t *face, + hb_codepoint_t glyph) +{ + const OT::MATH &math = _get_math (face); + return math.get_math_glyph_info().is_extended_shape (glyph); +} + +/** + * hb_ot_math_get_glyph_kerning: + * @font: #hb_font_t from which to retrieve the value + * @glyph: glyph index from which to retrieve the value + * @kern: the #hb_ot_math_kern_t from which to retrieve the value + * @correction_height: the correction height to use to determine the kerning. + * + * This function tries to retrieve the MathKern table for the specified font, + * glyph and #hb_ot_math_kern_t. Then it browses the list of heights from the + * MathKern table to find one value that is greater or equal to specified + * correction_height. If one is found the corresponding value from the list of + * kerns is returned and otherwise the last kern value is returned. + * + * Return value: requested kerning or 0 + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_kerning (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_glyph_info().get_kerning (glyph, kern, correction_height, font); +} + +/** + * hb_ot_math_get_glyph_variants: + * @font: #hb_font_t from which to retrieve the values + * @glyph: index of the glyph to stretch + * @direction: direction of the stretching + * @start_offset: offset of the first variant to retrieve + * @variants_count: maximum number of variants to retrieve after start_offset + * (IN) and actual number of variants retrieved (OUT) + * @variants: array of size at least @variants_count to store the result + * + * This function tries to retrieve the MathGlyphConstruction for the specified + * font, glyph and direction. Note that only the value of + * #HB_DIRECTION_IS_HORIZONTAL is considered. It provides the corresponding list + * of size variants as an array of hb_ot_math_glyph_variant_t structs. + * + * Return value: the total number of size variants available or 0 + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_variants().get_glyph_variants (glyph, direction, font, + start_offset, + variants_count, + variants); +} + +/** + * hb_ot_math_get_min_connector_overlap: + * @font: #hb_font_t from which to retrieve the value + * @direction: direction of the stretching + * + * This function tries to retrieve the MathVariants table for the specified + * font and returns the minimum overlap of connecting glyphs to draw a glyph + * assembly in the specified direction. Note that only the value of + * #HB_DIRECTION_IS_HORIZONTAL is considered. + * + * Return value: requested min connector overlap or 0 + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_variants().get_min_connector_overlap (direction, font); +} + +/** + * hb_ot_math_get_glyph_assembly: + * @font: #hb_font_t from which to retrieve the values + * @glyph: index of the glyph to stretch + * @direction: direction of the stretching + * @start_offset: offset of the first glyph part to retrieve + * @parts_count: maximum number of glyph parts to retrieve after start_offset + * (IN) and actual number of parts retrieved (OUT) + * @parts: array of size at least @parts_count to store the result + * @italics_correction: italic correction of the glyph assembly + * + * This function tries to retrieve the GlyphAssembly for the specified font, + * glyph and direction. Note that only the value of #HB_DIRECTION_IS_HORIZONTAL + * is considered. It provides the information necessary to draw the glyph + * assembly as an array of #hb_ot_math_glyph_part_t. + * + * Return value: the total number of parts in the glyph assembly + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */) +{ + const OT::MATH &math = _get_math (font->face); + return math.get_math_variants().get_glyph_parts (glyph, direction, font, + start_offset, + parts_count, + parts, + italics_correction); +} diff --git a/gfx/harfbuzz/src/hb-ot-math.h b/gfx/harfbuzz/src/hb-ot-math.h new file mode 100644 index 000000000..521a5ca03 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-math.h @@ -0,0 +1,209 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#ifndef HB_OT_H_IN +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_MATH_H +#define HB_OT_MATH_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/* + * MATH + */ + +#define HB_OT_TAG_MATH HB_TAG('M','A','T','H') + +/* Use with hb_buffer_set_script() for math shaping. */ +#define HB_OT_MATH_SCRIPT HB_TAG('m','a','t','h') + +/* Types */ + +/** + * hb_ot_math_constant_t: + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN = 0, + HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = 1, + HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT = 2, + HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT = 3, + HB_OT_MATH_CONSTANT_MATH_LEADING = 4, + HB_OT_MATH_CONSTANT_AXIS_HEIGHT = 5, + HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT = 6, + HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT = 7, + HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN = 8, + HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX = 9, + HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN = 10, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP = 11, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED = 12, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN = 13, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX = 14, + HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN = 15, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = 16, + HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT = 17, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN = 18, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN = 19, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN = 20, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN = 21, + HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP = 22, + HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP = 23, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN = 24, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = 25, + HB_OT_MATH_CONSTANT_STACK_GAP_MIN = 26, + HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN = 27, + HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP = 28, + HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN = 29, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN = 30, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN = 31, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP = 32, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = 33, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN = 34, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = 35, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN = 36, + HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = 37, + HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS = 38, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN = 39, + HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = 40, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP = 41, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP = 42, + HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP = 43, + HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS = 44, + HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER = 45, + HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP = 46, + HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS = 47, + HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER = 48, + HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP = 49, + HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP = 50, + HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS = 51, + HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER = 52, + HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE = 53, + HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE = 54, + HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55 +} hb_ot_math_constant_t; + +/** + * hb_ot_math_kern_t: + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_KERN_TOP_RIGHT = 0, + HB_OT_MATH_KERN_TOP_LEFT = 1, + HB_OT_MATH_KERN_BOTTOM_RIGHT = 2, + HB_OT_MATH_KERN_BOTTOM_LEFT = 3 +} hb_ot_math_kern_t; + +/** + * hb_ot_math_glyph_variant_t: + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_variant_t { + hb_codepoint_t glyph; + hb_position_t advance; +} hb_ot_math_glyph_variant_t; + +/** + * hb_ot_math_glyph_part_flags_t: + * + * Since: 1.3.3 + */ +typedef enum { /*< flags >*/ + HB_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ +} hb_ot_math_glyph_part_flags_t; + +/** + * hb_ot_math_glyph_part_t: + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_part_t { + hb_codepoint_t glyph; + hb_position_t start_connector_length; + hb_position_t end_connector_length; + hb_position_t full_advance; + hb_ot_math_glyph_part_flags_t flags; +} hb_ot_math_glyph_part_t; + +/* Methods */ + +HB_EXTERN hb_bool_t +hb_ot_math_has_data (hb_face_t *face); + +HB_EXTERN hb_position_t +hb_ot_math_get_constant (hb_font_t *font, + hb_ot_math_constant_t constant); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_italics_correction (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_bool_t +hb_ot_math_is_glyph_extended_shape (hb_face_t *face, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_kerning (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */); + +HB_EXTERN hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_MATH_H */ diff --git a/gfx/harfbuzz/src/hb-ot-maxp-table.hh b/gfx/harfbuzz/src/hb-ot-maxp-table.hh new file mode 100644 index 000000000..943e3908c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh @@ -0,0 +1,72 @@ +/* + * 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_MAXP_TABLE_HH +#define HB_OT_MAXP_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * maxp -- The Maximum Profile Table + */ + +#define HB_OT_TAG_maxp HB_TAG('m','a','x','p') + +struct maxp +{ + static const hb_tag_t tableTag = HB_OT_TAG_maxp; + + inline unsigned int get_num_glyphs (void) const + { + return numGlyphs; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1 || + (version.major == 0 && version.minor == 0x5000u))); + } + + /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */ + protected: + FixedVersion<>version; /* Version of the maxp table (0.5 or 1.0), + * 0x00005000u or 0x00010000u. */ + USHORT numGlyphs; /* The number of glyphs in the font. */ + public: + DEFINE_SIZE_STATIC (6); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_MAXP_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-name-table.hh b/gfx/harfbuzz/src/hb-ot-name-table.hh new file mode 100644 index 000000000..870f12332 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name-table.hh @@ -0,0 +1,136 @@ +/* + * 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" + + +namespace OT { + + +/* + * 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, const void *base) const + { + TRACE_SANITIZE (this); + /* We can check from base all the way up to the end of string... */ + return_trace (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 tableTag = 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 unsigned int get_size (void) const + { return min_size + count * nameRecord[0].min_size; } + + inline bool sanitize_records (hb_sanitize_context_t *c) const { + TRACE_SANITIZE (this); + 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 (false); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (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. */ + 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); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_NAME_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-os2-table.hh b/gfx/harfbuzz/src/hb-ot-os2-table.hh new file mode 100644 index 000000000..4709cd6e8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -0,0 +1,105 @@ +/* + * 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_OS2_TABLE_HH +#define HB_OT_OS2_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + +/* + * OS/2 and Windows Metrics + * http://www.microsoft.com/typography/otspec/os2.htm + */ + +#define HB_OT_TAG_os2 HB_TAG('O','S','/','2') + +struct os2 +{ + static const hb_tag_t tableTag = HB_OT_TAG_os2; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + USHORT version; + + /* Version 0 */ + SHORT xAvgCharWidth; + USHORT usWeightClass; + USHORT usWidthClass; + USHORT fsType; + SHORT ySubscriptXSize; + SHORT ySubscriptYSize; + SHORT ySubscriptXOffset; + SHORT ySubscriptYOffset; + SHORT ySuperscriptXSize; + SHORT ySuperscriptYSize; + SHORT ySuperscriptXOffset; + SHORT ySuperscriptYOffset; + SHORT yStrikeoutSize; + SHORT yStrikeoutPosition; + SHORT sFamilyClass; + BYTE panose[10]; + ULONG ulUnicodeRange[4]; + Tag achVendID; + USHORT fsSelection; + USHORT usFirstCharIndex; + USHORT usLastCharIndex; + SHORT sTypoAscender; + SHORT sTypoDescender; + SHORT sTypoLineGap; + USHORT usWinAscent; + USHORT usWinDescent; + + /* Version 1 */ + //ULONG ulCodePageRange1; + //ULONG ulCodePageRange2; + + /* Version 2 */ + //SHORT sxHeight; + //SHORT sCapHeight; + //USHORT usDefaultChar; + //USHORT usBreakChar; + //USHORT usMaxContext; + + /* Version 5 */ + //USHORT usLowerOpticalPointSize; + //USHORT usUpperOpticalPointSize; + + public: + DEFINE_SIZE_STATIC (78); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_OS2_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-post-table.hh b/gfx/harfbuzz/src/hb-ot-post-table.hh new file mode 100644 index 000000000..82ab3882a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-post-table.hh @@ -0,0 +1,119 @@ +/* + * Copyright © 2016 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_POST_TABLE_HH +#define HB_OT_POST_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * post -- PostScript + */ + +#define HB_OT_TAG_post HB_TAG('p','o','s','t') + + +struct postV2Tail +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (numberOfGlyphs.sanitize (c) && + c->check_array (glyphNameIndex, sizeof (USHORT), numberOfGlyphs)); + } + + USHORT numberOfGlyphs; /* Number of glyphs (this should be the + * same as numGlyphs in 'maxp' table). */ + USHORT glyphNameIndex[VAR]; /* This is not an offset, but is the + * ordinal number of the glyph in 'post' + * string tables. */ + BYTE namesX[VAR]; /* Glyph names with length bytes [variable] + * (a Pascal string). */ + + DEFINE_SIZE_ARRAY2 (2, glyphNameIndex, namesX); +}; + +struct post +{ + static const hb_tag_t tableTag = HB_OT_TAG_post; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + if (version.to_int () == 0x00020000) + { + const postV2Tail &v2 = StructAfter<postV2Tail>(*this); + return_trace (v2.sanitize (c)); + } + return_trace (true); + } + + public: + FixedVersion<>version; /* 0x00010000 for version 1.0 + * 0x00020000 for version 2.0 + * 0x00025000 for version 2.5 (deprecated) + * 0x00030000 for version 3.0 */ + Fixed italicAngle; /* Italic angle in counter-clockwise degrees + * from the vertical. Zero for upright text, + * negative for text that leans to the right + * (forward). */ + FWORD underlinePosition; /* This is the suggested distance of the top + * of the underline from the baseline + * (negative values indicate below baseline). + * The PostScript definition of this FontInfo + * dictionary key (the y coordinate of the + * center of the stroke) is not used for + * historical reasons. The value of the + * PostScript key may be calculated by + * subtracting half the underlineThickness + * from the value of this field. */ + FWORD underlineThickness; /* Suggested values for the underline + thickness. */ + ULONG isFixedPitch; /* Set to 0 if the font is proportionally + * spaced, non-zero if the font is not + * proportionally spaced (i.e. monospaced). */ + ULONG minMemType42; /* Minimum memory usage when an OpenType font + * is downloaded. */ + ULONG maxMemType42; /* Maximum memory usage when an OpenType font + * is downloaded. */ + ULONG minMemType1; /* Minimum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ + ULONG maxMemType1; /* Maximum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ +/*postV2Tail v2[VAR];*/ + DEFINE_SIZE_STATIC (32); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_POST_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh new file mode 100644 index 000000000..d97d28521 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh @@ -0,0 +1,354 @@ +/* + * 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_ARABIC_FALLBACK_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH + +#include "hb-private.hh" + +#include "hb-ot-shape-private.hh" +#include "hb-ot-layout-gsub-table.hh" + + +/* Features ordered the same as the entries in shaping_table rows, + * followed by rlig. Don't change. */ +static const hb_tag_t arabic_fallback_features[] = +{ + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), + HB_TAG('i','s','o','l'), + HB_TAG('r','l','i','g'), +}; + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + unsigned int feature_index) +{ + OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + unsigned int num_glyphs = 0; + + /* Populate arrays */ + for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) + { + hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; + hb_codepoint_t u_glyph, s_glyph; + + if (!s || + !hb_font_get_glyph (font, u, 0, &u_glyph) || + !hb_font_get_glyph (font, s, 0, &s_glyph) || + u_glyph == s_glyph || + u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) + continue; + + glyphs[num_glyphs].set (u_glyph); + substitutes[num_glyphs].set (s_glyph); + + num_glyphs++; + } + + if (!num_glyphs) + return NULL; + + /* Bubble-sort or something equally good! + * May not be good-enough for presidential candidate interviews, but good-enough for us... */ + hb_stable_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); + + OT::Supplier<OT::GlyphID> glyphs_supplier (glyphs, num_glyphs); + OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs); + + /* Each glyph takes four bytes max, and there's some overhead. */ + char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; + OT::hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); + bool ret = lookup->serialize_single (&c, + OT::LookupFlag::IgnoreMarks, + glyphs_supplier, + substitutes_supplier, + num_glyphs); + c.end_serialize (); + /* TODO sanitize the results? */ + + return ret ? c.copy<OT::SubstLookup> () : NULL; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font) +{ + OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; + unsigned int num_first_glyphs = 0; + + /* We know that all our ligatures are 2-component */ + OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; + OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; + unsigned int num_ligatures = 0; + + /* Populate arrays */ + + /* Sort out the first-glyphs */ + for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++) + { + hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; + hb_codepoint_t first_glyph; + if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) + continue; + first_glyphs[num_first_glyphs].set (first_glyph); + ligature_per_first_glyph_count_list[num_first_glyphs] = 0; + first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; + num_first_glyphs++; + } + hb_stable_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]); + + /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ + for (unsigned int i = 0; i < num_first_glyphs; i++) + { + unsigned int first_glyph_idx = first_glyphs_indirection[i]; + + for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++) + { + hb_codepoint_t second_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second; + hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature; + hb_codepoint_t second_glyph, ligature_glyph; + if (!second_u || + !hb_font_get_glyph (font, second_u, 0, &second_glyph) || + !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) + continue; + + ligature_per_first_glyph_count_list[i]++; + + ligature_list[num_ligatures].set (ligature_glyph); + component_count_list[num_ligatures] = 2; + component_list[num_ligatures].set (second_glyph); + num_ligatures++; + } + } + + if (!num_ligatures) + return NULL; + + OT::Supplier<OT::GlyphID> first_glyphs_supplier (first_glyphs, num_first_glyphs); + OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list, num_first_glyphs); + OT::Supplier<OT::GlyphID> ligatures_supplier (ligature_list, num_ligatures); + OT::Supplier<unsigned int > component_count_supplier (component_count_list, num_ligatures); + OT::Supplier<OT::GlyphID> component_supplier (component_list, num_ligatures); + + /* 16 bytes per ligature ought to be enough... */ + char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128]; + OT::hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); + bool ret = lookup->serialize_ligature (&c, + OT::LookupFlag::IgnoreMarks, + first_glyphs_supplier, + ligature_per_first_glyph_count_supplier, + num_first_glyphs, + ligatures_supplier, + component_count_supplier, + component_supplier); + + c.end_serialize (); + /* TODO sanitize the results? */ + + return ret ? c.copy<OT::SubstLookup> () : NULL; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + unsigned int feature_index) +{ + if (feature_index < 4) + return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); + else + return arabic_fallback_synthesize_lookup_ligature (plan, font); +} + +#define ARABIC_FALLBACK_MAX_LOOKUPS 5 + +struct arabic_fallback_plan_t +{ + ASSERT_POD (); + + unsigned int num_lookups; + bool free_lookups; + + hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; +}; + +static const arabic_fallback_plan_t arabic_fallback_plan_nil = {}; + +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256) +#define HB_WITH_WIN1256 +#endif + +#ifdef HB_WITH_WIN1256 +#include "hb-ot-shape-complex-arabic-win1256.hh" +#endif + +struct ManifestLookup { + OT::Tag tag; + OT::OffsetTo<OT::SubstLookup> lookupOffset; +}; +typedef OT::ArrayOf<ManifestLookup> Manifest; + +static bool +arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ +#ifdef HB_WITH_WIN1256 + /* Does this font look like it's Windows-1256-encoded? */ + hb_codepoint_t g; + if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ && + hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ && + hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ && + hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ && + hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */)) + return false; + + const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest); + ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup) + <= ARABIC_FALLBACK_MAX_LOOKUPS); + /* TODO sanitize the table? */ + + unsigned j = 0; + unsigned int count = manifest.len; + for (unsigned int i = 0; i < count; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset)); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = false; + + return j > 0; +#else + return false; +#endif +} + +static bool +arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS); + unsigned int j = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = true; + + return j > 0; +} + +static arabic_fallback_plan_t * +arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); + if (unlikely (!fallback_plan)) + return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil); + + fallback_plan->num_lookups = 0; + fallback_plan->free_lookups = false; + + /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms, + * in case the font has cmap entries for the presentation-forms characters. */ + if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font)) + return fallback_plan; + + /* See if this looks like a Windows-1256-encoded font. If it does, use a + * hand-coded GSUB table. */ + if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font)) + return fallback_plan; + + free (fallback_plan); + return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil); +} + +static void +arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) +{ + if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil) + return; + + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) + { + fallback_plan->accel_array[i].fini (); + if (fallback_plan->free_lookups) + free (fallback_plan->lookup_array[i]); + } + + free (fallback_plan); +} + +static void +arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + OT::hb_apply_context_t c (0, font, buffer); + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) { + c.set_lookup_mask (fallback_plan->mask_array[i]); + hb_ot_layout_substitute_lookup (&c, + *fallback_plan->lookup_array[i], + fallback_plan->accel_array[i]); + } +} + + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh new file mode 100644 index 000000000..fcedc7d74 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh @@ -0,0 +1,50 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-ot-shape-complex-private.hh" + + +struct arabic_shape_plan_t; + +HB_INTERNAL void * +data_create_arabic (const hb_ot_shape_plan_t *plan); + +HB_INTERNAL void +data_destroy_arabic (void *data); + +HB_INTERNAL void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script); + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh new file mode 100644 index 000000000..736c7f76b --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh @@ -0,0 +1,395 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + * + * on files with these headers: + * + * # ArabicShaping-9.0.0.txt + * # Date: 2016-02-24, 22:25:00 GMT [RP] + * # Blocks-9.0.0.txt + * # Date: 2016-02-05, 23:48: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 + + +#define X JOINING_TYPE_X +#define R JOINING_TYPE_R +#define T JOINING_TYPE_T +#define U JOINING_TYPE_U +#define A JOINING_GROUP_ALAPH +#define DR JOINING_GROUP_DALATH_RISH +#define L JOINING_TYPE_L +#define C JOINING_TYPE_C +#define D JOINING_TYPE_D + +static const uint8_t joining_table[] = +{ + +#define joining_offset_0x0600u 0 + + /* Arabic */ + + /* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D, + /* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D, + /* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X, + /* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D, + + /* Syriac */ + + /* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D, + /* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D, + + /* Arabic Supplement */ + + /* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D, + /* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D, + + /* FILLER */ + + /* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* NKo */ + + /* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X, + + /* FILLER */ + + /* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Mandaic */ + + /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X, + /* 0860 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0880 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Arabic Extended-A */ + + /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,X,D,D,D,R,D,D,D,D,X,X, + /* 08C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 08E0 */ X,X,U, + +#define joining_offset_0x1806u 739 + + /* Mongolian */ + + /* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X, + /* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D, + +#define joining_offset_0x200cu 904 + + /* General Punctuation */ + + /* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2060 */ X,X,X,X,X,X,U,U,U,U, + +#define joining_offset_0xa840u 998 + + /* Phags-pa */ + + /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U, + +#define joining_offset_0x10ac0u 1050 + + /* Manichaean */ + + /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D, + /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R, + +#define joining_offset_0x10b80u 1098 + + /* Psalter Pahlavi */ + + /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U, + +#define joining_offset_0x1e900u 1146 + + /* Adlam */ + + /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E940 */ D,D,D,D, + +}; /* Table items: 1214; occupancy: 54% */ + + +static unsigned int +joining_type (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u]; + break; + + case 0x1u: + if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u]; + break; + + case 0x2u: + if (hb_in_range (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu]; + break; + + case 0xAu: + if (hb_in_range (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u]; + break; + + case 0x10u: + if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; + if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; + break; + + case 0x1Eu: + if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + break; + + default: + break; + } + return X; +} + +#undef X +#undef R +#undef T +#undef U +#undef A +#undef DR +#undef L +#undef C +#undef D + + +static const uint16_t shaping_table[][4] = +{ + {0x0000u, 0x0000u, 0x0000u, 0xFE80u}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */ + {0x0000u, 0x0000u, 0xFE82u, 0xFE81u}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ + {0x0000u, 0x0000u, 0xFE84u, 0xFE83u}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE86u, 0xFE85u}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE88u, 0xFE87u}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ + {0xFE8Bu, 0xFE8Cu, 0xFE8Au, 0xFE89u}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE8Eu, 0xFE8Du}, /* U+0627 ARABIC LETTER ALEF */ + {0xFE91u, 0xFE92u, 0xFE90u, 0xFE8Fu}, /* U+0628 ARABIC LETTER BEH */ + {0x0000u, 0x0000u, 0xFE94u, 0xFE93u}, /* U+0629 ARABIC LETTER TEH MARBUTA */ + {0xFE97u, 0xFE98u, 0xFE96u, 0xFE95u}, /* U+062A ARABIC LETTER TEH */ + {0xFE9Bu, 0xFE9Cu, 0xFE9Au, 0xFE99u}, /* U+062B ARABIC LETTER THEH */ + {0xFE9Fu, 0xFEA0u, 0xFE9Eu, 0xFE9Du}, /* U+062C ARABIC LETTER JEEM */ + {0xFEA3u, 0xFEA4u, 0xFEA2u, 0xFEA1u}, /* U+062D ARABIC LETTER HAH */ + {0xFEA7u, 0xFEA8u, 0xFEA6u, 0xFEA5u}, /* U+062E ARABIC LETTER KHAH */ + {0x0000u, 0x0000u, 0xFEAAu, 0xFEA9u}, /* U+062F ARABIC LETTER DAL */ + {0x0000u, 0x0000u, 0xFEACu, 0xFEABu}, /* U+0630 ARABIC LETTER THAL */ + {0x0000u, 0x0000u, 0xFEAEu, 0xFEADu}, /* U+0631 ARABIC LETTER REH */ + {0x0000u, 0x0000u, 0xFEB0u, 0xFEAFu}, /* U+0632 ARABIC LETTER ZAIN */ + {0xFEB3u, 0xFEB4u, 0xFEB2u, 0xFEB1u}, /* U+0633 ARABIC LETTER SEEN */ + {0xFEB7u, 0xFEB8u, 0xFEB6u, 0xFEB5u}, /* U+0634 ARABIC LETTER SHEEN */ + {0xFEBBu, 0xFEBCu, 0xFEBAu, 0xFEB9u}, /* U+0635 ARABIC LETTER SAD */ + {0xFEBFu, 0xFEC0u, 0xFEBEu, 0xFEBDu}, /* U+0636 ARABIC LETTER DAD */ + {0xFEC3u, 0xFEC4u, 0xFEC2u, 0xFEC1u}, /* U+0637 ARABIC LETTER TAH */ + {0xFEC7u, 0xFEC8u, 0xFEC6u, 0xFEC5u}, /* U+0638 ARABIC LETTER ZAH */ + {0xFECBu, 0xFECCu, 0xFECAu, 0xFEC9u}, /* U+0639 ARABIC LETTER AIN */ + {0xFECFu, 0xFED0u, 0xFECEu, 0xFECDu}, /* U+063A ARABIC LETTER GHAIN */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0640 */ + {0xFED3u, 0xFED4u, 0xFED2u, 0xFED1u}, /* U+0641 ARABIC LETTER FEH */ + {0xFED7u, 0xFED8u, 0xFED6u, 0xFED5u}, /* U+0642 ARABIC LETTER QAF */ + {0xFEDBu, 0xFEDCu, 0xFEDAu, 0xFED9u}, /* U+0643 ARABIC LETTER KAF */ + {0xFEDFu, 0xFEE0u, 0xFEDEu, 0xFEDDu}, /* U+0644 ARABIC LETTER LAM */ + {0xFEE3u, 0xFEE4u, 0xFEE2u, 0xFEE1u}, /* U+0645 ARABIC LETTER MEEM */ + {0xFEE7u, 0xFEE8u, 0xFEE6u, 0xFEE5u}, /* U+0646 ARABIC LETTER NOON */ + {0xFEEBu, 0xFEECu, 0xFEEAu, 0xFEE9u}, /* U+0647 ARABIC LETTER HEH */ + {0x0000u, 0x0000u, 0xFEEEu, 0xFEEDu}, /* U+0648 ARABIC LETTER WAW */ + {0xFBE8u, 0xFBE9u, 0xFEF0u, 0xFEEFu}, /* U+0649 ARABIC LETTER */ + {0xFEF3u, 0xFEF4u, 0xFEF2u, 0xFEF1u}, /* U+064A ARABIC LETTER YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0650 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0651 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0652 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0653 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0654 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0655 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0656 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0657 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0658 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0659 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0660 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0661 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0662 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0663 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0664 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0665 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0666 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0667 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0668 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0669 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0670 */ + {0x0000u, 0x0000u, 0xFB51u, 0xFB50u}, /* U+0671 ARABIC LETTER ALEF WASLA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0672 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0673 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0674 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0675 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0676 */ + {0x0000u, 0x0000u, 0x0000u, 0xFBDDu}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0678 */ + {0xFB68u, 0xFB69u, 0xFB67u, 0xFB66u}, /* U+0679 ARABIC LETTER TTEH */ + {0xFB60u, 0xFB61u, 0xFB5Fu, 0xFB5Eu}, /* U+067A ARABIC LETTER TTEHEH */ + {0xFB54u, 0xFB55u, 0xFB53u, 0xFB52u}, /* U+067B ARABIC LETTER BEEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067D */ + {0xFB58u, 0xFB59u, 0xFB57u, 0xFB56u}, /* U+067E ARABIC LETTER PEH */ + {0xFB64u, 0xFB65u, 0xFB63u, 0xFB62u}, /* U+067F ARABIC LETTER TEHEH */ + {0xFB5Cu, 0xFB5Du, 0xFB5Bu, 0xFB5Au}, /* U+0680 ARABIC LETTER BEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0681 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0682 */ + {0xFB78u, 0xFB79u, 0xFB77u, 0xFB76u}, /* U+0683 ARABIC LETTER NYEH */ + {0xFB74u, 0xFB75u, 0xFB73u, 0xFB72u}, /* U+0684 ARABIC LETTER DYEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0685 */ + {0xFB7Cu, 0xFB7Du, 0xFB7Bu, 0xFB7Au}, /* U+0686 ARABIC LETTER TCHEH */ + {0xFB80u, 0xFB81u, 0xFB7Fu, 0xFB7Eu}, /* U+0687 ARABIC LETTER TCHEHEH */ + {0x0000u, 0x0000u, 0xFB89u, 0xFB88u}, /* U+0688 ARABIC LETTER DDAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0689 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068B */ + {0x0000u, 0x0000u, 0xFB85u, 0xFB84u}, /* U+068C ARABIC LETTER DAHAL */ + {0x0000u, 0x0000u, 0xFB83u, 0xFB82u}, /* U+068D ARABIC LETTER DDAHAL */ + {0x0000u, 0x0000u, 0xFB87u, 0xFB86u}, /* U+068E ARABIC LETTER DUL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0690 */ + {0x0000u, 0x0000u, 0xFB8Du, 0xFB8Cu}, /* U+0691 ARABIC LETTER RREH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0692 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0693 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0694 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0695 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0696 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0697 */ + {0x0000u, 0x0000u, 0xFB8Bu, 0xFB8Au}, /* U+0698 ARABIC LETTER JEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0699 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A0 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A1 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A3 */ + {0xFB6Cu, 0xFB6Du, 0xFB6Bu, 0xFB6Au}, /* U+06A4 ARABIC LETTER VEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A5 */ + {0xFB70u, 0xFB71u, 0xFB6Fu, 0xFB6Eu}, /* U+06A6 ARABIC LETTER PEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A8 */ + {0xFB90u, 0xFB91u, 0xFB8Fu, 0xFB8Eu}, /* U+06A9 ARABIC LETTER KEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AB */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AC */ + {0xFBD5u, 0xFBD6u, 0xFBD4u, 0xFBD3u}, /* U+06AD ARABIC LETTER NG */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AE */ + {0xFB94u, 0xFB95u, 0xFB93u, 0xFB92u}, /* U+06AF ARABIC LETTER GAF */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B0 */ + {0xFB9Cu, 0xFB9Du, 0xFB9Bu, 0xFB9Au}, /* U+06B1 ARABIC LETTER NGOEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B2 */ + {0xFB98u, 0xFB99u, 0xFB97u, 0xFB96u}, /* U+06B3 ARABIC LETTER GUEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B4 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B5 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B6 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B8 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B9 */ + {0x0000u, 0x0000u, 0xFB9Fu, 0xFB9Eu}, /* U+06BA ARABIC LETTER NOON GHUNNA */ + {0xFBA2u, 0xFBA3u, 0xFBA1u, 0xFBA0u}, /* U+06BB ARABIC LETTER RNOON */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BC */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BD */ + {0xFBACu, 0xFBADu, 0xFBABu, 0xFBAAu}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BF */ + {0x0000u, 0x0000u, 0xFBA5u, 0xFBA4u}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */ + {0xFBA8u, 0xFBA9u, 0xFBA7u, 0xFBA6u}, /* U+06C1 ARABIC LETTER HEH GOAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C3 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C4 */ + {0x0000u, 0x0000u, 0xFBE1u, 0xFBE0u}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */ + {0x0000u, 0x0000u, 0xFBDAu, 0xFBD9u}, /* U+06C6 ARABIC LETTER OE */ + {0x0000u, 0x0000u, 0xFBD8u, 0xFBD7u}, /* U+06C7 ARABIC LETTER U */ + {0x0000u, 0x0000u, 0xFBDCu, 0xFBDBu}, /* U+06C8 ARABIC LETTER YU */ + {0x0000u, 0x0000u, 0xFBE3u, 0xFBE2u}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CA */ + {0x0000u, 0x0000u, 0xFBDFu, 0xFBDEu}, /* U+06CB ARABIC LETTER VE */ + {0xFBFEu, 0xFBFFu, 0xFBFDu, 0xFBFCu}, /* U+06CC ARABIC LETTER FARSI YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CD */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CF */ + {0xFBE6u, 0xFBE7u, 0xFBE5u, 0xFBE4u}, /* U+06D0 ARABIC LETTER E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06D1 */ + {0x0000u, 0x0000u, 0xFBAFu, 0xFBAEu}, /* U+06D2 ARABIC LETTER YEH BARREE */ + {0x0000u, 0x0000u, 0xFBB1u, 0xFBB0u}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */ +}; + +#define SHAPING_TABLE_FIRST 0x0621u +#define SHAPING_TABLE_LAST 0x06D3u + + +static const struct ligature_set_t { + uint16_t first; + struct ligature_pairs_t { + uint16_t second; + uint16_t ligature; + } ligatures[4]; +} ligature_table[] = +{ + { 0xFEDFu, { + { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { 0xFE82u, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ + { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ + { 0xFE84u, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + }}, + { 0xFEE0u, { + { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { 0xFE82u, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ + { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ + { 0xFE84u, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + }}, +}; + + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh new file mode 100644 index 000000000..e70c48f42 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh @@ -0,0 +1,323 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH + + +/* + * The macros in the first part of this file are generic macros that can + * be used to define the bytes for OpenType table data in code in a + * readable manner. We can move the macros to reside with their respective + * struct types, but since we only use these to define one data table, the + * Windows-1256 Arabic shaping table in this file, we keep them here. + */ + + +/* First we measure, then we cut. */ +#ifndef OT_MEASURE +#define OT_MEASURE +#define OT_TABLE_START static const struct TABLE_NAME { +#define OT_TABLE_END } +#define OT_LABEL_START(Name) unsigned char Name[ +#define OT_LABEL_END ]; +#define OT_BYTE(u8) +1/*byte*/ +#define OT_USHORT(u16) +2/*bytes*/ +#else +#undef OT_MEASURE +#define OT_TABLE_START TABLE_NAME = { +#define OT_TABLE_END }; +#define OT_LABEL_START(Name) { +#define OT_LABEL_END }, +#define OT_BYTE(u8) (u8), +#define OT_USHORT(u16) (unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu), +#define OT_COUNT(Name, ItemSize) ((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \ + / (unsigned int)(ItemSize) \ + /* OT_ASSERT it's divisible (and positive). */) +#define OT_DISTANCE(From,To) ((unsigned int) \ + ((char*)(&((struct TABLE_NAME*)0)->To) - \ + (char*)(&((struct TABLE_NAME*)0)->From)) \ + /* OT_ASSERT it's positive. */) +#endif + + +#define OT_LABEL(Name) \ + OT_LABEL_END \ + OT_LABEL_START(Name) + +/* Whenever we receive an argument that is a list, it will expand to + * contain commas. That cannot be passed to another macro because the + * commas will throw off the preprocessor. The solution is to wrap + * the passed-in argument in OT_LIST() before passing to the next macro. + * Unfortunately this trick requires vararg macros. */ +#define OT_LIST(...) __VA_ARGS__ + + +/* + * Basic Types + */ + +#define OT_TAG(a,b,c,d) \ + OT_BYTE(a) OT_BYTE(b) OT_BYTE(c) OT_BYTE(d) + +#define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \ + OT_USHORT(OT_DISTANCE(From, To)) + +#define OT_GLYPHID /* GlyphID */ \ + OT_USHORT + +#define OT_UARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(OT_COUNT(Name##Data, 2)) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + +#define OT_UHEADLESSARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(OT_COUNT(Name##Data, 2) + 1) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + + +/* + * Common Types + */ + +#define OT_LOOKUP_FLAG_IGNORE_MARKS 0x08u + +#define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \ + OT_LABEL_START(Name) \ + OT_USHORT(LookupType) \ + OT_USHORT(LookupFlag) \ + OT_LABEL_END \ + OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets)) + +#define OT_SUBLOOKUP(Name, SubFormat, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(SubFormat) \ + Items + +#define OT_COVERAGE1(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(1) \ + OT_LABEL_END \ + OT_UARRAY(Name##Glyphs, OT_LIST(Items)) + + +/* + * GSUB + */ + +#define OT_LOOKUP_TYPE_SUBST_SINGLE 1u +#define OT_LOOKUP_TYPE_SUBST_LIGATURE 4u + +#define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \ + OT_SUBLOOKUP(Name, 2, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \ + /* ASSERT_STATIC_EXPR_ZERO (len(FromGlyphs) == len(ToGlyphs)) */ + +#define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \ + OT_SUBLOOKUP(Name, 1, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \ + /* ASSERT_STATIC_EXPR_ZERO (len(FirstGlyphs) == len(LigatureSetOffsets)) */ + +#define OT_LIGATURE_SET(Name, LigatureSetOffsets) \ + OT_UARRAY(Name, OT_LIST(LigatureSetOffsets)) + +#define OT_LIGATURE(Name, Components, LigGlyph) \ + OT_LABEL_START(Name) \ + LigGlyph \ + OT_LABEL_END \ + OT_UHEADLESSARRAY(Name##ComponentsArray, OT_LIST(Components)) + +/* + * + * Start of Windows-1256 shaping table. + * + */ + +/* Table name. */ +#define TABLE_NAME arabic_win1256_gsub_lookups + +/* Table manifest. */ +#define MANIFEST(Items) \ + OT_LABEL_START(manifest) \ + OT_USHORT(OT_COUNT(manifestData, 6)) \ + OT_LABEL(manifestData) \ + Items \ + OT_LABEL_END + +#define MANIFEST_LOOKUP(Tag, Name) \ + Tag \ + OT_OFFSET(manifest, Name) + +/* Shorthand. */ +#define G OT_GLYPHID + +/* + * Table Start + */ +OT_TABLE_START + + +/* + * Manifest + */ +MANIFEST( + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligLookup) + MANIFEST_LOOKUP(OT_TAG('i','n','i','t'), initLookup) + MANIFEST_LOOKUP(OT_TAG('m','e','d','i'), mediLookup) + MANIFEST_LOOKUP(OT_TAG('f','i','n','a'), finaLookup) + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligMarksLookup) +) + +/* + * Lookups + */ +OT_LOOKUP(initLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(initLookup, initmediSubLookup) + OT_OFFSET(initLookup, initSubLookup) +) +OT_LOOKUP(mediLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(mediLookup, initmediSubLookup) + OT_OFFSET(mediLookup, mediSubLookup) + OT_OFFSET(mediLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(finaLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(finaLookup, finaSubLookup) + /* We don't need this one currently as the sequence inherits masks + * from the first item. Just in case we change that in the future + * to be smart about Arabic masks when ligating... */ + OT_OFFSET(finaLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(rligLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(rligLookup, lamAlefLigaturesSubLookup) +) +OT_LOOKUP(rligMarksLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, 0, + OT_OFFSET(rligMarksLookup, shaddaLigaturesSubLookup) +) + +/* + * init/medi/fina forms + */ +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initmediSubLookup, + G(198) G(200) G(201) G(202) G(203) G(204) G(205) G(206) G(211) + G(212) G(213) G(214) G(223) G(225) G(227) G(228) G(236) G(237), + G(162) G(4) G(5) G(5) G(6) G(7) G(9) G(11) G(13) + G(14) G(15) G(26) G(140) G(141) G(142) G(143) G(154) G(154) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(27) G(30) G(128) G(131) G(144) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(mediSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(28) G(31) G(129) G(138) G(149) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(finaSubLookup, + G(194) G(195) G(197) G(198) G(199) G(201) G(204) G(205) G(206) + G(218) G(219) G(229) G(236) G(237), + G(2) G(1) G(3) G(181) G(0) G(159) G(8) G(10) G(12) + G(29) G(127) G(152) G(160) G(156) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(medifinaLamAlefSubLookup, + G(165) G(178) G(180) G(252), + G(170) G(179) G(185) G(255) +) + +/* + * Lam+Alef ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(lamAlefLigaturesSubLookup, + G(225), + OT_OFFSET(lamAlefLigaturesSubLookup, lamLigatureSet) +) +OT_LIGATURE_SET(lamLigatureSet, + OT_OFFSET(lamLigatureSet, lamInitLigature1) + OT_OFFSET(lamLigatureSet, lamInitLigature2) + OT_OFFSET(lamLigatureSet, lamInitLigature3) + OT_OFFSET(lamLigatureSet, lamInitLigature4) +) +OT_LIGATURE(lamInitLigature1, G(199), G(165)) +OT_LIGATURE(lamInitLigature2, G(195), G(178)) +OT_LIGATURE(lamInitLigature3, G(194), G(180)) +OT_LIGATURE(lamInitLigature4, G(197), G(252)) + +/* + * Shadda ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(shaddaLigaturesSubLookup, + G(248), + OT_OFFSET(shaddaLigaturesSubLookup, shaddaLigatureSet) +) +OT_LIGATURE_SET(shaddaLigatureSet, + OT_OFFSET(shaddaLigatureSet, shaddaLigature1) + OT_OFFSET(shaddaLigatureSet, shaddaLigature2) + OT_OFFSET(shaddaLigatureSet, shaddaLigature3) +) +OT_LIGATURE(shaddaLigature1, G(243), G(172)) +OT_LIGATURE(shaddaLigature2, G(245), G(173)) +OT_LIGATURE(shaddaLigature3, G(246), G(175)) + +/* + * Table end + */ +OT_TABLE_END + + +/* + * Clean up + */ +#undef OT_TABLE_START +#undef OT_TABLE_END +#undef OT_LABEL_START +#undef OT_LABEL_END +#undef OT_BYTE +#undef OT_USHORT +#undef OT_DISTANCE +#undef OT_COUNT + +/* + * Include a second time to get the table data... + */ +#if 0 +#include "hb-private.hh" /* Make check-includes.sh happy. */ +#endif +#ifdef OT_MEASURE +#include "hb-ot-shape-complex-arabic-win1256.hh" +#endif + +#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc new file mode 100644 index 000000000..56ec5cd65 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc @@ -0,0 +1,624 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-arabic-private.hh" +#include "hb-ot-shape-private.hh" + + +#ifndef HB_DEBUG_ARABIC +#define HB_DEBUG_ARABIC (HB_DEBUG+0) +#endif + + +/* buffer var allocations */ +#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ + +#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 + +/* See: + * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ +#define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ + (FLAG_SAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ + /*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) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) + + +/* + * Joining types: + */ + +/* + * Bits used in the joining tables + */ +enum hb_arabic_joining_type_t { + JOINING_TYPE_U = 0, + JOINING_TYPE_L = 1, + JOINING_TYPE_R = 2, + JOINING_TYPE_D = 3, + JOINING_TYPE_C = JOINING_TYPE_D, + JOINING_GROUP_ALAPH = 4, + JOINING_GROUP_DALATH_RISH = 5, + NUM_STATE_MACHINE_COLS = 6, + + JOINING_TYPE_T = 7, + JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */ +}; + +#include "hb-ot-shape-complex-arabic-table.hh" + +static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) +{ + unsigned int j_type = joining_type(u); + if (likely (j_type != JOINING_TYPE_X)) + return j_type; + + return (FLAG_SAFE(gen_cat) & + (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT)) + ) ? JOINING_TYPE_T : JOINING_TYPE_U; +} + +#define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3') + +static const hb_tag_t arabic_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('f','i','n','a'), + HB_TAG('f','i','n','2'), + HB_TAG('f','i','n','3'), + HB_TAG('m','e','d','i'), + HB_TAG('m','e','d','2'), + HB_TAG('i','n','i','t'), + HB_TAG_NONE +}; + + +/* Same order as the feature array */ +enum arabic_action_t { + ISOL, + FINA, + FIN2, + FIN3, + MEDI, + MED2, + INIT, + + NONE, + + ARABIC_NUM_FEATURES = NONE, + + /* We abuse the same byte for other things... */ + STCH_FIXED, + STCH_REPEATING, +}; + +static const struct arabic_state_table_entry { + uint8_t prev_action; + uint8_t curr_action; + uint16_t next_state; +} arabic_state_table[][NUM_STATE_MACHINE_COLS] = +{ + /* jt_U, jt_L, jt_R, jt_D, jg_ALAPH, jg_DALATH_RISH */ + + /* State 0: prev was U, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, }, + + /* State 1: prev was R or ISOL/ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, }, + + /* State 2: prev was D/L in ISOL form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, }, + + /* State 3: prev was D in FINA form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, }, + + /* State 4: prev was FINA ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, }, + + /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, }, + + /* State 6: prev was DALATH/RISH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, } +}; + + +static void +nuke_joiners (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_arabic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* We apply features according to the Arabic spec, with pauses + * in between most. + * + * The pause between init/medi/... and rlig is required. See eg: + * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 + * + * The pauses between init/medi/... themselves are not necessarily + * needed as only one of those features is applied to any character. + * The only difference it makes is when fonts have contextual + * substitutions. We now follow the order of the spec, which makes + * for better experience if that's what Uniscribe is doing. + * + * At least for Arabic, looks like Uniscribe has a pause between + * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't + * work. However, testing shows that rlig and calt are applied + * together for Mongolian in Uniscribe. As such, we only add a + * pause for Arabic, not other scripts. + */ + + map->add_gsub_pause (nuke_joiners); + + map->add_global_bool_feature (HB_TAG('s','t','c','h')); + map->add_gsub_pause (record_stch); + + map->add_global_bool_feature (HB_TAG('c','c','m','p')); + map->add_global_bool_feature (HB_TAG('l','o','c','l')); + + map->add_gsub_pause (NULL); + + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) + { + bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); + map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE); + map->add_gsub_pause (NULL); + } + + map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK); + if (plan->props.script == HB_SCRIPT_ARABIC) + map->add_gsub_pause (arabic_fallback_shape); + + map->add_global_bool_feature (HB_TAG('c','a','l','t')); + + /* The spec includes 'cswh'. Earlier versions of Windows + * used to enable this by default, but testing suggests + * that Windows 8 and later do not enable it by default, + * and spec now says 'Off by default'. + * We disabled this in ae23c24c32. + * Note that IranNastaliq uses this feature extensively + * to fixup broken glyph sequences. Oh well... + * Test case: U+0643,U+0640,U+0631. */ + //map->add_gsub_pause (NULL); + //map->add_global_bool_feature (HB_TAG('c','s','w','h')); + map->add_global_bool_feature (HB_TAG('m','s','e','t')); +} + +#include "hb-ot-shape-complex-arabic-fallback.hh" + +struct arabic_shape_plan_t +{ + ASSERT_POD (); + + /* The "+ 1" in the next array is to accommodate for the "NONE" command, + * which is not an OpenType feature, but this simplifies the code by not + * having to do a "if (... < NONE) ..." and just rely on the fact that + * mask_array[NONE] == 0. */ + hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; + + arabic_fallback_plan_t *fallback_plan; + + unsigned int do_fallback : 1; + unsigned int has_stch : 1; +}; + +void * +data_create_arabic (const hb_ot_shape_plan_t *plan) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t)); + if (unlikely (!arabic_plan)) + return NULL; + + arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; + arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { + arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); + arabic_plan->do_fallback = arabic_plan->do_fallback && + (FEATURE_IS_SYRIAC (arabic_features[i]) || + plan->map.needs_fallback (arabic_features[i])); + } + + return arabic_plan; +} + +void +data_destroy_arabic (void *data) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data; + + arabic_fallback_plan_destroy (arabic_plan->fallback_plan); + + free (data); +} + +static void +arabic_joining (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + unsigned int prev = (unsigned int) -1, state = 0; + + /* Check pre-context */ + for (unsigned int i = 0; i < buffer->context_len[0]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + state = entry->next_state; + break; + } + + for (unsigned int i = 0; i < count; i++) + { + unsigned int this_type = get_joining_type (info[i].codepoint, _hb_glyph_info_get_general_category (&info[i])); + + if (unlikely (this_type == JOINING_TYPE_T)) { + info[i].arabic_shaping_action() = NONE; + continue; + } + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + + if (entry->prev_action != NONE && prev != (unsigned int) -1) + info[prev].arabic_shaping_action() = entry->prev_action; + + info[i].arabic_shaping_action() = entry->curr_action; + + prev = i; + state = entry->next_state; + } + + for (unsigned int i = 0; i < buffer->context_len[1]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + if (entry->prev_action != NONE && prev != (unsigned int) -1) + info[prev].arabic_shaping_action() = entry->prev_action; + break; + } +} + +static void +mongolian_variation_selectors (hb_buffer_t *buffer) +{ + /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du))) + info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action(); +} + +void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); + + arabic_joining (buffer); + if (script == HB_SCRIPT_MONGOLIAN) + mongolian_variation_selectors (buffer); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; +} + +static void +setup_masks_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); +} + + +static void +nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_zwj (&info[i])) + _hb_glyph_info_flip_joiners (&info[i]); +} + +static void +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + + if (!arabic_plan->do_fallback) + return; + +retry: + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan); + if (unlikely (!fallback_plan)) + { + /* This sucks. We need a font to build the fallback plan... */ + fallback_plan = arabic_fallback_plan_create (plan, font); + if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) { + arabic_fallback_plan_destroy (fallback_plan); + goto retry; + } + } + + arabic_fallback_plan_shape (fallback_plan, font, buffer); +} + +/* + * Stretch feature: "stch". + * See example here: + * https://www.microsoft.com/typography/OpenTypeDev/syriac/intro.htm + * We implement this in a generic way, such that the Arabic subtending + * marks can use it as well. + */ + +static void +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + if (!arabic_plan->has_stch) + return; + + /* 'stch' feature was just applied. Look for anything that multiplied, + * and record it for stch treatment later. Note that rtlm, frac, etc + * are applied before stch, but we assume that they didn't result in + * anything multiplying into 5 pieces, so it's safe-ish... */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_multiplied (&info[i]))) + { + unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); + info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; + } +} + +static void +apply_stch (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))) + return; + + /* The Arabic shaper currently always processes in RTL mode, so we should + * stretch / position the stretched pieces to the left / preceding glyphs. */ + + /* We do a two pass implementation: + * First pass calculates the exact number of extra glyphs we need, + * We then enlarge buffer to have that much room, + * Second pass applies the stretch, copying things to the end of buffer. + */ + + int sign = font->x_scale < 0 ? -1 : +1; + unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT + typedef enum { MEASURE, CUT } step_t; + + for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1)) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int new_len = count + extra_glyphs_needed; // write head during CUT + unsigned int j = new_len; + for (unsigned int i = count; i; i--) + { + if (!hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + if (step == CUT) + { + --j; + info[j] = info[i - 1]; + pos[j] = pos[i - 1]; + } + continue; + } + + /* Yay, justification! */ + + hb_position_t w_total = 0; // Total to be filled + hb_position_t w_fixed = 0; // Sum of fixed tiles + hb_position_t w_repeating = 0; // Sum of repeating tiles + int n_fixed = 0; + int n_repeating = 0; + + unsigned int end = i; + while (i && + hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + i--; + hb_position_t width = font->get_glyph_h_advance (info[i].codepoint); + if (info[i].arabic_shaping_action() == STCH_FIXED) + { + w_fixed += width; + n_fixed++; + } + else + { + w_repeating += width; + n_repeating++; + } + } + unsigned int start = i; + unsigned int context = i; + while (context && + !hb_in_range<unsigned> (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && + (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || + HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) + { + context--; + w_total += pos[context].x_advance; + } + i++; // Don't touch i again. + + DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)", + step == MEASURE ? "measuring" : "cutting", context, start, end); + DEBUG_MSG (ARABIC, NULL, "rest of word: count=%d width %d", start - context, w_total); + DEBUG_MSG (ARABIC, NULL, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); + DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); + + /* Number of additional times to repeat each repeating tile. */ + int n_copies = 0; + + hb_position_t w_remaining = w_total - w_fixed; + if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) + n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; + + /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ + hb_position_t extra_repeat_overlap = 0; + hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); + if (shortfall > 0) + { + ++n_copies; + hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; + if (excess > 0) + extra_repeat_overlap = excess / (n_copies * n_repeating); + } + + if (step == MEASURE) + { + extra_glyphs_needed += n_copies * n_repeating; + DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", n_copies); + } + else + { + hb_position_t x_offset = 0; + for (unsigned int k = end; k > start; k--) + { + hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint); + + unsigned int repeat = 1; + if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) + repeat += n_copies; + + DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d", + repeat, info[k - 1].codepoint, j); + for (unsigned int n = 0; n < repeat; n++) + { + x_offset -= width; + if (n > 0) + x_offset += extra_repeat_overlap; + pos[k - 1].x_offset = x_offset; + /* Append copy. */ + --j; + info[j] = info[k - 1]; + pos[j] = pos[k - 1]; + } + } + } + } + + if (step == MEASURE) + { + if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) + break; + } + else + { + assert (j == 0); + buffer->len = new_len; + } + } +} + + +static void +postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + apply_stch (plan, buffer, font); + + HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); +} + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = +{ + "arabic", + collect_features_arabic, + NULL, /* override_features */ + data_create_arabic, + data_destroy_arabic, + NULL, /* preprocess_text */ + postprocess_glyphs_arabic, + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_arabic, + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc new file mode 100644 index 000000000..42830ab61 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc @@ -0,0 +1,46 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-private.hh" + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default = +{ + "default", + NULL, /* collect_features */ + NULL, /* override_features */ + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc new file mode 100644 index 000000000..eb95a28c0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc @@ -0,0 +1,425 @@ +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-private.hh" + + +/* Hangul shaper */ + + +/* Same order as the feature array below */ +enum { + NONE, + + LJMO, + VJMO, + TJMO, + + FIRST_HANGUL_FEATURE = LJMO, + HANGUL_FEATURE_COUNT = TJMO + 1 +}; + +static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] = +{ + HB_TAG_NONE, + HB_TAG('l','j','m','o'), + HB_TAG('v','j','m','o'), + HB_TAG('t','j','m','o') +}; + +static void +collect_features_hangul (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++) + map->add_feature (hangul_features[i], 1, F_NONE); +} + +static void +override_features_hangul (hb_ot_shape_planner_t *plan) +{ + /* Uniscribe does not apply 'calt' for Hangul, and certain fonts + * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups + * in calt, which is not desirable. */ + plan->map.add_feature (HB_TAG('c','a','l','t'), 0, F_GLOBAL); +} + +struct hangul_shape_plan_t +{ + ASSERT_POD (); + + hb_mask_t mask_array[HANGUL_FEATURE_COUNT]; +}; + +static void * +data_create_hangul (const hb_ot_shape_plan_t *plan) +{ + hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) calloc (1, sizeof (hangul_shape_plan_t)); + if (unlikely (!hangul_plan)) + return NULL; + + for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++) + hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]); + + return hangul_plan; +} + +static void +data_destroy_hangul (void *data) +{ + free (data); +} + +/* Constants for algorithmic hangul syllable [de]composition. */ +#define LBase 0x1100u +#define VBase 0x1161u +#define TBase 0x11A7u +#define LCount 19u +#define VCount 21u +#define TCount 28u +#define SBase 0xAC00u +#define NCount (VCount * TCount) +#define SCount (LCount * NCount) + +#define isCombiningL(u) (hb_in_range ((u), LBase, LBase+LCount-1)) +#define isCombiningV(u) (hb_in_range ((u), VBase, VBase+VCount-1)) +#define isCombiningT(u) (hb_in_range ((u), TBase+1, TBase+TCount-1)) +#define isCombinedS(u) (hb_in_range ((u), SBase, SBase+SCount-1)) + +#define isL(u) (hb_in_ranges ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu)) +#define isV(u) (hb_in_ranges ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u)) +#define isT(u) (hb_in_ranges ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu)) + +#define isHangulTone(u) (hb_in_range ((u), 0x302Eu, 0x302Fu)) + +/* buffer var allocations */ +#define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */ + +static bool +is_zero_width_char (hb_font_t *font, + hb_codepoint_t unicode) +{ + hb_codepoint_t glyph; + return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0; +} + +static void +preprocess_text_hangul (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature); + + /* Hangul syllables come in two shapes: LV, and LVT. Of those: + * + * - LV can be precomposed, or decomposed. Lets call those + * <LV> and <L,V>, + * - LVT can be fully precomposed, partically precomposed, or + * fully decomposed. Ie. <LVT>, <LV,T>, or <L,V,T>. + * + * The composition / decomposition is mechanical. However, not + * all <L,V> sequences compose, and not all <LV,T> sequences + * compose. + * + * Here are the specifics: + * + * - <L>: U+1100..115F, U+A960..A97F + * - <V>: U+1160..11A7, U+D7B0..D7C7 + * - <T>: U+11A8..11FF, U+D7CB..D7FB + * + * - Only the <L,V> sequences for the 11xx ranges combine. + * - Only <LV,T> sequences for T in U+11A8..11C3 combine. + * + * Here is what we want to accomplish in this shaper: + * + * - If the whole syllable can be precomposed, do that, + * - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features. + * - If a valid syllable is followed by a Hangul tone mark, reorder the tone + * mark to precede the whole syllable - unless it is a zero-width glyph, in + * which case we leave it untouched, assuming it's designed to overstrike. + * + * That is, of the different possible syllables: + * + * <L> + * <L,V> + * <L,V,T> + * <LV> + * <LVT> + * <LV, T> + * + * - <L> needs no work. + * + * - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we + * should fully decompose them if font supports. + * + * - <L,V> and <L,V,T> we should compose if the whole thing can be composed. + * + * - <LV,T> we should compose if the whole thing can be composed, otherwise we should + * decompose. + */ + + buffer->clear_output (); + unsigned int start = 0, end = 0; /* Extent of most recently seen syllable; + * valid only if start < end + */ + unsigned int count = buffer->len; + + for (buffer->idx = 0; buffer->idx < count && !buffer->in_error;) + { + hb_codepoint_t u = buffer->cur().codepoint; + + if (isHangulTone (u)) + { + /* + * We could cache the width of the tone marks and the existence of dotted-circle, + * but the use of the Hangul tone mark characters seems to be rare enough that + * I didn't bother for now. + */ + if (start < end && end == buffer->out_len) + { + /* Tone mark follows a valid syllable; move it in front, unless it's zero width. */ + buffer->next_glyph (); + if (!is_zero_width_char (font, u)) + { + buffer->merge_out_clusters (start, end + 1); + hb_glyph_info_t *info = buffer->out_info; + hb_glyph_info_t tone = info[end]; + memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t)); + info[start] = tone; + } + } + else + { + /* No valid syllable as base for tone mark; try to insert dotted circle. */ + if (font->has_glyph (0x25CCu)) + { + hb_codepoint_t chars[2]; + if (!is_zero_width_char (font, u)) { + chars[0] = u; + chars[1] = 0x25CCu; + } else { + chars[0] = 0x25CCu; + chars[1] = u; + } + buffer->replace_glyphs (1, 2, chars); + } + else + { + /* No dotted circle available in the font; just leave tone mark untouched. */ + buffer->next_glyph (); + } + } + start = end = buffer->out_len; + continue; + } + + start = buffer->out_len; /* Remember current position as a potential syllable start; + * will only be used if we set end to a later position. + */ + + if (isL (u) && buffer->idx + 1 < count) + { + hb_codepoint_t l = u; + hb_codepoint_t v = buffer->cur(+1).codepoint; + if (isV (v)) + { + /* Have <L,V> or <L,V,T>. */ + hb_codepoint_t t = 0; + unsigned int tindex = 0; + if (buffer->idx + 2 < count) + { + t = buffer->cur(+2).codepoint; + if (isT (t)) + tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */ + else + t = 0; /* The next character was not a trailing jamo. */ + } + + /* We've got a syllable <L,V,T?>; see if it can potentially be composed. */ + if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t))) + { + /* Try to compose; if this succeeds, end is set to start+1. */ + hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex; + if (font->has_glyph (s)) + { + buffer->replace_glyphs (t ? 3 : 2, 1, &s); + if (unlikely (buffer->in_error)) + return; + end = start + 1; + continue; + } + } + + /* We didn't compose, either because it's an Old Hangul syllable without a + * precomposed character in Unicode, or because the font didn't support the + * necessary precomposed glyph. + * Set jamo features on the individual glyphs, and advance past them. + */ + buffer->cur().hangul_shaping_feature() = LJMO; + buffer->next_glyph (); + buffer->cur().hangul_shaping_feature() = VJMO; + buffer->next_glyph (); + if (t) + { + buffer->cur().hangul_shaping_feature() = TJMO; + buffer->next_glyph (); + end = start + 3; + } + else + end = start + 2; + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + } + + else if (isCombinedS (u)) + { + /* Have <LV>, <LVT>, or <LV,T> */ + hb_codepoint_t s = u; + bool has_glyph = font->has_glyph (s); + unsigned int lindex = (s - SBase) / NCount; + unsigned int nindex = (s - SBase) % NCount; + unsigned int vindex = nindex / TCount; + unsigned int tindex = nindex % TCount; + + if (!tindex && + buffer->idx + 1 < count && + isCombiningT (buffer->cur(+1).codepoint)) + { + /* <LV,T>, try to combine. */ + unsigned int new_tindex = buffer->cur(+1).codepoint - TBase; + hb_codepoint_t new_s = s + new_tindex; + if (font->has_glyph (new_s)) + { + buffer->replace_glyphs (2, 1, &new_s); + if (unlikely (buffer->in_error)) + return; + end = start + 1; + continue; + } + } + + /* Otherwise, decompose if font doesn't support <LV> or <LVT>, + * or if having non-combining <LV,T>. Note that we already handled + * combining <LV,T> above. */ + if (!has_glyph || + (!tindex && + buffer->idx + 1 < count && + isT (buffer->cur(+1).codepoint))) + { + hb_codepoint_t decomposed[3] = {LBase + lindex, + VBase + vindex, + TBase + tindex}; + if (font->has_glyph (decomposed[0]) && + font->has_glyph (decomposed[1]) && + (!tindex || font->has_glyph (decomposed[2]))) + { + unsigned int s_len = tindex ? 3 : 2; + buffer->replace_glyphs (1, s_len, decomposed); + if (unlikely (buffer->in_error)) + return; + + /* We decomposed S: apply jamo features to the individual glyphs + * that are now in buffer->out_info. + */ + hb_glyph_info_t *info = buffer->out_info; + + /* If we decomposed an LV because of a non-combining T following, + * we want to include this T in the syllable. + */ + if (has_glyph && !tindex) + { + buffer->next_glyph (); + s_len++; + } + end = start + s_len; + + unsigned int i = start; + info[i++].hangul_shaping_feature() = LJMO; + info[i++].hangul_shaping_feature() = VJMO; + if (i < end) + info[i++].hangul_shaping_feature() = TJMO; + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + } + + if (has_glyph) + { + /* We didn't decompose the S, so just advance past it. */ + end = start + 1; + buffer->next_glyph (); + continue; + } + } + + /* Didn't find a recognizable syllable, so we leave end <= start; + * this will prevent tone-mark reordering happening. + */ + buffer->next_glyph (); + } + buffer->swap_buffers (); +} + +static void +setup_masks_hangul (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data; + + if (likely (hangul_plan)) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++, info++) + info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()]; + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul = +{ + "hangul", + collect_features_hangul, + override_features_hangul, + data_create_hangul, + data_destroy_hangul, + preprocess_text_hangul, + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_hangul, + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-hebrew.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-hebrew.cc new file mode 100644 index 000000000..96f249461 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hebrew.cc @@ -0,0 +1,186 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-private.hh" + + +static bool +compose_hebrew (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Hebrew presentation-form shaping. + * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 + * Hebrew presentation forms with dagesh, for characters U+05D0..05EA; + * Note that some letters do not have a dagesh presForm encoded. + */ + static const hb_codepoint_t sDageshForms[0x05EAu - 0x05D0u + 1] = { + 0xFB30u, /* ALEF */ + 0xFB31u, /* BET */ + 0xFB32u, /* GIMEL */ + 0xFB33u, /* DALET */ + 0xFB34u, /* HE */ + 0xFB35u, /* VAV */ + 0xFB36u, /* ZAYIN */ + 0x0000u, /* HET */ + 0xFB38u, /* TET */ + 0xFB39u, /* YOD */ + 0xFB3Au, /* FINAL KAF */ + 0xFB3Bu, /* KAF */ + 0xFB3Cu, /* LAMED */ + 0x0000u, /* FINAL MEM */ + 0xFB3Eu, /* MEM */ + 0x0000u, /* FINAL NUN */ + 0xFB40u, /* NUN */ + 0xFB41u, /* SAMEKH */ + 0x0000u, /* AYIN */ + 0xFB43u, /* FINAL PE */ + 0xFB44u, /* PE */ + 0x0000u, /* FINAL TSADI */ + 0xFB46u, /* TSADI */ + 0xFB47u, /* QOF */ + 0xFB48u, /* RESH */ + 0xFB49u, /* SHIN */ + 0xFB4Au /* TAV */ + }; + + bool found = (bool) c->unicode->compose (a, b, ab); + + if (!found && !c->plan->has_mark) + { + /* Special-case Hebrew presentation forms that are excluded from + * standard normalization, but wanted for old fonts. */ + switch (b) { + case 0x05B4u: /* HIRIQ */ + if (a == 0x05D9u) { /* YOD */ + *ab = 0xFB1Du; + found = true; + } + break; + case 0x05B7u: /* patah */ + if (a == 0x05F2u) { /* YIDDISH YOD YOD */ + *ab = 0xFB1Fu; + found = true; + } else if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Eu; + found = true; + } + break; + case 0x05B8u: /* QAMATS */ + if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Fu; + found = true; + } + break; + case 0x05B9u: /* HOLAM */ + if (a == 0x05D5u) { /* VAV */ + *ab = 0xFB4Bu; + found = true; + } + break; + case 0x05BCu: /* DAGESH */ + if (a >= 0x05D0u && a <= 0x05EAu) { + *ab = sDageshForms[a - 0x05D0u]; + found = (*ab != 0); + } else if (a == 0xFB2Au) { /* SHIN WITH SHIN DOT */ + *ab = 0xFB2Cu; + found = true; + } else if (a == 0xFB2Bu) { /* SHIN WITH SIN DOT */ + *ab = 0xFB2Du; + found = true; + } + break; + case 0x05BFu: /* RAFE */ + switch (a) { + case 0x05D1u: /* BET */ + *ab = 0xFB4Cu; + found = true; + break; + case 0x05DBu: /* KAF */ + *ab = 0xFB4Du; + found = true; + break; + case 0x05E4u: /* PE */ + *ab = 0xFB4Eu; + found = true; + break; + } + break; + case 0x05C1u: /* SHIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Au; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Cu; + found = true; + } + break; + case 0x05C2u: /* SIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Bu; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Du; + found = true; + } + break; + } + } + + return found; +} + +static bool +disable_otl_hebrew (const hb_ot_shape_plan_t *plan) +{ + /* For Hebrew shaper, use fallback if GPOS does not have 'hebr' + * script. This matches Uniscribe better, and makes fonts like + * Arial that have GSUB/GPOS/GDEF but no data for Hebrew work. + * See: + * https://github.com/behdad/harfbuzz/issues/347#issuecomment-267838368 + */ + return plan->map.chosen_script[1] != HB_TAG ('h','e','b','r'); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew = +{ + "hebrew", + NULL, /* collect_features */ + NULL, /* override_features */ + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + compose_hebrew, + NULL, /* setup_masks */ + disable_otl_hebrew, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh new file mode 100644 index 000000000..5a7a265c8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh @@ -0,0 +1,1564 @@ + +#line 1 "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" + + +#line 36 "hb-ot-shape-complex-indic-machine.hh" +static const unsigned char _indic_syllable_machine_trans_keys[] = { + 8u, 8u, 1u, 16u, 8u, 13u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, + 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, + 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 8u, 6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, + 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 8u, 8u, 1u, 16u, 8u, 13u, + 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, + 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, + 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, + 4u, 14u, 4u, 14u, 8u, 8u, 1u, 16u, 8u, 13u, 5u, 8u, 5u, 7u, 7u, 7u, + 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, + 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, + 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, + 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 8u, 8u, 1u, 16u, + 8u, 13u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, + 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, + 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, + 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 5u, 8u, 4u, 14u, 4u, 14u, 5u, 8u, + 5u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, + 5u, 7u, 7u, 7u, 8u, 8u, 1u, 16u, 8u, 13u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, + 16u, 16u, 8u, 8u, 1u, 18u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 14u, 5u, 14u, + 5u, 10u, 9u, 10u, 9u, 9u, 9u, 10u, 9u, 10u, 9u, 9u, 5u, 10u, 3u, 13u, + 3u, 10u, 5u, 10u, 3u, 10u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, + 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, + 5u, 14u, 3u, 14u, 1u, 16u, 4u, 14u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, + 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, + 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, + 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, + 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 14u, 5u, 14u, 5u, 10u, + 9u, 10u, 9u, 9u, 9u, 10u, 9u, 10u, 9u, 9u, 5u, 10u, 3u, 13u, 3u, 10u, + 5u, 10u, 3u, 10u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, + 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, + 3u, 14u, 1u, 16u, 4u, 14u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, + 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 14u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 5u, 14u, 5u, 14u, 5u, 10u, 9u, 10u, 9u, 9u, + 9u, 10u, 9u, 10u, 9u, 9u, 5u, 10u, 3u, 13u, 3u, 10u, 5u, 10u, 3u, 10u, + 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, + 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 1u, 16u, + 4u, 14u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, + 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, + 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 14u, 3u, 17u, 4u, 14u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 5u, 14u, 5u, 14u, 5u, 10u, 9u, 10u, 9u, 9u, + 9u, 10u, 9u, 10u, 9u, 9u, 5u, 10u, 3u, 13u, 3u, 10u, 5u, 10u, 3u, 10u, + 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, + 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 1u, 16u, + 4u, 14u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, + 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, + 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 1u, 17u, 3u, 17u, + 1u, 17u, 4u, 14u, 5u, 10u, 9u, 10u, 9u, 9u, 9u, 10u, 9u, 10u, 9u, 9u, + 5u, 10u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 3u, 17u, 3u, 17u, 1u, 16u, + 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, + 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 13u, 3u, 10u, 5u, 10u, 3u, 10u, + 3u, 13u, 1u, 16u, 3u, 10u, 5u, 10u, 5u, 10u, 9u, 10u, 9u, 9u, 9u, 10u, + 9u, 10u, 9u, 9u, 5u, 10u, 0 +}; + +static const char _indic_syllable_machine_key_spans[] = { + 1, 16, 6, 4, 3, 1, 4, 3, + 1, 4, 3, 1, 4, 3, 1, 5, + 1, 1, 5, 1, 1, 5, 1, 1, + 5, 1, 1, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 1, 16, 6, + 4, 3, 1, 4, 3, 1, 4, 3, + 1, 4, 3, 1, 5, 1, 1, 5, + 1, 1, 5, 1, 1, 5, 1, 1, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 1, 16, 6, 4, 3, 1, + 4, 3, 1, 4, 3, 1, 4, 3, + 1, 5, 1, 1, 5, 1, 1, 5, + 1, 1, 5, 1, 1, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 1, 16, + 6, 4, 3, 1, 4, 3, 1, 4, + 3, 1, 4, 3, 1, 5, 1, 1, + 5, 1, 1, 5, 1, 1, 5, 1, + 1, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 4, 11, 11, 4, + 3, 4, 3, 1, 4, 3, 1, 4, + 3, 1, 1, 16, 6, 5, 1, 1, + 5, 1, 1, 5, 1, 1, 5, 1, + 1, 1, 18, 15, 15, 14, 16, 15, + 15, 14, 16, 15, 15, 14, 16, 15, + 15, 14, 16, 15, 15, 14, 10, 10, + 6, 2, 1, 2, 2, 1, 6, 11, + 8, 6, 8, 11, 12, 12, 11, 10, + 12, 11, 10, 12, 11, 10, 12, 11, + 10, 12, 16, 11, 15, 15, 16, 16, + 16, 16, 16, 15, 15, 16, 16, 16, + 16, 16, 15, 15, 16, 16, 16, 16, + 16, 15, 15, 16, 16, 16, 16, 16, + 15, 15, 15, 15, 14, 16, 15, 15, + 14, 16, 15, 15, 14, 16, 15, 15, + 14, 16, 15, 15, 14, 10, 10, 6, + 2, 1, 2, 2, 1, 6, 11, 8, + 6, 8, 11, 12, 12, 11, 10, 12, + 11, 10, 12, 11, 10, 12, 11, 10, + 12, 16, 11, 15, 15, 16, 16, 16, + 16, 16, 15, 15, 16, 16, 16, 16, + 16, 15, 15, 16, 16, 16, 16, 16, + 15, 15, 16, 16, 16, 16, 11, 16, + 15, 15, 14, 16, 15, 15, 14, 16, + 15, 15, 14, 16, 15, 15, 14, 16, + 15, 15, 14, 10, 10, 6, 2, 1, + 2, 2, 1, 6, 11, 8, 6, 8, + 11, 12, 12, 11, 10, 12, 11, 10, + 12, 11, 10, 12, 11, 10, 12, 16, + 11, 15, 15, 16, 16, 16, 16, 16, + 15, 15, 16, 16, 16, 16, 16, 15, + 15, 16, 16, 16, 16, 16, 15, 15, + 16, 16, 16, 16, 16, 11, 15, 11, + 15, 15, 14, 16, 15, 15, 14, 16, + 15, 15, 14, 16, 15, 15, 14, 16, + 15, 15, 14, 10, 10, 6, 2, 1, + 2, 2, 1, 6, 11, 8, 6, 8, + 11, 12, 12, 11, 10, 12, 11, 10, + 12, 11, 10, 12, 11, 10, 12, 16, + 11, 15, 15, 16, 16, 16, 16, 16, + 15, 15, 16, 16, 16, 16, 16, 15, + 15, 16, 16, 16, 16, 16, 15, 15, + 16, 16, 16, 16, 16, 15, 17, 15, + 17, 11, 6, 2, 1, 2, 2, 1, + 6, 16, 15, 15, 14, 15, 15, 16, + 12, 11, 10, 12, 11, 10, 12, 11, + 10, 12, 11, 10, 11, 8, 6, 8, + 11, 16, 8, 6, 6, 2, 1, 2, + 2, 1, 6 +}; + +static const short _indic_syllable_machine_index_offsets[] = { + 0, 2, 19, 26, 31, 35, 37, 42, + 46, 48, 53, 57, 59, 64, 68, 70, + 76, 78, 80, 86, 88, 90, 96, 98, + 100, 106, 108, 110, 122, 134, 146, 158, + 170, 182, 194, 206, 218, 230, 232, 249, + 256, 261, 265, 267, 272, 276, 278, 283, + 287, 289, 294, 298, 300, 306, 308, 310, + 316, 318, 320, 326, 328, 330, 336, 338, + 340, 352, 364, 376, 388, 400, 412, 424, + 436, 448, 460, 462, 479, 486, 491, 495, + 497, 502, 506, 508, 513, 517, 519, 524, + 528, 530, 536, 538, 540, 546, 548, 550, + 556, 558, 560, 566, 568, 570, 582, 594, + 606, 618, 630, 642, 654, 666, 678, 680, + 697, 704, 709, 713, 715, 720, 724, 726, + 731, 735, 737, 742, 746, 748, 754, 756, + 758, 764, 766, 768, 774, 776, 778, 784, + 786, 788, 800, 812, 824, 836, 848, 860, + 872, 884, 896, 908, 920, 925, 937, 949, + 954, 958, 963, 967, 969, 974, 978, 980, + 985, 989, 991, 993, 1010, 1017, 1023, 1025, + 1027, 1033, 1035, 1037, 1043, 1045, 1047, 1053, + 1055, 1057, 1059, 1078, 1094, 1110, 1125, 1142, + 1158, 1174, 1189, 1206, 1222, 1238, 1253, 1270, + 1286, 1302, 1317, 1334, 1350, 1366, 1381, 1392, + 1403, 1410, 1413, 1415, 1418, 1421, 1423, 1430, + 1442, 1451, 1458, 1467, 1479, 1492, 1505, 1517, + 1528, 1541, 1553, 1564, 1577, 1589, 1600, 1613, + 1625, 1636, 1649, 1666, 1678, 1694, 1710, 1727, + 1744, 1761, 1778, 1795, 1811, 1827, 1844, 1861, + 1878, 1895, 1912, 1928, 1944, 1961, 1978, 1995, + 2012, 2029, 2045, 2061, 2078, 2095, 2112, 2129, + 2146, 2162, 2178, 2194, 2210, 2225, 2242, 2258, + 2274, 2289, 2306, 2322, 2338, 2353, 2370, 2386, + 2402, 2417, 2434, 2450, 2466, 2481, 2492, 2503, + 2510, 2513, 2515, 2518, 2521, 2523, 2530, 2542, + 2551, 2558, 2567, 2579, 2592, 2605, 2617, 2628, + 2641, 2653, 2664, 2677, 2689, 2700, 2713, 2725, + 2736, 2749, 2766, 2778, 2794, 2810, 2827, 2844, + 2861, 2878, 2895, 2911, 2927, 2944, 2961, 2978, + 2995, 3012, 3028, 3044, 3061, 3078, 3095, 3112, + 3129, 3145, 3161, 3178, 3195, 3212, 3229, 3241, + 3258, 3274, 3290, 3305, 3322, 3338, 3354, 3369, + 3386, 3402, 3418, 3433, 3450, 3466, 3482, 3497, + 3514, 3530, 3546, 3561, 3572, 3583, 3590, 3593, + 3595, 3598, 3601, 3603, 3610, 3622, 3631, 3638, + 3647, 3659, 3672, 3685, 3697, 3708, 3721, 3733, + 3744, 3757, 3769, 3780, 3793, 3805, 3816, 3829, + 3846, 3858, 3874, 3890, 3907, 3924, 3941, 3958, + 3975, 3991, 4007, 4024, 4041, 4058, 4075, 4092, + 4108, 4124, 4141, 4158, 4175, 4192, 4209, 4225, + 4241, 4258, 4275, 4292, 4309, 4326, 4338, 4354, + 4366, 4382, 4398, 4413, 4430, 4446, 4462, 4477, + 4494, 4510, 4526, 4541, 4558, 4574, 4590, 4605, + 4622, 4638, 4654, 4669, 4680, 4691, 4698, 4701, + 4703, 4706, 4709, 4711, 4718, 4730, 4739, 4746, + 4755, 4767, 4780, 4793, 4805, 4816, 4829, 4841, + 4852, 4865, 4877, 4888, 4901, 4913, 4924, 4937, + 4954, 4966, 4982, 4998, 5015, 5032, 5049, 5066, + 5083, 5099, 5115, 5132, 5149, 5166, 5183, 5200, + 5216, 5232, 5249, 5266, 5283, 5300, 5317, 5333, + 5349, 5366, 5383, 5400, 5417, 5434, 5450, 5468, + 5484, 5502, 5514, 5521, 5524, 5526, 5529, 5532, + 5534, 5541, 5558, 5574, 5590, 5605, 5621, 5637, + 5654, 5667, 5679, 5690, 5703, 5715, 5726, 5739, + 5751, 5762, 5775, 5787, 5798, 5810, 5819, 5826, + 5835, 5847, 5864, 5873, 5880, 5887, 5890, 5892, + 5895, 5898, 5900 +}; + +static const short _indic_syllable_machine_indicies[] = { + 1, 0, 2, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 0, 1, 0, 0, 0, 0, + 4, 0, 5, 5, 6, 1, 0, 7, + 7, 6, 0, 6, 0, 8, 8, 9, + 1, 0, 10, 10, 9, 0, 9, 0, + 11, 11, 12, 1, 0, 13, 13, 12, + 0, 12, 0, 14, 14, 15, 1, 0, + 16, 16, 15, 0, 15, 0, 17, 0, + 0, 0, 1, 0, 18, 0, 19, 0, + 20, 14, 14, 15, 1, 0, 21, 0, + 22, 0, 23, 11, 11, 12, 1, 0, + 24, 0, 25, 0, 26, 8, 8, 9, + 1, 0, 27, 0, 28, 0, 29, 5, + 5, 6, 1, 0, 0, 0, 0, 0, + 29, 0, 29, 5, 5, 6, 1, 0, + 0, 0, 0, 30, 29, 0, 31, 5, + 5, 6, 1, 0, 0, 0, 0, 0, + 31, 0, 31, 5, 5, 6, 1, 0, + 0, 0, 0, 32, 31, 0, 33, 5, + 5, 6, 1, 0, 0, 0, 0, 0, + 33, 0, 33, 5, 5, 6, 1, 0, + 0, 0, 0, 34, 33, 0, 35, 5, + 5, 6, 1, 0, 0, 0, 0, 0, + 35, 0, 35, 5, 5, 6, 1, 0, + 0, 0, 0, 36, 35, 0, 37, 5, + 5, 6, 1, 0, 0, 0, 0, 0, + 37, 0, 37, 5, 5, 6, 1, 0, + 0, 0, 0, 38, 37, 0, 40, 39, + 41, 42, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 41, + 39, 40, 39, 39, 39, 39, 43, 39, + 44, 44, 45, 40, 39, 46, 46, 45, + 39, 45, 39, 47, 47, 48, 40, 39, + 49, 49, 48, 39, 48, 39, 50, 50, + 51, 40, 39, 52, 52, 51, 39, 51, + 39, 53, 53, 54, 40, 39, 55, 55, + 54, 39, 54, 39, 56, 39, 39, 39, + 40, 39, 57, 39, 58, 39, 59, 53, + 53, 54, 40, 39, 60, 39, 61, 39, + 62, 50, 50, 51, 40, 39, 63, 39, + 64, 39, 65, 47, 47, 48, 40, 39, + 66, 39, 67, 39, 68, 44, 44, 45, + 40, 39, 39, 39, 39, 39, 68, 39, + 68, 44, 44, 45, 40, 39, 39, 39, + 39, 69, 68, 39, 70, 44, 44, 45, + 40, 39, 39, 39, 39, 39, 70, 39, + 70, 44, 44, 45, 40, 39, 39, 39, + 39, 71, 70, 39, 72, 44, 44, 45, + 40, 39, 39, 39, 39, 39, 72, 39, + 72, 44, 44, 45, 40, 39, 39, 39, + 39, 73, 72, 39, 74, 44, 44, 45, + 40, 39, 39, 39, 39, 39, 74, 39, + 74, 44, 44, 45, 40, 39, 39, 39, + 39, 75, 74, 39, 76, 44, 44, 45, + 40, 39, 39, 39, 39, 39, 76, 39, + 76, 44, 44, 45, 40, 39, 39, 39, + 39, 77, 76, 39, 79, 78, 80, 81, + 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 80, 78, 79, + 78, 78, 78, 78, 82, 78, 83, 83, + 84, 79, 78, 86, 86, 84, 85, 84, + 85, 87, 87, 88, 79, 78, 89, 89, + 88, 78, 88, 78, 90, 90, 91, 79, + 78, 92, 92, 91, 78, 91, 78, 93, + 93, 94, 79, 78, 95, 95, 94, 78, + 94, 78, 96, 78, 78, 78, 79, 78, + 97, 78, 98, 78, 99, 93, 93, 94, + 79, 78, 100, 78, 101, 78, 102, 90, + 90, 91, 79, 78, 103, 78, 104, 78, + 105, 87, 87, 88, 79, 78, 106, 78, + 107, 78, 108, 83, 83, 84, 79, 78, + 78, 78, 78, 78, 108, 78, 108, 83, + 83, 84, 79, 78, 78, 78, 78, 109, + 108, 78, 110, 83, 83, 84, 79, 78, + 78, 78, 78, 78, 110, 78, 110, 83, + 83, 84, 79, 78, 78, 78, 78, 111, + 110, 78, 112, 83, 83, 84, 79, 78, + 78, 78, 78, 78, 112, 78, 112, 83, + 83, 84, 79, 78, 78, 78, 78, 113, + 112, 78, 114, 83, 83, 84, 79, 78, + 78, 78, 78, 78, 114, 78, 114, 83, + 83, 84, 79, 78, 78, 78, 78, 115, + 114, 78, 116, 83, 83, 84, 79, 78, + 78, 78, 78, 78, 116, 78, 118, 117, + 119, 120, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 119, + 117, 118, 117, 117, 117, 117, 121, 117, + 122, 122, 123, 118, 117, 124, 124, 123, + 117, 123, 117, 125, 125, 126, 118, 117, + 127, 127, 126, 117, 126, 117, 128, 128, + 129, 118, 117, 130, 130, 129, 117, 129, + 117, 131, 131, 132, 118, 117, 133, 133, + 132, 117, 132, 117, 134, 117, 117, 117, + 118, 117, 135, 117, 136, 117, 137, 131, + 131, 132, 118, 117, 138, 117, 139, 117, + 140, 128, 128, 129, 118, 117, 141, 117, + 142, 117, 143, 125, 125, 126, 118, 117, + 144, 117, 145, 117, 146, 122, 122, 123, + 118, 117, 117, 117, 117, 117, 146, 117, + 146, 122, 122, 123, 118, 117, 117, 117, + 117, 147, 146, 117, 148, 122, 122, 123, + 118, 117, 117, 117, 117, 117, 148, 117, + 148, 122, 122, 123, 118, 117, 117, 117, + 117, 149, 148, 117, 150, 122, 122, 123, + 118, 117, 117, 117, 117, 117, 150, 117, + 150, 122, 122, 123, 118, 117, 117, 117, + 117, 151, 150, 117, 152, 122, 122, 123, + 118, 117, 117, 117, 117, 117, 152, 117, + 152, 122, 122, 123, 118, 117, 117, 117, + 117, 153, 152, 117, 154, 122, 122, 123, + 118, 117, 117, 117, 117, 117, 154, 117, + 154, 122, 122, 123, 118, 117, 117, 117, + 117, 155, 154, 117, 116, 83, 83, 84, + 79, 78, 78, 78, 78, 156, 116, 78, + 86, 86, 84, 1, 0, 114, 83, 83, + 84, 157, 0, 0, 0, 0, 0, 114, + 0, 114, 83, 83, 84, 157, 0, 0, + 0, 0, 158, 114, 0, 159, 159, 160, + 1, 0, 7, 7, 160, 0, 161, 161, + 162, 157, 0, 163, 163, 162, 0, 162, + 0, 164, 164, 165, 157, 0, 166, 166, + 165, 0, 165, 0, 167, 167, 168, 157, + 0, 169, 169, 168, 0, 168, 0, 157, + 0, 170, 171, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 170, 0, 157, 0, 0, 0, 0, 172, + 0, 173, 0, 0, 0, 157, 0, 174, + 0, 175, 0, 176, 167, 167, 168, 157, + 0, 177, 0, 178, 0, 179, 164, 164, + 165, 157, 0, 180, 0, 181, 0, 182, + 161, 161, 162, 157, 0, 183, 0, 184, + 0, 186, 185, 188, 189, 190, 191, 192, + 193, 84, 79, 194, 195, 196, 196, 156, + 197, 198, 199, 200, 201, 187, 203, 204, + 205, 206, 6, 1, 207, 208, 202, 202, + 38, 209, 202, 202, 210, 202, 211, 204, + 212, 212, 6, 1, 207, 208, 202, 202, + 202, 209, 202, 202, 210, 202, 204, 212, + 212, 6, 1, 207, 208, 202, 202, 202, + 209, 202, 202, 210, 202, 213, 202, 202, + 202, 19, 214, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 213, 202, 216, 217, + 218, 219, 6, 1, 207, 208, 202, 202, + 36, 220, 202, 202, 210, 202, 221, 217, + 222, 222, 6, 1, 207, 208, 202, 202, + 202, 220, 202, 202, 210, 202, 217, 222, + 222, 6, 1, 207, 208, 202, 202, 202, + 220, 202, 202, 210, 202, 223, 202, 202, + 202, 19, 224, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 223, 202, 225, 226, + 227, 228, 6, 1, 207, 208, 202, 202, + 34, 229, 202, 202, 210, 202, 230, 226, + 231, 231, 6, 1, 207, 208, 202, 202, + 202, 229, 202, 202, 210, 202, 226, 231, + 231, 6, 1, 207, 208, 202, 202, 202, + 229, 202, 202, 210, 202, 232, 202, 202, + 202, 19, 233, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 232, 202, 234, 235, + 236, 237, 6, 1, 207, 208, 202, 202, + 32, 238, 202, 202, 210, 202, 239, 235, + 240, 240, 6, 1, 207, 208, 202, 202, + 202, 238, 202, 202, 210, 202, 235, 240, + 240, 6, 1, 207, 208, 202, 202, 202, + 238, 202, 202, 210, 202, 241, 202, 202, + 202, 19, 242, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 241, 202, 243, 244, + 245, 246, 6, 1, 207, 208, 202, 202, + 30, 247, 202, 202, 210, 202, 248, 244, + 249, 249, 6, 1, 207, 208, 202, 202, + 202, 247, 202, 202, 210, 202, 244, 249, + 249, 6, 1, 207, 208, 202, 202, 202, + 247, 202, 202, 210, 202, 19, 250, 202, + 1, 207, 208, 202, 202, 202, 215, 202, + 251, 251, 202, 1, 207, 208, 202, 202, + 202, 215, 202, 252, 202, 202, 253, 207, + 208, 202, 207, 208, 202, 254, 202, 207, + 255, 202, 207, 256, 202, 207, 202, 252, + 202, 202, 202, 207, 208, 202, 257, 202, + 258, 259, 202, 1, 207, 208, 202, 202, + 4, 202, 3, 202, 251, 251, 202, 1, + 207, 208, 202, 251, 251, 202, 1, 207, + 208, 202, 257, 202, 251, 251, 202, 1, + 207, 208, 202, 257, 202, 258, 251, 202, + 1, 207, 208, 202, 202, 4, 202, 19, + 202, 260, 260, 6, 1, 207, 208, 202, + 202, 202, 215, 202, 261, 28, 262, 263, + 9, 1, 207, 208, 202, 202, 202, 215, + 202, 28, 262, 263, 9, 1, 207, 208, + 202, 202, 202, 215, 202, 262, 262, 9, + 1, 207, 208, 202, 202, 202, 215, 202, + 264, 25, 265, 266, 12, 1, 207, 208, + 202, 202, 202, 215, 202, 25, 265, 266, + 12, 1, 207, 208, 202, 202, 202, 215, + 202, 265, 265, 12, 1, 207, 208, 202, + 202, 202, 215, 202, 267, 22, 268, 269, + 15, 1, 207, 208, 202, 202, 202, 215, + 202, 22, 268, 269, 15, 1, 207, 208, + 202, 202, 202, 215, 202, 268, 268, 15, + 1, 207, 208, 202, 202, 202, 215, 202, + 270, 19, 251, 271, 202, 1, 207, 208, + 202, 202, 202, 215, 202, 19, 251, 271, + 202, 1, 207, 208, 202, 202, 202, 215, + 202, 251, 272, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 19, 202, 251, 251, + 202, 1, 207, 208, 202, 202, 202, 215, + 202, 2, 3, 202, 202, 19, 250, 202, + 1, 207, 208, 202, 202, 202, 215, 202, + 2, 202, 244, 249, 249, 6, 1, 207, + 208, 202, 202, 202, 247, 202, 243, 244, + 249, 249, 6, 1, 207, 208, 202, 202, + 202, 247, 202, 202, 210, 202, 243, 244, + 245, 249, 6, 1, 207, 208, 202, 202, + 30, 247, 202, 202, 210, 202, 241, 202, + 273, 202, 260, 260, 6, 1, 207, 208, + 202, 202, 202, 215, 202, 241, 202, 241, + 202, 202, 202, 251, 251, 202, 1, 207, + 208, 202, 202, 202, 215, 202, 241, 202, + 241, 202, 202, 202, 251, 274, 202, 1, + 207, 208, 202, 202, 202, 215, 202, 241, + 202, 241, 202, 273, 202, 251, 251, 202, + 1, 207, 208, 202, 202, 202, 215, 202, + 241, 202, 241, 3, 202, 202, 19, 242, + 202, 1, 207, 208, 202, 202, 202, 215, + 202, 241, 202, 234, 235, 240, 240, 6, + 1, 207, 208, 202, 202, 202, 238, 202, + 202, 210, 202, 234, 235, 236, 240, 6, + 1, 207, 208, 202, 202, 32, 238, 202, + 202, 210, 202, 232, 202, 275, 202, 260, + 260, 6, 1, 207, 208, 202, 202, 202, + 215, 202, 232, 202, 232, 202, 202, 202, + 251, 251, 202, 1, 207, 208, 202, 202, + 202, 215, 202, 232, 202, 232, 202, 202, + 202, 251, 276, 202, 1, 207, 208, 202, + 202, 202, 215, 202, 232, 202, 232, 202, + 275, 202, 251, 251, 202, 1, 207, 208, + 202, 202, 202, 215, 202, 232, 202, 232, + 3, 202, 202, 19, 233, 202, 1, 207, + 208, 202, 202, 202, 215, 202, 232, 202, + 225, 226, 231, 231, 6, 1, 207, 208, + 202, 202, 202, 229, 202, 202, 210, 202, + 225, 226, 227, 231, 6, 1, 207, 208, + 202, 202, 34, 229, 202, 202, 210, 202, + 223, 202, 277, 202, 260, 260, 6, 1, + 207, 208, 202, 202, 202, 215, 202, 223, + 202, 223, 202, 202, 202, 251, 251, 202, + 1, 207, 208, 202, 202, 202, 215, 202, + 223, 202, 223, 202, 202, 202, 251, 278, + 202, 1, 207, 208, 202, 202, 202, 215, + 202, 223, 202, 223, 202, 277, 202, 251, + 251, 202, 1, 207, 208, 202, 202, 202, + 215, 202, 223, 202, 223, 3, 202, 202, + 19, 224, 202, 1, 207, 208, 202, 202, + 202, 215, 202, 223, 202, 216, 217, 222, + 222, 6, 1, 207, 208, 202, 202, 202, + 220, 202, 202, 210, 202, 216, 217, 218, + 222, 6, 1, 207, 208, 202, 202, 36, + 220, 202, 202, 210, 202, 213, 202, 279, + 202, 260, 260, 6, 1, 207, 208, 202, + 202, 202, 215, 202, 213, 202, 213, 202, + 202, 202, 251, 251, 202, 1, 207, 208, + 202, 202, 202, 215, 202, 213, 202, 213, + 202, 202, 202, 251, 280, 202, 1, 207, + 208, 202, 202, 202, 215, 202, 213, 202, + 213, 202, 279, 202, 251, 251, 202, 1, + 207, 208, 202, 202, 202, 215, 202, 213, + 202, 213, 3, 202, 202, 19, 214, 202, + 1, 207, 208, 202, 202, 202, 215, 202, + 213, 202, 203, 204, 212, 212, 6, 1, + 207, 208, 202, 202, 202, 209, 202, 202, + 210, 202, 203, 204, 205, 212, 6, 1, + 207, 208, 202, 202, 38, 209, 202, 202, + 210, 202, 282, 283, 284, 285, 45, 40, + 286, 287, 281, 281, 77, 288, 281, 281, + 289, 281, 290, 283, 291, 285, 45, 40, + 286, 287, 281, 281, 281, 288, 281, 281, + 289, 281, 283, 291, 285, 45, 40, 286, + 287, 281, 281, 281, 288, 281, 281, 289, + 281, 292, 281, 281, 281, 58, 293, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 292, 281, 295, 296, 297, 298, 45, 40, + 286, 287, 281, 281, 75, 299, 281, 281, + 289, 281, 300, 296, 301, 301, 45, 40, + 286, 287, 281, 281, 281, 299, 281, 281, + 289, 281, 296, 301, 301, 45, 40, 286, + 287, 281, 281, 281, 299, 281, 281, 289, + 281, 302, 281, 281, 281, 58, 303, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 302, 281, 304, 305, 306, 307, 45, 40, + 286, 287, 281, 281, 73, 308, 281, 281, + 289, 281, 309, 305, 310, 310, 45, 40, + 286, 287, 281, 281, 281, 308, 281, 281, + 289, 281, 305, 310, 310, 45, 40, 286, + 287, 281, 281, 281, 308, 281, 281, 289, + 281, 311, 281, 281, 281, 58, 312, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 311, 281, 313, 314, 315, 316, 45, 40, + 286, 287, 281, 281, 71, 317, 281, 281, + 289, 281, 318, 314, 319, 319, 45, 40, + 286, 287, 281, 281, 281, 317, 281, 281, + 289, 281, 314, 319, 319, 45, 40, 286, + 287, 281, 281, 281, 317, 281, 281, 289, + 281, 320, 281, 281, 281, 58, 321, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 320, 281, 322, 323, 324, 325, 45, 40, + 286, 287, 281, 281, 69, 326, 281, 281, + 289, 281, 327, 323, 328, 328, 45, 40, + 286, 287, 281, 281, 281, 326, 281, 281, + 289, 281, 323, 328, 328, 45, 40, 286, + 287, 281, 281, 281, 326, 281, 281, 289, + 281, 58, 329, 281, 40, 286, 287, 281, + 281, 281, 294, 281, 330, 330, 281, 40, + 286, 287, 281, 281, 281, 294, 281, 331, + 281, 281, 332, 286, 287, 281, 286, 287, + 281, 333, 281, 286, 334, 281, 286, 335, + 281, 286, 281, 331, 281, 281, 281, 286, + 287, 281, 336, 281, 337, 338, 281, 40, + 286, 287, 281, 281, 43, 281, 42, 281, + 330, 330, 281, 40, 286, 287, 281, 330, + 330, 281, 40, 286, 287, 281, 336, 281, + 330, 330, 281, 40, 286, 287, 281, 336, + 281, 337, 330, 281, 40, 286, 287, 281, + 281, 43, 281, 58, 281, 339, 339, 45, + 40, 286, 287, 281, 281, 281, 294, 281, + 340, 67, 341, 342, 48, 40, 286, 287, + 281, 281, 281, 294, 281, 67, 341, 342, + 48, 40, 286, 287, 281, 281, 281, 294, + 281, 341, 341, 48, 40, 286, 287, 281, + 281, 281, 294, 281, 343, 64, 344, 345, + 51, 40, 286, 287, 281, 281, 281, 294, + 281, 64, 344, 345, 51, 40, 286, 287, + 281, 281, 281, 294, 281, 344, 344, 51, + 40, 286, 287, 281, 281, 281, 294, 281, + 346, 61, 347, 348, 54, 40, 286, 287, + 281, 281, 281, 294, 281, 61, 347, 348, + 54, 40, 286, 287, 281, 281, 281, 294, + 281, 347, 347, 54, 40, 286, 287, 281, + 281, 281, 294, 281, 349, 58, 330, 350, + 281, 40, 286, 287, 281, 281, 281, 294, + 281, 58, 330, 350, 281, 40, 286, 287, + 281, 281, 281, 294, 281, 330, 351, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 58, 281, 330, 330, 281, 40, 286, 287, + 281, 281, 281, 294, 281, 41, 42, 281, + 281, 58, 329, 281, 40, 286, 287, 281, + 281, 281, 294, 281, 41, 281, 323, 328, + 328, 45, 40, 286, 287, 281, 281, 281, + 326, 281, 322, 323, 328, 328, 45, 40, + 286, 287, 281, 281, 281, 326, 281, 281, + 289, 281, 322, 323, 324, 328, 45, 40, + 286, 287, 281, 281, 69, 326, 281, 281, + 289, 281, 320, 281, 352, 281, 339, 339, + 45, 40, 286, 287, 281, 281, 281, 294, + 281, 320, 281, 320, 281, 281, 281, 330, + 330, 281, 40, 286, 287, 281, 281, 281, + 294, 281, 320, 281, 320, 281, 281, 281, + 330, 353, 281, 40, 286, 287, 281, 281, + 281, 294, 281, 320, 281, 320, 281, 352, + 281, 330, 330, 281, 40, 286, 287, 281, + 281, 281, 294, 281, 320, 281, 320, 42, + 281, 281, 58, 321, 281, 40, 286, 287, + 281, 281, 281, 294, 281, 320, 281, 313, + 314, 319, 319, 45, 40, 286, 287, 281, + 281, 281, 317, 281, 281, 289, 281, 313, + 314, 315, 319, 45, 40, 286, 287, 281, + 281, 71, 317, 281, 281, 289, 281, 311, + 281, 354, 281, 339, 339, 45, 40, 286, + 287, 281, 281, 281, 294, 281, 311, 281, + 311, 281, 281, 281, 330, 330, 281, 40, + 286, 287, 281, 281, 281, 294, 281, 311, + 281, 311, 281, 281, 281, 330, 355, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 311, 281, 311, 281, 354, 281, 330, 330, + 281, 40, 286, 287, 281, 281, 281, 294, + 281, 311, 281, 311, 42, 281, 281, 58, + 312, 281, 40, 286, 287, 281, 281, 281, + 294, 281, 311, 281, 304, 305, 310, 310, + 45, 40, 286, 287, 281, 281, 281, 308, + 281, 281, 289, 281, 304, 305, 306, 310, + 45, 40, 286, 287, 281, 281, 73, 308, + 281, 281, 289, 281, 302, 281, 356, 281, + 339, 339, 45, 40, 286, 287, 281, 281, + 281, 294, 281, 302, 281, 302, 281, 281, + 281, 330, 330, 281, 40, 286, 287, 281, + 281, 281, 294, 281, 302, 281, 302, 281, + 281, 281, 330, 357, 281, 40, 286, 287, + 281, 281, 281, 294, 281, 302, 281, 302, + 281, 356, 281, 330, 330, 281, 40, 286, + 287, 281, 281, 281, 294, 281, 302, 281, + 302, 42, 281, 281, 58, 303, 281, 40, + 286, 287, 281, 281, 281, 294, 281, 302, + 281, 295, 296, 301, 301, 45, 40, 286, + 287, 281, 281, 281, 299, 281, 281, 289, + 281, 295, 296, 297, 301, 45, 40, 286, + 287, 281, 281, 75, 299, 281, 281, 289, + 281, 292, 281, 358, 281, 339, 339, 45, + 40, 286, 287, 281, 281, 281, 294, 281, + 292, 281, 292, 281, 281, 281, 330, 330, + 281, 40, 286, 287, 281, 281, 281, 294, + 281, 292, 281, 292, 281, 281, 281, 330, + 359, 281, 40, 286, 287, 281, 281, 281, + 294, 281, 292, 281, 292, 281, 358, 281, + 330, 330, 281, 40, 286, 287, 281, 281, + 281, 294, 281, 292, 281, 76, 44, 44, + 45, 40, 281, 281, 281, 281, 281, 76, + 281, 292, 42, 281, 281, 58, 293, 281, + 40, 286, 287, 281, 281, 281, 294, 281, + 292, 281, 282, 283, 291, 285, 45, 40, + 286, 287, 281, 281, 281, 288, 281, 281, + 289, 281, 361, 191, 362, 362, 84, 79, + 194, 195, 360, 360, 360, 197, 360, 360, + 200, 360, 191, 362, 362, 84, 79, 194, + 195, 360, 360, 360, 197, 360, 360, 200, + 360, 363, 360, 360, 360, 98, 364, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 363, 360, 366, 367, 368, 369, 84, 79, + 194, 195, 360, 360, 115, 370, 360, 360, + 200, 360, 371, 367, 372, 372, 84, 79, + 194, 195, 360, 360, 360, 370, 360, 360, + 200, 360, 367, 372, 372, 84, 79, 194, + 195, 360, 360, 360, 370, 360, 360, 200, + 360, 373, 360, 360, 360, 98, 374, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 373, 360, 375, 376, 377, 378, 84, 79, + 194, 195, 360, 360, 113, 379, 360, 360, + 200, 360, 380, 376, 381, 381, 84, 79, + 194, 195, 360, 360, 360, 379, 360, 360, + 200, 360, 376, 381, 381, 84, 79, 194, + 195, 360, 360, 360, 379, 360, 360, 200, + 360, 382, 360, 360, 360, 98, 383, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 382, 360, 384, 385, 386, 387, 84, 79, + 194, 195, 360, 360, 111, 388, 360, 360, + 200, 360, 389, 385, 390, 390, 84, 79, + 194, 195, 360, 360, 360, 388, 360, 360, + 200, 360, 385, 390, 390, 84, 79, 194, + 195, 360, 360, 360, 388, 360, 360, 200, + 360, 391, 360, 360, 360, 98, 392, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 391, 360, 393, 394, 395, 396, 84, 79, + 194, 195, 360, 360, 109, 397, 360, 360, + 200, 360, 398, 394, 399, 399, 84, 79, + 194, 195, 360, 360, 360, 397, 360, 360, + 200, 360, 394, 399, 399, 84, 79, 194, + 195, 360, 360, 360, 397, 360, 360, 200, + 360, 98, 400, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 401, 401, 360, 79, + 194, 195, 360, 360, 360, 365, 360, 402, + 360, 360, 403, 194, 195, 360, 194, 195, + 360, 404, 360, 194, 405, 360, 194, 406, + 360, 194, 360, 402, 360, 360, 360, 194, + 195, 360, 407, 360, 408, 409, 360, 79, + 194, 195, 360, 360, 82, 360, 81, 360, + 401, 401, 360, 79, 194, 195, 360, 401, + 401, 360, 79, 194, 195, 360, 407, 360, + 401, 401, 360, 79, 194, 195, 360, 407, + 360, 408, 401, 360, 79, 194, 195, 360, + 360, 82, 360, 98, 360, 410, 410, 84, + 79, 194, 195, 360, 360, 360, 365, 360, + 411, 107, 412, 413, 88, 79, 194, 195, + 360, 360, 360, 365, 360, 107, 412, 413, + 88, 79, 194, 195, 360, 360, 360, 365, + 360, 412, 412, 88, 79, 194, 195, 360, + 360, 360, 365, 360, 414, 104, 415, 416, + 91, 79, 194, 195, 360, 360, 360, 365, + 360, 104, 415, 416, 91, 79, 194, 195, + 360, 360, 360, 365, 360, 415, 415, 91, + 79, 194, 195, 360, 360, 360, 365, 360, + 417, 101, 418, 419, 94, 79, 194, 195, + 360, 360, 360, 365, 360, 101, 418, 419, + 94, 79, 194, 195, 360, 360, 360, 365, + 360, 418, 418, 94, 79, 194, 195, 360, + 360, 360, 365, 360, 420, 98, 401, 421, + 360, 79, 194, 195, 360, 360, 360, 365, + 360, 98, 401, 421, 360, 79, 194, 195, + 360, 360, 360, 365, 360, 401, 422, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 98, 360, 401, 401, 360, 79, 194, 195, + 360, 360, 360, 365, 360, 80, 81, 360, + 360, 98, 400, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 80, 360, 394, 399, + 399, 84, 79, 194, 195, 360, 360, 360, + 397, 360, 393, 394, 399, 399, 84, 79, + 194, 195, 360, 360, 360, 397, 360, 360, + 200, 360, 393, 394, 395, 399, 84, 79, + 194, 195, 360, 360, 109, 397, 360, 360, + 200, 360, 391, 360, 423, 360, 410, 410, + 84, 79, 194, 195, 360, 360, 360, 365, + 360, 391, 360, 391, 360, 360, 360, 401, + 401, 360, 79, 194, 195, 360, 360, 360, + 365, 360, 391, 360, 391, 360, 360, 360, + 401, 424, 360, 79, 194, 195, 360, 360, + 360, 365, 360, 391, 360, 391, 360, 423, + 360, 401, 401, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 391, 360, 391, 81, + 360, 360, 98, 392, 360, 79, 194, 195, + 360, 360, 360, 365, 360, 391, 360, 384, + 385, 390, 390, 84, 79, 194, 195, 360, + 360, 360, 388, 360, 360, 200, 360, 384, + 385, 386, 390, 84, 79, 194, 195, 360, + 360, 111, 388, 360, 360, 200, 360, 382, + 360, 425, 360, 410, 410, 84, 79, 194, + 195, 360, 360, 360, 365, 360, 382, 360, + 382, 360, 360, 360, 401, 401, 360, 79, + 194, 195, 360, 360, 360, 365, 360, 382, + 360, 382, 360, 360, 360, 401, 426, 360, + 79, 194, 195, 360, 360, 360, 365, 360, + 382, 360, 382, 360, 425, 360, 401, 401, + 360, 79, 194, 195, 360, 360, 360, 365, + 360, 382, 360, 382, 81, 360, 360, 98, + 383, 360, 79, 194, 195, 360, 360, 360, + 365, 360, 382, 360, 375, 376, 381, 381, + 84, 79, 194, 195, 360, 360, 360, 379, + 360, 360, 200, 360, 375, 376, 377, 381, + 84, 79, 194, 195, 360, 360, 113, 379, + 360, 360, 200, 360, 373, 360, 427, 360, + 410, 410, 84, 79, 194, 195, 360, 360, + 360, 365, 360, 373, 360, 373, 360, 360, + 360, 401, 401, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 373, 360, 373, 360, + 360, 360, 401, 428, 360, 79, 194, 195, + 360, 360, 360, 365, 360, 373, 360, 373, + 360, 427, 360, 401, 401, 360, 79, 194, + 195, 360, 360, 360, 365, 360, 373, 360, + 373, 81, 360, 360, 98, 374, 360, 79, + 194, 195, 360, 360, 360, 365, 360, 373, + 360, 366, 367, 372, 372, 84, 79, 194, + 195, 360, 360, 360, 370, 360, 360, 200, + 360, 366, 367, 368, 372, 84, 79, 194, + 195, 360, 360, 115, 370, 360, 360, 200, + 360, 363, 360, 429, 360, 410, 410, 84, + 79, 194, 195, 360, 360, 360, 365, 360, + 363, 360, 363, 360, 360, 360, 401, 401, + 360, 79, 194, 195, 360, 360, 360, 365, + 360, 363, 360, 363, 360, 360, 360, 401, + 430, 360, 79, 194, 195, 360, 360, 360, + 365, 360, 363, 360, 363, 360, 429, 360, + 401, 401, 360, 79, 194, 195, 360, 360, + 360, 365, 360, 363, 360, 363, 81, 360, + 360, 98, 364, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 363, 360, 116, 83, + 83, 84, 79, 431, 431, 431, 431, 156, + 116, 431, 190, 191, 362, 362, 84, 79, + 194, 195, 360, 360, 360, 197, 360, 360, + 200, 360, 116, 83, 83, 84, 79, 431, + 431, 431, 431, 431, 116, 431, 433, 434, + 435, 436, 123, 118, 437, 438, 432, 432, + 155, 439, 432, 432, 440, 432, 441, 434, + 436, 436, 123, 118, 437, 438, 432, 432, + 432, 439, 432, 432, 440, 432, 434, 436, + 436, 123, 118, 437, 438, 432, 432, 432, + 439, 432, 432, 440, 432, 442, 432, 432, + 432, 136, 443, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 442, 432, 445, 446, + 447, 448, 123, 118, 437, 438, 432, 432, + 153, 449, 432, 432, 440, 432, 450, 446, + 451, 451, 123, 118, 437, 438, 432, 432, + 432, 449, 432, 432, 440, 432, 446, 451, + 451, 123, 118, 437, 438, 432, 432, 432, + 449, 432, 432, 440, 432, 452, 432, 432, + 432, 136, 453, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 452, 432, 454, 455, + 456, 457, 123, 118, 437, 438, 432, 432, + 151, 458, 432, 432, 440, 432, 459, 455, + 460, 460, 123, 118, 437, 438, 432, 432, + 432, 458, 432, 432, 440, 432, 455, 460, + 460, 123, 118, 437, 438, 432, 432, 432, + 458, 432, 432, 440, 432, 461, 432, 432, + 432, 136, 462, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 461, 432, 463, 464, + 465, 466, 123, 118, 437, 438, 432, 432, + 149, 467, 432, 432, 440, 432, 468, 464, + 469, 469, 123, 118, 437, 438, 432, 432, + 432, 467, 432, 432, 440, 432, 464, 469, + 469, 123, 118, 437, 438, 432, 432, 432, + 467, 432, 432, 440, 432, 470, 432, 432, + 432, 136, 471, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 470, 432, 472, 473, + 474, 475, 123, 118, 437, 438, 432, 432, + 147, 476, 432, 432, 440, 432, 477, 473, + 478, 478, 123, 118, 437, 438, 432, 432, + 432, 476, 432, 432, 440, 432, 473, 478, + 478, 123, 118, 437, 438, 432, 432, 432, + 476, 432, 432, 440, 432, 136, 479, 432, + 118, 437, 438, 432, 432, 432, 444, 432, + 480, 480, 432, 118, 437, 438, 432, 432, + 432, 444, 432, 481, 432, 432, 482, 437, + 438, 432, 437, 438, 432, 483, 432, 437, + 484, 432, 437, 485, 432, 437, 432, 481, + 432, 432, 432, 437, 438, 432, 486, 432, + 487, 488, 432, 118, 437, 438, 432, 432, + 121, 432, 120, 432, 480, 480, 432, 118, + 437, 438, 432, 480, 480, 432, 118, 437, + 438, 432, 486, 432, 480, 480, 432, 118, + 437, 438, 432, 486, 432, 487, 480, 432, + 118, 437, 438, 432, 432, 121, 432, 136, + 432, 489, 489, 123, 118, 437, 438, 432, + 432, 432, 444, 432, 490, 145, 491, 492, + 126, 118, 437, 438, 432, 432, 432, 444, + 432, 145, 491, 492, 126, 118, 437, 438, + 432, 432, 432, 444, 432, 491, 491, 126, + 118, 437, 438, 432, 432, 432, 444, 432, + 493, 142, 494, 495, 129, 118, 437, 438, + 432, 432, 432, 444, 432, 142, 494, 495, + 129, 118, 437, 438, 432, 432, 432, 444, + 432, 494, 494, 129, 118, 437, 438, 432, + 432, 432, 444, 432, 496, 139, 497, 498, + 132, 118, 437, 438, 432, 432, 432, 444, + 432, 139, 497, 498, 132, 118, 437, 438, + 432, 432, 432, 444, 432, 497, 497, 132, + 118, 437, 438, 432, 432, 432, 444, 432, + 499, 136, 480, 500, 432, 118, 437, 438, + 432, 432, 432, 444, 432, 136, 480, 500, + 432, 118, 437, 438, 432, 432, 432, 444, + 432, 480, 501, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 136, 432, 480, 480, + 432, 118, 437, 438, 432, 432, 432, 444, + 432, 119, 120, 432, 432, 136, 479, 432, + 118, 437, 438, 432, 432, 432, 444, 432, + 119, 432, 473, 478, 478, 123, 118, 437, + 438, 432, 432, 432, 476, 432, 472, 473, + 478, 478, 123, 118, 437, 438, 432, 432, + 432, 476, 432, 432, 440, 432, 472, 473, + 474, 478, 123, 118, 437, 438, 432, 432, + 147, 476, 432, 432, 440, 432, 470, 432, + 502, 432, 489, 489, 123, 118, 437, 438, + 432, 432, 432, 444, 432, 470, 432, 470, + 432, 432, 432, 480, 480, 432, 118, 437, + 438, 432, 432, 432, 444, 432, 470, 432, + 470, 432, 432, 432, 480, 503, 432, 118, + 437, 438, 432, 432, 432, 444, 432, 470, + 432, 470, 432, 502, 432, 480, 480, 432, + 118, 437, 438, 432, 432, 432, 444, 432, + 470, 432, 470, 120, 432, 432, 136, 471, + 432, 118, 437, 438, 432, 432, 432, 444, + 432, 470, 432, 463, 464, 469, 469, 123, + 118, 437, 438, 432, 432, 432, 467, 432, + 432, 440, 432, 463, 464, 465, 469, 123, + 118, 437, 438, 432, 432, 149, 467, 432, + 432, 440, 432, 461, 432, 504, 432, 489, + 489, 123, 118, 437, 438, 432, 432, 432, + 444, 432, 461, 432, 461, 432, 432, 432, + 480, 480, 432, 118, 437, 438, 432, 432, + 432, 444, 432, 461, 432, 461, 432, 432, + 432, 480, 505, 432, 118, 437, 438, 432, + 432, 432, 444, 432, 461, 432, 461, 432, + 504, 432, 480, 480, 432, 118, 437, 438, + 432, 432, 432, 444, 432, 461, 432, 461, + 120, 432, 432, 136, 462, 432, 118, 437, + 438, 432, 432, 432, 444, 432, 461, 432, + 454, 455, 460, 460, 123, 118, 437, 438, + 432, 432, 432, 458, 432, 432, 440, 432, + 454, 455, 456, 460, 123, 118, 437, 438, + 432, 432, 151, 458, 432, 432, 440, 432, + 452, 432, 506, 432, 489, 489, 123, 118, + 437, 438, 432, 432, 432, 444, 432, 452, + 432, 452, 432, 432, 432, 480, 480, 432, + 118, 437, 438, 432, 432, 432, 444, 432, + 452, 432, 452, 432, 432, 432, 480, 507, + 432, 118, 437, 438, 432, 432, 432, 444, + 432, 452, 432, 452, 432, 506, 432, 480, + 480, 432, 118, 437, 438, 432, 432, 432, + 444, 432, 452, 432, 452, 120, 432, 432, + 136, 453, 432, 118, 437, 438, 432, 432, + 432, 444, 432, 452, 432, 445, 446, 451, + 451, 123, 118, 437, 438, 432, 432, 432, + 449, 432, 432, 440, 432, 445, 446, 447, + 451, 123, 118, 437, 438, 432, 432, 153, + 449, 432, 432, 440, 432, 442, 432, 508, + 432, 489, 489, 123, 118, 437, 438, 432, + 432, 432, 444, 432, 442, 432, 442, 432, + 432, 432, 480, 480, 432, 118, 437, 438, + 432, 432, 432, 444, 432, 442, 432, 442, + 432, 432, 432, 480, 509, 432, 118, 437, + 438, 432, 432, 432, 444, 432, 442, 432, + 442, 432, 508, 432, 480, 480, 432, 118, + 437, 438, 432, 432, 432, 444, 432, 442, + 432, 442, 120, 432, 432, 136, 443, 432, + 118, 437, 438, 432, 432, 432, 444, 432, + 442, 432, 433, 434, 436, 436, 123, 118, + 437, 438, 432, 432, 432, 439, 432, 432, + 440, 432, 188, 189, 190, 191, 510, 362, + 84, 79, 194, 195, 196, 196, 156, 197, + 360, 188, 200, 360, 203, 511, 205, 206, + 6, 1, 207, 208, 202, 202, 38, 209, + 202, 202, 210, 202, 213, 189, 190, 191, + 512, 513, 84, 157, 514, 515, 202, 196, + 156, 516, 202, 213, 200, 202, 116, 517, + 517, 84, 157, 207, 208, 202, 202, 156, + 518, 202, 519, 202, 202, 520, 514, 515, + 202, 514, 515, 202, 254, 202, 514, 521, + 202, 514, 522, 202, 514, 202, 519, 202, + 202, 202, 514, 515, 202, 523, 3, 360, + 360, 401, 430, 360, 79, 194, 195, 360, + 360, 360, 365, 360, 523, 360, 524, 367, + 525, 526, 84, 157, 514, 515, 202, 202, + 158, 370, 202, 202, 200, 202, 527, 367, + 528, 528, 84, 157, 514, 515, 202, 202, + 202, 370, 202, 202, 200, 202, 367, 528, + 528, 84, 157, 514, 515, 202, 202, 202, + 370, 202, 202, 200, 202, 524, 367, 528, + 528, 84, 157, 514, 515, 202, 202, 202, + 370, 202, 202, 200, 202, 524, 367, 525, + 528, 84, 157, 514, 515, 202, 202, 158, + 370, 202, 202, 200, 202, 213, 202, 279, + 116, 529, 529, 160, 157, 207, 208, 202, + 202, 202, 518, 202, 213, 202, 530, 184, + 531, 532, 162, 157, 514, 515, 202, 202, + 202, 533, 202, 184, 531, 532, 162, 157, + 514, 515, 202, 202, 202, 533, 202, 531, + 531, 162, 157, 514, 515, 202, 202, 202, + 533, 202, 534, 181, 535, 536, 165, 157, + 514, 515, 202, 202, 202, 533, 202, 181, + 535, 536, 165, 157, 514, 515, 202, 202, + 202, 533, 202, 535, 535, 165, 157, 514, + 515, 202, 202, 202, 533, 202, 537, 178, + 538, 539, 168, 157, 514, 515, 202, 202, + 202, 533, 202, 178, 538, 539, 168, 157, + 514, 515, 202, 202, 202, 533, 202, 538, + 538, 168, 157, 514, 515, 202, 202, 202, + 533, 202, 540, 175, 541, 542, 202, 157, + 514, 515, 202, 202, 202, 533, 202, 175, + 541, 542, 202, 157, 514, 515, 202, 202, + 202, 533, 202, 541, 541, 202, 157, 514, + 515, 202, 202, 202, 533, 202, 543, 202, + 544, 545, 202, 157, 514, 515, 202, 202, + 172, 202, 171, 202, 541, 541, 202, 157, + 514, 515, 202, 541, 541, 202, 157, 514, + 515, 202, 543, 202, 541, 541, 202, 157, + 514, 515, 202, 543, 202, 544, 541, 202, + 157, 514, 515, 202, 202, 172, 202, 523, + 171, 360, 360, 98, 364, 360, 79, 194, + 195, 360, 360, 360, 365, 360, 523, 360, + 547, 546, 548, 548, 546, 186, 549, 550, + 546, 548, 548, 546, 186, 549, 550, 546, + 551, 546, 546, 552, 549, 550, 546, 549, + 550, 546, 553, 546, 549, 554, 546, 549, + 555, 546, 549, 546, 551, 546, 546, 546, + 549, 550, 546, 0 +}; + +static const short _indic_syllable_machine_trans_targs[] = { + 178, 200, 207, 209, 210, 4, 213, 5, + 7, 216, 8, 10, 219, 11, 13, 222, + 14, 16, 17, 199, 19, 20, 221, 22, + 23, 218, 25, 26, 215, 224, 228, 232, + 235, 239, 242, 246, 249, 253, 256, 178, + 279, 286, 288, 289, 41, 292, 42, 44, + 295, 45, 47, 298, 48, 50, 301, 51, + 53, 54, 278, 56, 57, 300, 59, 60, + 297, 62, 63, 294, 303, 307, 311, 314, + 318, 321, 325, 328, 332, 336, 178, 357, + 364, 366, 367, 78, 370, 178, 79, 81, + 373, 82, 84, 376, 85, 87, 379, 88, + 90, 91, 356, 93, 94, 378, 96, 97, + 375, 99, 100, 372, 381, 385, 389, 392, + 396, 399, 403, 406, 410, 178, 437, 444, + 446, 447, 114, 450, 115, 117, 453, 118, + 120, 456, 121, 123, 459, 124, 126, 127, + 436, 129, 130, 458, 132, 133, 455, 135, + 136, 452, 461, 465, 469, 472, 476, 479, + 483, 486, 490, 493, 414, 498, 509, 152, + 512, 154, 515, 155, 157, 518, 158, 160, + 521, 161, 524, 526, 527, 166, 167, 523, + 169, 170, 520, 172, 173, 517, 175, 176, + 514, 178, 532, 178, 179, 258, 337, 339, + 413, 415, 359, 360, 416, 412, 494, 495, + 384, 530, 178, 180, 182, 36, 257, 202, + 203, 255, 227, 181, 35, 183, 251, 1, + 184, 186, 34, 250, 248, 185, 33, 187, + 244, 188, 190, 32, 243, 241, 189, 31, + 191, 237, 192, 194, 30, 236, 234, 193, + 29, 195, 230, 196, 198, 28, 229, 226, + 197, 27, 212, 0, 201, 206, 178, 204, + 205, 208, 2, 211, 3, 214, 6, 24, + 217, 9, 21, 220, 12, 18, 223, 15, + 225, 231, 233, 238, 240, 245, 247, 252, + 254, 178, 259, 261, 73, 334, 281, 282, + 335, 306, 260, 72, 262, 330, 38, 263, + 265, 71, 329, 327, 264, 70, 266, 323, + 267, 269, 69, 322, 320, 268, 68, 270, + 316, 271, 273, 67, 315, 313, 272, 66, + 274, 309, 275, 277, 65, 308, 305, 276, + 64, 291, 37, 280, 285, 178, 283, 284, + 287, 39, 290, 40, 293, 43, 61, 296, + 46, 58, 299, 49, 55, 302, 52, 304, + 310, 312, 317, 319, 324, 326, 331, 333, + 178, 338, 109, 340, 408, 75, 341, 343, + 108, 407, 405, 342, 107, 344, 401, 345, + 347, 106, 400, 398, 346, 105, 348, 394, + 349, 351, 104, 393, 391, 350, 103, 352, + 387, 353, 355, 102, 386, 383, 354, 101, + 369, 74, 358, 363, 178, 361, 362, 365, + 76, 368, 77, 371, 80, 98, 374, 83, + 95, 377, 86, 92, 380, 89, 382, 388, + 390, 395, 397, 402, 404, 409, 411, 178, + 178, 417, 419, 146, 145, 439, 440, 492, + 464, 418, 420, 488, 111, 421, 423, 144, + 487, 485, 422, 143, 424, 481, 425, 427, + 142, 480, 478, 426, 141, 428, 474, 429, + 431, 140, 473, 471, 430, 139, 432, 467, + 433, 435, 138, 466, 463, 434, 137, 449, + 110, 438, 443, 178, 441, 442, 445, 112, + 448, 113, 451, 116, 134, 454, 119, 131, + 457, 122, 128, 460, 125, 462, 468, 470, + 475, 477, 482, 484, 489, 491, 147, 496, + 497, 511, 500, 501, 529, 148, 505, 499, + 504, 502, 503, 506, 507, 150, 510, 508, + 149, 151, 513, 153, 174, 163, 516, 156, + 171, 519, 159, 168, 522, 162, 165, 525, + 164, 528, 178, 531, 177, 534, 535, 533, + 538, 178, 536, 537 +}; + +static const char _indic_syllable_machine_trans_actions[] = { + 1, 0, 2, 2, 2, 0, 2, 0, + 0, 2, 0, 0, 2, 0, 0, 2, + 0, 0, 0, 2, 0, 0, 2, 0, + 0, 2, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 0, 2, 2, 2, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 4, 0, + 2, 2, 2, 0, 2, 5, 0, 0, + 2, 0, 0, 2, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 2, 6, 2, 6, + 2, 6, 2, 6, 2, 7, 0, 2, + 2, 2, 0, 2, 0, 0, 2, 0, + 0, 2, 0, 0, 2, 0, 0, 0, + 2, 0, 0, 2, 0, 0, 2, 0, + 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 6, 0, 8, 0, + 2, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 2, 2, 2, 0, 0, 2, + 0, 0, 2, 0, 0, 2, 0, 0, + 2, 9, 0, 12, 2, 2, 6, 2, + 13, 13, 0, 0, 2, 2, 6, 2, + 6, 2, 14, 2, 2, 0, 2, 0, + 0, 2, 2, 2, 0, 2, 2, 0, + 2, 2, 0, 2, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 0, + 2, 2, 2, 2, 0, 2, 2, 2, + 0, 2, 2, 2, 2, 0, 2, 2, + 2, 0, 2, 0, 0, 0, 15, 0, + 0, 2, 0, 2, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 0, 2, 0, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 16, 2, 2, 0, 2, 0, 0, + 2, 2, 2, 0, 2, 2, 0, 2, + 2, 0, 2, 2, 2, 0, 2, 2, + 2, 2, 0, 2, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 0, + 2, 2, 2, 2, 0, 2, 2, 2, + 0, 2, 0, 0, 0, 17, 0, 0, + 2, 0, 2, 0, 2, 0, 0, 2, + 0, 0, 2, 0, 0, 2, 0, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 18, 6, 0, 6, 6, 0, 6, 2, + 0, 6, 2, 6, 0, 6, 6, 6, + 2, 0, 6, 2, 6, 0, 6, 6, + 6, 2, 0, 6, 2, 6, 0, 6, + 6, 6, 2, 0, 6, 2, 6, 0, + 6, 0, 0, 0, 19, 0, 0, 2, + 0, 2, 0, 2, 0, 0, 2, 0, + 0, 2, 0, 0, 2, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 20, + 21, 2, 2, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 0, 2, 2, 0, + 2, 2, 2, 0, 2, 2, 2, 2, + 0, 2, 2, 2, 0, 2, 2, 2, + 2, 0, 2, 2, 2, 0, 2, 2, + 2, 2, 0, 2, 2, 2, 0, 2, + 0, 0, 0, 22, 0, 0, 2, 0, + 2, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, + 8, 2, 0, 0, 2, 0, 2, 0, + 0, 0, 0, 8, 8, 0, 8, 8, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 2, 0, 0, 2, 0, 0, 2, + 0, 2, 23, 2, 0, 0, 0, 0, + 0, 24, 0, 0 +}; + +static const char _indic_syllable_machine_to_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 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, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 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 short _indic_syllable_machine_eof_trans[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 79, 79, 79, 79, 86, 86, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 79, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 186, 0, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 361, 361, 361, 361, 361, 432, 361, 432, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 361, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 361, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, + 203, 361, 547, 547, 547, 547, 547, 547, + 547, 547, 547 +}; + +static const int indic_syllable_machine_start = 178; +static const int indic_syllable_machine_first_final = 178; +static const int indic_syllable_machine_error = -1; + +static const int indic_syllable_machine_en_main = 178; + + +#line 36 "hb-ot-shape-complex-indic-machine.rl" + + + +#line 96 "hb-ot-shape-complex-indic-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 1382 "hb-ot-shape-complex-indic-machine.hh" + { + cs = indic_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 117 "hb-ot-shape-complex-indic-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + +#line 1399 "hb-ot-shape-complex-indic-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const short *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _indic_syllable_machine_from_state_actions[cs] ) { + case 11: +#line 1 "NONE" + {ts = p;} + break; +#line 1413 "hb-ot-shape-complex-indic-machine.hh" + } + + _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 "NONE" + {te = p+1;} + break; + case 15: +#line 87 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (consonant_syllable); }} + break; + case 17: +#line 88 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (vowel_syllable); }} + break; + case 22: +#line 89 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (standalone_cluster); }} + break; + case 24: +#line 90 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (symbol_cluster); }} + break; + case 19: +#line 91 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (broken_cluster); }} + break; + case 12: +#line 92 "hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ found_syllable (non_indic_cluster); }} + break; + case 14: +#line 87 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (consonant_syllable); }} + break; + case 16: +#line 88 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (vowel_syllable); }} + break; + case 21: +#line 89 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (standalone_cluster); }} + break; + case 23: +#line 90 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (symbol_cluster); }} + break; + case 18: +#line 91 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (broken_cluster); }} + break; + case 20: +#line 92 "hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ found_syllable (non_indic_cluster); }} + break; + case 1: +#line 87 "hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (consonant_syllable); }} + break; + case 3: +#line 88 "hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (vowel_syllable); }} + break; + case 7: +#line 89 "hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (standalone_cluster); }} + break; + case 9: +#line 90 "hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (symbol_cluster); }} + break; + case 4: +#line 91 "hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (broken_cluster); }} + break; + case 5: +#line 1 "NONE" + { switch( act ) { + case 1: + {{p = ((te))-1;} found_syllable (consonant_syllable); } + break; + case 5: + {{p = ((te))-1;} found_syllable (broken_cluster); } + break; + case 6: + {{p = ((te))-1;} found_syllable (non_indic_cluster); } + break; + } + } + break; + case 8: +#line 1 "NONE" + {te = p+1;} +#line 87 "hb-ot-shape-complex-indic-machine.rl" + {act = 1;} + break; + case 6: +#line 1 "NONE" + {te = p+1;} +#line 91 "hb-ot-shape-complex-indic-machine.rl" + {act = 5;} + break; + case 13: +#line 1 "NONE" + {te = p+1;} +#line 92 "hb-ot-shape-complex-indic-machine.rl" + {act = 6;} + break; +#line 1536 "hb-ot-shape-complex-indic-machine.hh" + } + +_again: + switch ( _indic_syllable_machine_to_state_actions[cs] ) { + case 10: +#line 1 "NONE" + {ts = 0;} + break; +#line 1545 "hb-ot-shape-complex-indic-machine.hh" + } + + 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 126 "hb-ot-shape-complex-indic-machine.rl" + +} + +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl new file mode 100644 index 000000000..86a7ceb22 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl @@ -0,0 +1,129 @@ +/* + * 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" + +%%{ + machine indic_syllable_machine; + alphtype unsigned char; + write data; +}%% + +%%{ + +# Same order as enum indic_category_t. Not sure how to avoid duplication. +X = 0; +C = 1; +V = 2; +N = 3; +H = 4; +ZWNJ = 5; +ZWJ = 6; +M = 7; +SM = 8; +VD = 9; +A = 10; +PLACEHOLDER = 11; +DOTTEDCIRCLE = 12; +RS = 13; +Coeng = 14; +Repha = 15; +Ra = 16; +CM = 17; +Symbol= 18; + +c = (C | Ra); # is_consonant +n = ((ZWNJ?.RS)? (N.N?)?); # is_consonant_modifier +z = ZWJ|ZWNJ; # is_joiner +h = H | Coeng; # is_halant_or_coeng +reph = (Ra H | Repha); # possible reph + +cn = c.ZWJ?.n?; +forced_rakar = ZWJ H ZWJ Ra; +symbol = Symbol.N?; +matra_group = z{0,3}.M.N?.(H | forced_rakar)?; +syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}? VD{0,2}; +place_holder = PLACEHOLDER | DOTTEDCIRCLE; +halant_group = (z?.h.(ZWJ.N?)?); +final_halant_group = halant_group | h.ZWNJ; +medial_group = CM?; +halant_or_matra_group = (final_halant_group | (h.ZWJ)? matra_group{0,4}) (Coeng (cn|V))?; + + +consonant_syllable = Repha? (cn.halant_group){0,4} cn medial_group halant_or_matra_group syllable_tail; +vowel_syllable = reph? V.n? (ZWJ | (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail); +standalone_cluster = (Repha? PLACEHOLDER | reph? DOTTEDCIRCLE).n? (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail; +symbol_cluster = symbol syllable_tail; +broken_cluster = reph? n? (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (consonant_syllable); }; + vowel_syllable => { found_syllable (vowel_syllable); }; + standalone_cluster => { found_syllable (standalone_cluster); }; + symbol_cluster => { found_syllable (symbol_cluster); }; + broken_cluster => { found_syllable (broken_cluster); }; + other => { found_syllable (non_indic_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].indic_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh new file mode 100644 index 000000000..5879c3e49 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh @@ -0,0 +1,189 @@ +/* + * 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" +#include "hb-ot-shape-private.hh" /* XXX Remove */ + + +#define INDIC_TABLE_ELEMENT_TYPE uint16_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 = 1, + OT_V = 2, + OT_N = 3, + OT_H = 4, + OT_ZWNJ = 5, + OT_ZWJ = 6, + OT_M = 7, + OT_SM = 8, + OT_VD = 9, + OT_A = 10, + OT_PLACEHOLDER = 11, + OT_DOTTEDCIRCLE = 12, + OT_RS = 13, /* Register Shifter, used in Khmer OT spec. */ + OT_Coeng = 14, /* Khmer-style Virama. */ + OT_Repha = 15, /* Atomically-encoded logical or visual repha. */ + OT_Ra = 16, + OT_CM = 17, /* Consonant-Medial. */ + OT_Symbol = 18 /* Avagraha, etc that take marks (SM,A,VD). */ +}; + +#define MEDIAL_FLAGS (FLAG (OT_CM)) + +/* 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! */ +#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) +#define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)) +#define HALANT_OR_COENG_FLAGS (FLAG (OT_H) | FLAG (OT_Coeng)) + + +/* Visual positions in a syllable from left to right. */ +enum indic_position_t { + POS_START, + + POS_RA_TO_BECOME_REPH, + POS_PRE_M, + POS_PRE_C, + + POS_BASE_C, + POS_AFTER_MAIN, + + POS_ABOVE_C, + + POS_BEFORE_SUB, + POS_BELOW_C, + POS_AFTER_SUB, + + POS_BEFORE_POST, + POS_POST_C, + POS_AFTER_POST, + + POS_FINAL_C, + POS_SMVD, + + POS_END +}; + +/* Categories used in IndicSyllabicCategory.txt from UCD. */ +enum indic_syllabic_category_t { + INDIC_SYLLABIC_CATEGORY_OTHER = OT_X, + + INDIC_SYLLABIC_CATEGORY_AVAGRAHA = OT_Symbol, + INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM, + INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER = OT_PLACEHOLDER, /* Don't care. */ + INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK = OT_A, + INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_CM, + INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER = OT_M, /* U+17CD only. */ + INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_CM, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_PLACEHOLDER, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA = OT_Repha, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED = OT_X, /* Don't care. */ + INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_CM, + INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA = OT_N, + INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER = OT_Repha, /* TODO */ + INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK = OT_SM, + INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER = OT_Coeng, + INDIC_SYLLABIC_CATEGORY_JOINER = OT_ZWJ, + INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X, + INDIC_SYLLABIC_CATEGORY_NON_JOINER = OT_ZWNJ, + INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N, + INDIC_SYLLABIC_CATEGORY_NUMBER = OT_PLACEHOLDER, + INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER = OT_PLACEHOLDER, /* Don't care. */ + INDIC_SYLLABIC_CATEGORY_PURE_KILLER = OT_M, /* Is like a vowel matra. */ + INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_RS, + INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER = OT_M, /* Misc Khmer signs. */ + INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X, + INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_N, + 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_END, + + INDIC_MATRA_CATEGORY_LEFT = POS_PRE_C, + INDIC_MATRA_CATEGORY_TOP = POS_ABOVE_C, + INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW_C, + INDIC_MATRA_CATEGORY_RIGHT = POS_POST_C, + + /* These should resolve to the position of the last part of the split sequence. */ + INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, + INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, + INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_TOP, + INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, + INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, + + INDIC_MATRA_CATEGORY_OVERSTRUCK = POS_AFTER_MAIN, + INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = POS_PRE_M +}; + +#define INDIC_COMBINE_CATEGORIES(S,M) \ + ( \ + ASSERT_STATIC_EXPR_ZERO (S < 255 && M < 255) + \ + ( S | \ + ( \ + ( \ + S == INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL || \ + S == INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK || \ + S == INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER || \ + S == INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA || \ + S == INDIC_SYLLABIC_CATEGORY_VIRAMA || \ + S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT || \ + false \ + ? M : INDIC_MATRA_CATEGORY_NOT_APPLICABLE \ + ) << 8 \ + ) \ + ) \ + ) + +HB_INTERNAL INDIC_TABLE_ELEMENT_TYPE +hb_indic_get_categories (hb_codepoint_t u); + +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc new file mode 100644 index 000000000..80a6b25e3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc @@ -0,0 +1,484 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-9.0.0.txt + * # Date: 2016-05-21, 02:46:00 GMT [RP] + * # IndicPositionalCategory-9.0.0.txt + * # Date: 2016-02-25, 00:48:00 GMT [RP] + * # Blocks-9.0.0.txt + * # Date: 2016-02-05, 23:48:00 GMT [KW] + */ + +#include "hb-ot-shape-complex-indic-private.hh" + + +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 15 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 67 chars; Bindu */ +#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ +#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 53 chars; Cantillation_Mark */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 1907 chars; Consonant */ +#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 10 chars; Consonant_Dead */ +#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 62 chars; Consonant_Final */ +#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ +#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ +#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 22 chars; Consonant_Medial */ +#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 16 chars; Consonant_Placeholder */ +#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 1 chars; Consonant_Preceding_Repha */ +#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 2 chars; Consonant_Prefixed */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 90 chars; Consonant_Subjoined */ +#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ +#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 4 chars; Consonant_With_Stacker */ +#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 2 chars; Gemination_Mark */ +#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 7 chars; Invisible_Stacker */ +#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */ +#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ +#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ +#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 24 chars; Nukta */ +#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 459 chars; Number */ +#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ +#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ +#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 16 chars; Pure_Killer */ +#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */ +#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 22 chars; Syllable_Modifier */ +#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ +#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 24 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 31 chars; Visarga */ +#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 602 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 431 chars; Vowel_Independent */ + +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 300 chars; Bottom */ +#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ +#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 57 chars; Left */ +#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 21 chars; Left_And_Right */ +#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ +#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 258 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 342 chars; Top */ +#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 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 /* 6 chars; Top_And_Left */ +#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */ +#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */ +#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */ + +#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M) + + +static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { + + +#define indic_offset_0x0028u 0 + + + /* Basic Latin */ + + /* 0028 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(CP,x), _(x,x), _(x,x), + /* 0030 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0038 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x00b0u 24 + + + /* Latin-1 Supplement */ + + /* 00B0 */ _(x,x), _(x,x), _(SM,x), _(SM,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 00B8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 00C0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 00C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 00D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(CP,x), + +#define indic_offset_0x0900u 64 + + + /* Devanagari */ + + /* 0900 */ _(Bi,T), _(Bi,T), _(Bi,T), _(Vs,R), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 0908 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 0910 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0918 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0920 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0928 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0930 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0938 */ _(C,x), _(C,x), _(M,T), _(M,R), _(N,B), _(A,x), _(M,R), _(M,L), + /* 0940 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), + /* 0948 */ _(M,T), _(M,R), _(M,R), _(M,R), _(M,R), _(V,B), _(M,L), _(M,R), + /* 0950 */ _(x,x), _(Ca,T), _(Ca,B), _(x,T), _(x,T), _(M,T), _(M,B), _(M,B), + /* 0958 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0960 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0968 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0970 */ _(x,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 0978 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + + /* Bengali */ + + /* 0980 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0988 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(VI,x), + /* 0990 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0998 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 09A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 09A8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 09B0 */ _(C,x), _(x,x), _(C,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), + /* 09B8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,L), + /* 09C0 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L), + /* 09C8 */ _(M,L), _(x,x), _(x,x), _(M,LR), _(M,LR), _(V,B), _(CD,x), _(x,x), + /* 09D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), + /* 09D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), + /* 09E0 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 09E8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 09F0 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 09F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Gurmukhi */ + + /* 0A00 */ _(x,x), _(Bi,T), _(Bi,T), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0A08 */ _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x), + /* 0A10 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0A18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0A20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0A28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0A30 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), + /* 0A38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(x,x), _(M,R), _(M,L), + /* 0A40 */ _(M,R), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), + /* 0A48 */ _(M,T), _(x,x), _(x,x), _(M,T), _(M,T), _(V,B), _(x,x), _(x,x), + /* 0A50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0A58 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), + /* 0A60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0A70 */ _(Bi,T), _(GM,T), _(CP,x), _(CP,x), _(x,x), _(CM,B), _(x,x), _(x,x), + /* 0A78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Gujarati */ + + /* 0A80 */ _(x,x), _(Bi,T), _(Bi,T), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0A88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), + /* 0A90 */ _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0A98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0AA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0AA8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0AB0 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), + /* 0AB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,L), + /* 0AC0 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(x,x), _(M,T), + /* 0AC8 */ _(M,T), _(M,TR), _(x,x), _(M,R), _(M,R), _(V,B), _(x,x), _(x,x), + /* 0AD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0AD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0AE0 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0AE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0AF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0AF8 */ _(x,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Oriya */ + + /* 0B00 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0B08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(VI,x), + /* 0B10 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0B18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0B20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0B28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0B30 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), + /* 0B38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T), + /* 0B40 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L), + /* 0B48 */ _(M,TL), _(x,x), _(x,x), _(M,LR),_(M,TLR), _(V,B), _(x,x), _(x,x), + /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,TR), + /* 0B58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), + /* 0B60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0B68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0B70 */ _(x,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0B78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Tamil */ + + /* 0B80 */ _(x,x), _(x,x), _(Bi,T), _(ML,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0B88 */ _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(x,x), _(VI,x), _(VI,x), + /* 0B90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(x,x), _(x,x), + /* 0B98 */ _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(C,x), _(C,x), + /* 0BA0 */ _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), + /* 0BA8 */ _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), + /* 0BB0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0BB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), _(M,R), + /* 0BC0 */ _(M,T), _(M,R), _(M,R), _(x,x), _(x,x), _(x,x), _(M,L), _(M,L), + /* 0BC8 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T), _(x,x), _(x,x), + /* 0BD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), + /* 0BD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0BE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0BE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0BF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0BF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Telugu */ + + /* 0C00 */ _(Bi,T), _(Bi,R), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), + /* 0C10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0C18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0C20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0C28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0C30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0C38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(A,x), _(M,T), _(M,T), + /* 0C40 */ _(M,T), _(M,R), _(M,R), _(M,R), _(M,R), _(x,x), _(M,T), _(M,T), + /* 0C48 */ _(M,TB), _(x,x), _(M,T), _(M,T), _(M,T), _(V,T), _(x,x), _(x,x), + /* 0C50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,B), _(x,x), + /* 0C58 */ _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0C60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0C68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0C70 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0C78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Kannada */ + + /* 0C80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), + /* 0C90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0C98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0CA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0CA8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0CB0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), + /* 0CB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T), + /* 0CC0 */ _(M,TR), _(M,R), _(M,R), _(M,R), _(M,R), _(x,x), _(M,T), _(M,TR), + /* 0CC8 */ _(M,TR), _(x,x), _(M,TR), _(M,TR), _(M,T), _(V,T), _(x,x), _(x,x), + /* 0CD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), _(M,R), _(x,x), + /* 0CD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(x,x), + /* 0CE0 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0CE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0CF0 */ _(x,x),_(CWS,x),_(CWS,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0CF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Malayalam */ + + /* 0D00 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), + /* 0D10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 0D18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0D20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0D28 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0D30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0D38 */ _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(A,x), _(M,R), _(M,R), + /* 0D40 */ _(M,R), _(M,R), _(M,R), _(M,B), _(M,B), _(x,x), _(M,L), _(M,L), + /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,x), _(x,x), + /* 0D50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(M,R), + /* 0D58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x), + /* 0D60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0D68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0D70 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0D78 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), + + /* Sinhala */ + + /* 0D80 */ _(x,x), _(x,x), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D90 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), + /* 0D98 */ _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0DA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0DA8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0DB0 */ _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0DB8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(x,x), + /* 0DC0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), + /* 0DC8 */ _(x,x), _(x,x), _(V,T), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), + /* 0DD0 */ _(M,R), _(M,R), _(M,T), _(M,T), _(M,B), _(x,x), _(M,B), _(x,x), + /* 0DD8 */ _(M,R), _(M,L), _(M,TL), _(M,L), _(M,LR),_(M,TLR), _(M,LR), _(M,R), + /* 0DE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 0DE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 0DF0 */ _(x,x), _(x,x), _(M,R), _(M,R), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x1000u 1336 + + + /* Myanmar */ + + /* 1000 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1008 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1010 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1018 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1020 */ _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 1028 */ _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), _(M,T), _(M,T), _(M,B), + /* 1030 */ _(M,B), _(M,L), _(M,T), _(M,T), _(M,T), _(M,T), _(Bi,T), _(TM,B), + /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R), _(CM,x), _(CM,B), _(CM,B), _(C,x), + /* 1040 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1048 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(CP,x), _(x,x), + /* 1050 */ _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), + /* 1058 */ _(M,B), _(M,B), _(C,x), _(C,x), _(C,x), _(C,x), _(CM,B), _(CM,B), + /* 1060 */ _(CM,B), _(C,x), _(M,R), _(TM,R), _(TM,R), _(C,x), _(C,x), _(M,R), + /* 1068 */ _(M,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(C,x), _(C,x), + /* 1070 */ _(C,x), _(M,T), _(M,T), _(M,T), _(M,T), _(C,x), _(C,x), _(C,x), + /* 1078 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1080 */ _(C,x), _(C,x), _(CM,B), _(M,R), _(M,L), _(M,T), _(M,T), _(TM,R), + /* 1088 */ _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,B), _(C,x), _(TM,R), + /* 1090 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1098 */ _(Nd,x), _(Nd,x), _(TM,R), _(TM,R), _(M,R), _(M,T), _(x,x), _(x,x), + +#define indic_offset_0x1780u 1496 + + + /* Khmer */ + + /* 1780 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1788 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1790 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 1798 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 17A0 */ _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 17A8 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 17B0 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(M,R), _(M,T), + /* 17B8 */ _(M,T), _(M,T), _(M,T), _(M,B), _(M,B), _(M,B), _(M,TL),_(M,TLR), + /* 17C0 */ _(M,LR), _(M,L), _(M,L), _(M,L), _(M,LR), _(M,LR), _(Bi,T), _(Vs,R), + /* 17C8 */ _(M,R), _(RS,T), _(RS,T), _(SM,T),_(CSR,T), _(CK,T), _(SM,T), _(SM,T), + /* 17D0 */ _(SM,T), _(PK,T), _(IS,x), _(SM,T), _(x,x), _(x,x), _(x,x), _(x,x), + /* 17D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(A,x), _(SM,T), _(x,x), _(x,x), + /* 17E0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 17E8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x1cd0u 1608 + + + /* Vedic Extensions */ + + /* 1CD0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(x,x), _(Ca,O), _(Ca,B), _(Ca,B), _(Ca,B), + /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B), + /* 1CE0 */ _(Ca,T), _(Ca,R), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), + /* 1CE8 */ _(x,O), _(x,x), _(x,x), _(x,x), _(x,x), _(x,B), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(Ca,T), _(x,x), _(x,x), _(x,x), + /* 1CF8 */ _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x2008u 1656 + + + /* General Punctuation */ + + /* 2008 */ _(x,x), _(x,x), _(x,x), _(x,x),_(ZWNJ,x),_(ZWJ,x), _(x,x), _(x,x), + /* 2010 */ _(CP,x), _(CP,x), _(CP,x), _(CP,x), _(CP,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x2070u 1672 + + + /* Superscripts and Subscripts */ + + /* 2070 */ _(x,x), _(x,x), _(x,x), _(x,x), _(SM,x), _(x,x), _(x,x), _(x,x), + /* 2078 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 2080 */ _(x,x), _(x,x), _(SM,x), _(SM,x), _(SM,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0xa8e0u 1696 + + + /* Devanagari Extended */ + + /* A8E0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), + /* A8E8 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), + /* A8F0 */ _(Ca,T), _(Ca,T), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0xa9e0u 1720 + + + /* Myanmar Extended-B */ + + /* A9E0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,T), _(x,x), _(C,x), + /* A9E8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* A9F0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* A9F8 */ _(Nd,x), _(Nd,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), + +#define indic_offset_0xaa60u 1752 + + + /* Myanmar Extended-A */ + + /* AA60 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* AA68 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* AA70 */ _(x,x), _(C,x), _(C,x), _(C,x), _(CP,x), _(CP,x), _(CP,x), _(x,x), + /* AA78 */ _(x,x), _(x,x), _(C,x), _(TM,R), _(TM,T), _(TM,R), _(C,x), _(C,x), + +}; /* Table items: 1784; occupancy: 69% */ + +INDIC_TABLE_ELEMENT_TYPE +hb_indic_get_categories (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u]; + if (hb_in_range (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u]; + if (hb_in_range (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u]; + if (unlikely (u == 0x00A0u)) return _(CP,x); + break; + + case 0x1u: + if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u]; + if (hb_in_range (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u]; + if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u]; + break; + + case 0x2u: + if (hb_in_range (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u]; + if (hb_in_range (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u]; + if (unlikely (u == 0x25CCu)) return _(CP,x); + break; + + case 0xAu: + if (hb_in_range (u, 0xA8E0u, 0xA8F7u)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u]; + if (hb_in_range (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u]; + if (hb_in_range (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u]; + break; + + default: + break; + } + return _(x,x); +} + +#undef _ + +#undef ISC_A +#undef ISC_Bi +#undef ISC_BJN +#undef ISC_Ca +#undef ISC_C +#undef ISC_CD +#undef ISC_CF +#undef ISC_CHL +#undef ISC_CK +#undef ISC_CM +#undef ISC_CP +#undef ISC_CPR +#undef ISC_CPrf +#undef ISC_CS +#undef ISC_CSR +#undef ISC_CWS +#undef ISC_GM +#undef ISC_IS +#undef ISC_ZWJ +#undef ISC_ML +#undef ISC_ZWNJ +#undef ISC_N +#undef ISC_Nd +#undef ISC_NJ +#undef ISC_x +#undef ISC_PK +#undef ISC_RS +#undef ISC_SM +#undef ISC_TL +#undef ISC_TM +#undef ISC_V +#undef ISC_Vs +#undef ISC_Vo +#undef ISC_M +#undef ISC_VI + +#undef IMC_B +#undef IMC_BR +#undef IMC_L +#undef IMC_LR +#undef IMC_x +#undef IMC_O +#undef IMC_R +#undef IMC_T +#undef IMC_TB +#undef IMC_TBR +#undef IMC_TL +#undef IMC_TLR +#undef IMC_TR +#undef IMC_VOL + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc new file mode 100644 index 000000000..b48fb561c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc @@ -0,0 +1,1820 @@ +/* + * 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 "hb-ot-shape-complex-indic-private.hh" +#include "hb-ot-layout-private.hh" + +/* buffer var allocations */ +#define indic_category() complex_var_u8_0() /* indic_category_t */ +#define indic_position() complex_var_u8_1() /* indic_position_t */ + + +/* + * Indic shaper. + */ + + +#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base)) + +#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u)) +#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u)) +#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u)) +#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u)) +#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u)) +#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u)) +#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u)) +#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u)) +#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u)) +#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u)) +#define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780u)) + + +#define MATRA_POS_LEFT(u) POS_PRE_M +#define MATRA_POS_RIGHT(u) ( \ + IS_DEVA(u) ? POS_AFTER_SUB : \ + IS_BENG(u) ? POS_AFTER_POST : \ + IS_GURU(u) ? POS_AFTER_POST : \ + IS_GUJR(u) ? POS_AFTER_POST : \ + IS_ORYA(u) ? POS_AFTER_POST : \ + IS_TAML(u) ? POS_AFTER_POST : \ + IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \ + IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \ + IS_MLYM(u) ? POS_AFTER_POST : \ + IS_SINH(u) ? POS_AFTER_SUB : \ + IS_KHMR(u) ? POS_AFTER_POST : \ + /*default*/ POS_AFTER_SUB \ + ) +#define MATRA_POS_TOP(u) ( /* BENG and MLYM don't have top matras. */ \ + IS_DEVA(u) ? POS_AFTER_SUB : \ + IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \ + IS_GUJR(u) ? POS_AFTER_SUB : \ + IS_ORYA(u) ? POS_AFTER_MAIN : \ + IS_TAML(u) ? POS_AFTER_SUB : \ + IS_TELU(u) ? POS_BEFORE_SUB : \ + IS_KNDA(u) ? POS_BEFORE_SUB : \ + IS_SINH(u) ? POS_AFTER_SUB : \ + IS_KHMR(u) ? POS_AFTER_POST : \ + /*default*/ POS_AFTER_SUB \ + ) +#define MATRA_POS_BOTTOM(u) ( \ + IS_DEVA(u) ? POS_AFTER_SUB : \ + IS_BENG(u) ? POS_AFTER_SUB : \ + IS_GURU(u) ? POS_AFTER_POST : \ + IS_GUJR(u) ? POS_AFTER_POST : \ + IS_ORYA(u) ? POS_AFTER_SUB : \ + IS_TAML(u) ? POS_AFTER_POST : \ + IS_TELU(u) ? POS_BEFORE_SUB : \ + IS_KNDA(u) ? POS_BEFORE_SUB : \ + IS_MLYM(u) ? POS_AFTER_POST : \ + IS_SINH(u) ? POS_AFTER_SUB : \ + IS_KHMR(u) ? POS_AFTER_POST : \ + /*default*/ POS_AFTER_SUB \ + ) + +static inline indic_position_t +matra_position (hb_codepoint_t u, indic_position_t side) +{ + switch ((int) side) + { + case POS_PRE_C: return MATRA_POS_LEFT (u); + case POS_POST_C: return MATRA_POS_RIGHT (u); + case POS_ABOVE_C: return MATRA_POS_TOP (u); + case POS_BELOW_C: return MATRA_POS_BOTTOM (u); + }; + return side; +} + +/* 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[] = { + 0x0930u, /* Devanagari */ + 0x09B0u, /* Bengali */ + 0x09F0u, /* Bengali */ + 0x0A30u, /* Gurmukhi */ /* No Reph */ + 0x0AB0u, /* Gujarati */ + 0x0B30u, /* Oriya */ + 0x0BB0u, /* Tamil */ /* No Reph */ + 0x0C30u, /* Telugu */ /* Reph formed only with ZWJ */ + 0x0CB0u, /* Kannada */ + 0x0D30u, /* Malayalam */ /* No Reph, Logical Repha */ + + 0x0DBBu, /* Sinhala */ /* Reph formed only with ZWJ */ + + 0x179Au, /* Khmer */ /* No Reph, Visual Repha */ +}; + +static inline bool +is_ra (hb_codepoint_t u) +{ + for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++) + if (u == ra_chars[i]) + return true; + return false; +} + +static inline bool +is_one_of (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_SAFE (info.indic_category()) & flags); +} + +static inline bool +is_joiner (const hb_glyph_info_t &info) +{ + return is_one_of (info, JOINER_FLAGS); +} + +static inline bool +is_consonant (const hb_glyph_info_t &info) +{ + return is_one_of (info, CONSONANT_FLAGS); +} + +static inline bool +is_halant_or_coeng (const hb_glyph_info_t &info) +{ + return is_one_of (info, HALANT_OR_COENG_FLAGS); +} + +static inline void +set_indic_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + indic_category_t cat = (indic_category_t) (type & 0x7Fu); + indic_position_t pos = (indic_position_t) (type >> 8); + + + /* + * Re-assign category + */ + + /* The following act more like the Bindus. */ + if (unlikely (hb_in_range (u, 0x0953u, 0x0954u))) + cat = OT_SM; + /* The following act like consonants. */ + else if (unlikely (hb_in_ranges (u, 0x0A72u, 0x0A73u, + 0x1CF5u, 0x1CF6u))) + cat = OT_C; + /* TODO: The following should only be allowed after a Visarga. + * For now, just treat them like regular tone marks. */ + else if (unlikely (hb_in_range (u, 0x1CE2u, 0x1CE8u))) + cat = OT_A; + /* TODO: The following should only be allowed after some of + * the nasalization marks, maybe only for U+1CE9..U+1CF1. + * For now, just treat them like tone marks. */ + else if (unlikely (u == 0x1CEDu)) + cat = OT_A; + /* The following take marks in standalone clusters, similar to Avagraha. */ + else if (unlikely (hb_in_ranges (u, 0xA8F2u, 0xA8F7u, + 0x1CE9u, 0x1CECu, + 0x1CEEu, 0x1CF1u))) + { + cat = OT_Symbol; + ASSERT_STATIC ((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol); + } + else if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */ + else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u))) + cat = OT_PLACEHOLDER; + else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE; + + + /* + * Re-assign position. + */ + + if ((FLAG_SAFE (cat) & CONSONANT_FLAGS)) + { + pos = POS_BASE_C; + if (is_ra (u)) + cat = OT_Ra; + } + else if (cat == OT_M) + { + pos = matra_position (u, pos); + } + else if ((FLAG_SAFE (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol)))) + { + pos = POS_SMVD; + } + + if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */ + + + + info.indic_category() = cat; + info.indic_position() = pos; +} + +/* + * Things above this line should ideally be moved to the Indic table itself. + */ + + +/* + * Indic configurations. Note that we do not want to keep every single script-specific + * behavior in these tables necessarily. This should mainly be used for per-script + * properties that are cheaper keeping here, than in the code. Ie. if, say, one and + * only one script has an exception, that one script can be if'ed directly in the code, + * instead of adding a new flag in these structs. + */ + +enum base_position_t { + BASE_POS_FIRST, + BASE_POS_LAST_SINHALA, + BASE_POS_LAST +}; +enum reph_position_t { + REPH_POS_AFTER_MAIN = POS_AFTER_MAIN, + REPH_POS_BEFORE_SUB = POS_BEFORE_SUB, + REPH_POS_AFTER_SUB = POS_AFTER_SUB, + REPH_POS_BEFORE_POST = POS_BEFORE_POST, + REPH_POS_AFTER_POST = POS_AFTER_POST, + REPH_POS_DONT_CARE = POS_RA_TO_BECOME_REPH +}; +enum reph_mode_t { + REPH_MODE_IMPLICIT, /* Reph formed out of initial Ra,H sequence. */ + REPH_MODE_EXPLICIT, /* Reph formed out of initial Ra,H,ZWJ sequence. */ + REPH_MODE_VIS_REPHA, /* Encoded Repha character, no reordering needed. */ + REPH_MODE_LOG_REPHA /* Encoded Repha character, needs reordering. */ +}; +enum blwf_mode_t { + BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */ + BLWF_MODE_POST_ONLY /* Below-forms feature applied to post-base only. */ +}; +struct indic_config_t +{ + hb_script_t script; + bool has_old_spec; + hb_codepoint_t virama; + base_position_t base_pos; + reph_position_t reph_pos; + reph_mode_t reph_mode; + blwf_mode_t blwf_mode; +}; + +static const indic_config_t indic_configs[] = +{ + /* Default. Should be first. */ + {HB_SCRIPT_INVALID, false, 0,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_DEVANAGARI,true, 0x094Du,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_BENGALI, true, 0x09CDu,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GUJARATI, true, 0x0ACDu,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_ORIYA, true, 0x0B4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TAMIL, true, 0x0BCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TELUGU, true, 0x0C4Du,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_KANNADA, true, 0x0CCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_SINHALA, false,0x0DCAu,BASE_POS_LAST_SINHALA, + REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_KHMER, false,0x17D2u,BASE_POS_FIRST,REPH_POS_DONT_CARE, REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST}, +}; + + + +/* + * Indic shaper. + */ + +struct feature_list_t { + hb_tag_t tag; + hb_ot_map_feature_flags_t flags; +}; + +static const feature_list_t +indic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after initial_reordering. + */ + {HB_TAG('n','u','k','t'), F_GLOBAL}, + {HB_TAG('a','k','h','n'), F_GLOBAL}, + {HB_TAG('r','p','h','f'), F_NONE}, + {HB_TAG('r','k','r','f'), F_GLOBAL}, + {HB_TAG('p','r','e','f'), F_NONE}, + {HB_TAG('b','l','w','f'), F_NONE}, + {HB_TAG('a','b','v','f'), F_NONE}, + {HB_TAG('h','a','l','f'), F_NONE}, + {HB_TAG('p','s','t','f'), F_NONE}, + {HB_TAG('v','a','t','u'), F_GLOBAL}, + {HB_TAG('c','j','c','t'), F_GLOBAL}, + {HB_TAG('c','f','a','r'), F_NONE}, + /* + * Other features. + * These features are applied all at once, after final_reordering. + * Default Bengali font in Windows for example has intermixed + * lookups for init,pres,abvs,blws features. + */ + {HB_TAG('i','n','i','t'), F_NONE}, + {HB_TAG('p','r','e','s'), F_GLOBAL}, + {HB_TAG('a','b','v','s'), F_GLOBAL}, + {HB_TAG('b','l','w','s'), F_GLOBAL}, + {HB_TAG('p','s','t','s'), F_GLOBAL}, + {HB_TAG('h','a','l','n'), F_GLOBAL}, + /* Positioning features, though we don't care about the types. */ + {HB_TAG('d','i','s','t'), F_GLOBAL}, + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, +}; + +/* + * Must be in the same order as the indic_features array. + */ +enum { + _NUKT, + _AKHN, + RPHF, + _RKRF, + PREF, + BLWF, + ABVF, + HALF, + PSTF, + _VATU, + _CJCT, + CFAR, + + INIT, + _PRES, + _ABVS, + _BLWS, + _PSTS, + _HALN, + _DIST, + _ABVM, + _BLWM, + + INDIC_NUM_FEATURES, + INDIC_BASIC_FEATURES = INIT /* Don't forget to update this! */ +}; + +static void +setup_syllables (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +initial_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +final_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +clear_syllables (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_indic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables); + + map->add_global_bool_feature (HB_TAG('l','o','c','l')); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->add_global_bool_feature (HB_TAG('c','c','m','p')); + + + unsigned int i = 0; + map->add_gsub_pause (initial_reordering); + for (; i < INDIC_BASIC_FEATURES; i++) { + map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ); + map->add_gsub_pause (NULL); + } + map->add_gsub_pause (final_reordering); + for (; i < INDIC_NUM_FEATURES; i++) { + map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ); + } + + map->add_global_bool_feature (HB_TAG('c','a','l','t')); + map->add_global_bool_feature (HB_TAG('c','l','i','g')); + + map->add_gsub_pause (clear_syllables); +} + +static void +override_features_indic (hb_ot_shape_planner_t *plan) +{ + /* Uniscribe does not apply 'kern' in Khmer. */ + if (hb_options ().uniscribe_bug_compatible) + { + switch ((hb_tag_t) plan->props.script) + { + case HB_SCRIPT_KHMER: + plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL); + break; + } + } + + plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL); +} + + +struct would_substitute_feature_t +{ + inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) + { + zero_context = zero_context_; + map->get_stage_lookups (0/*GSUB*/, + map->get_feature_stage (0/*GSUB*/, feature_tag), + &lookups, &count); + } + + inline bool would_substitute (const hb_codepoint_t *glyphs, + unsigned int glyphs_count, + hb_face_t *face) const + { + for (unsigned int i = 0; i < count; i++) + if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) + return true; + return false; + } + + private: + const hb_ot_map_t::lookup_map_t *lookups; + unsigned int count; + bool zero_context; +}; + +struct indic_shape_plan_t +{ + ASSERT_POD (); + + inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const + { + hb_codepoint_t glyph = virama_glyph; + if (unlikely (virama_glyph == (hb_codepoint_t) -1)) + { + if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph)) + glyph = 0; + /* Technically speaking, the spec says we should apply 'locl' to virama too. + * Maybe one day... */ + + /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph + * during shape planning... Instead, overwrite it here. It's safe. Don't worry! */ + (const_cast<indic_shape_plan_t *> (this))->virama_glyph = glyph; + } + + *pglyph = glyph; + return glyph != 0; + } + + const indic_config_t *config; + + bool is_old_spec; + hb_codepoint_t virama_glyph; + + would_substitute_feature_t rphf; + would_substitute_feature_t pref; + would_substitute_feature_t blwf; + would_substitute_feature_t pstf; + + hb_mask_t mask_array[INDIC_NUM_FEATURES]; +}; + +static void * +data_create_indic (const hb_ot_shape_plan_t *plan) +{ + indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) calloc (1, sizeof (indic_shape_plan_t)); + if (unlikely (!indic_plan)) + return NULL; + + indic_plan->config = &indic_configs[0]; + for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++) + if (plan->props.script == indic_configs[i].script) { + indic_plan->config = &indic_configs[i]; + break; + } + + indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); + indic_plan->virama_glyph = (hb_codepoint_t) -1; + + /* Use zero-context would_substitute() matching for new-spec of the main + * Indic scripts, and scripts with one spec only, but not for old-specs. + * The new-spec for all dual-spec scripts says zero-context matching happens. + * + * However, testing with Malayalam shows that old and new spec both allow + * context. Testing with Bengali new-spec however shows that it doesn't. + * So, the heuristic here is the way it is. It should *only* be changed, + * as we discover more cases of what Windows does. DON'T TOUCH OTHERWISE. + */ + bool zero_context = !indic_plan->is_old_spec && plan->props.script != HB_SCRIPT_MALAYALAM; + indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context); + indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); + indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); + indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); + + for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) + indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? + 0 : plan->map.get_1_mask (indic_features[i].tag); + + return indic_plan; +} + +static void +data_destroy_indic (void *data) +{ + free (data); +} + +static indic_position_t +consonant_position_from_face (const indic_shape_plan_t *indic_plan, + const hb_codepoint_t consonant, + const hb_codepoint_t virama, + hb_face_t *face) +{ + /* For old-spec, the order of glyphs is Consonant,Virama, + * whereas for new-spec, it's Virama,Consonant. However, + * some broken fonts (like Free Sans) simply copied lookups + * from old-spec to new-spec without modification. + * And oddly enough, Uniscribe seems to respect those lookups. + * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds + * base at 0. The font however, only has lookups matching + * 930,94D in 'blwf', not the expected 94D,930 (with new-spec + * table). As such, we simply match both sequences. Seems + * to work. */ + hb_codepoint_t glyphs[3] = {virama, consonant, virama}; + if (indic_plan->blwf.would_substitute (glyphs , 2, face) || + indic_plan->blwf.would_substitute (glyphs+1, 2, face)) + return POS_BELOW_C; + if (indic_plan->pstf.would_substitute (glyphs , 2, face) || + indic_plan->pstf.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + if (indic_plan->pref.would_substitute (glyphs , 2, face) || + indic_plan->pref.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + return POS_BASE_C; +} + + +enum syllable_type_t { + consonant_syllable, + vowel_syllable, + standalone_cluster, + symbol_cluster, + broken_cluster, + non_indic_cluster, +}; + +#include "hb-ot-shape-complex-indic-machine.hh" + + +static void +setup_masks_indic (const hb_ot_shape_plan_t *plan 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); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_indic_properties (info[i]); +} + +static void +setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + find_syllables (buffer); +} + +static int +compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->indic_position(); + int b = pb->indic_position(); + + return a < b ? -1 : a == b ? 0 : +1; +} + + + +static void +update_consonant_positions (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + + if (indic_plan->config->base_pos != BASE_POS_LAST) + return; + + hb_codepoint_t virama; + if (indic_plan->get_virama_glyph (font, &virama)) + { + hb_face_t *face = font->face; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (info[i].indic_position() == POS_BASE_C) + { + hb_codepoint_t consonant = info[i].codepoint; + info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face); + } + } +} + + +/* Rules from: + * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */ + +static void +initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + + /* 1. Find base consonant: + * + * The shaping engine finds the base consonant of the syllable, using the + * following algorithm: starting from the end of the syllable, move backwards + * until a consonant is found that does not have a below-base or post-base + * form (post-base forms have to follow below-base forms), or that is not a + * pre-base reordering Ra, or arrive at the first consonant. The consonant + * stopped at will be the base. + * + * 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. + */ + + unsigned int base = end; + bool has_reph = false; + + { + /* -> 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 (indic_plan->config->reph_pos != REPH_POS_DONT_CARE && + indic_plan->mask_array[RPHF] && + start + 3 <= end && + ( + (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == OT_ZWJ) + )) + { + /* See if it matches the 'rphf' feature. */ + hb_codepoint_t glyphs[3] = {info[start].codepoint, + info[start + 1].codepoint, + indic_plan->config->reph_mode == REPH_MODE_EXPLICIT ? + info[start + 2].codepoint : 0}; + if (indic_plan->rphf.would_substitute (glyphs, 2, face) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && + indic_plan->rphf.would_substitute (glyphs, 3, face))) + { + limit += 2; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == OT_Repha) + { + limit += 1; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + + switch (indic_plan->config->base_pos) + { + case BASE_POS_LAST: + { + /* -> starting from the end of the syllable, move backwards */ + unsigned int i = end; + bool seen_below = false; + do { + i--; + /* -> until a consonant is found */ + if (is_consonant (info[i])) + { + /* -> 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 || seen_below)) + { + base = i; + break; + } + if (info[i].indic_position() == POS_BELOW_C) + seen_below = true; + + /* -> or that is not a pre-base reordering Ra, + * + * IMPLEMENTATION NOTES: + * + * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped + * by the logic above already. + */ + + /* -> or arrive at the first consonant. The consonant stopped at will + * be the base. */ + base = i; + } + else + { + /* A ZWJ after a Halant stops the base search, and requests an explicit + * half form. + * A ZWJ before a Halant, requests a subjoined form instead, and hence + * search continues. This is particularly important for Bengali + * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */ + if (start < i && + info[i].indic_category() == OT_ZWJ && + info[i - 1].indic_category() == OT_H) + break; + } + } while (i > limit); + } + break; + + case BASE_POS_LAST_SINHALA: + { + /* Sinhala base positioning is slightly different from main Indic, in that: + * 1. Its ZWJ behavior is different, + * 2. We don't need to look into the font for consonant positions. + */ + + if (!has_reph) + base = limit; + + /* Find the last base consonant that is not blocked by ZWJ. If there is + * a ZWJ right before a base consonant, that would request a subjoined form. */ + for (unsigned int i = limit; i < end; i++) + if (is_consonant (info[i])) + { + if (limit < i && info[i - 1].indic_category() == OT_ZWJ) + break; + else + base = i; + } + + /* Mark all subsequent consonants as below. */ + for (unsigned int i = base + 1; i < end; i++) + if (is_consonant (info[i])) + info[i].indic_position() = POS_BELOW_C; + } + break; + + case BASE_POS_FIRST: + { + /* The first consonant is always the base. */ + + assert (indic_plan->config->reph_mode == REPH_MODE_VIS_REPHA); + assert (!has_reph); + + base = start; + + /* Mark all subsequent consonants as below. */ + for (unsigned int i = base + 1; i < end; i++) + if (is_consonant (info[i])) + info[i].indic_position() = POS_BELOW_C; + } + break; + } + + /* -> 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. + * + * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */ + if (has_reph && base == start && limit - base <= 2) { + /* Have no other consonant, so Reph is not formed and Ra becomes base. */ + has_reph = false; + } + } + + + /* 2. Decompose and reorder Matras: + * + * Each matra and any syllable modifier sign in the cluster are moved to the + * appropriate position relative to the consonant(s) in the cluster. The + * shaping engine decomposes two- or three-part matras into their constituent + * parts before any repositioning. Matra characters are classified by which + * consonant in a conjunct they have affinity for and are reordered to the + * following positions: + * + * o Before first half form in the syllable + * o After subjoined consonants + * o After post-form consonant + * o After main consonant (for above marks) + * + * IMPLEMENTATION NOTES: + * + * The normalize() routine has already decomposed matras for us, so we don't + * need to worry about that. + */ + + + /* 3. Reorder marks to canonical order: + * + * Adjacent nukta and halant or nukta and vedic sign are always repositioned + * if necessary, so that the nukta is first. + * + * IMPLEMENTATION NOTES: + * + * We don't need to do this: the normalize() routine already did this for us. + */ + + + /* Reorder characters */ + + for (unsigned int i = start; i < base; i++) + info[i].indic_position() = MIN (POS_PRE_C, (indic_position_t) info[i].indic_position()); + + if (base < end) + info[base].indic_position() = POS_BASE_C; + + /* Mark final consonants. A final consonant is one appearing after a matra, + * like in Khmer. */ + for (unsigned int i = base + 1; i < end; i++) + if (info[i].indic_category() == OT_M) { + for (unsigned int j = i + 1; j < end; j++) + if (is_consonant (info[j])) { + info[j].indic_position() = POS_FINAL_C; + break; + } + break; + } + + /* Handle beginning Ra */ + 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. + * + * Reports suggest that in some scripts Uniscribe does this only if there + * is *not* a Halant after last consonant already (eg. Kannada), while it + * does it unconditionally in other scripts (eg. Malayalam). We don't + * currently know about other scripts, so we single out Malayalam for now. + * + * Kannada test case: + * U+0C9A,U+0CCD,U+0C9A,U+0CCD + * With some versions of Lohit Kannada. + * https://bugs.freedesktop.org/show_bug.cgi?id=59118 + * + * Malayalam test case: + * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D + * With lohit-ttf-20121122/Lohit-Malayalam.ttf + */ + if (indic_plan->is_old_spec) + { + bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM; + 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 (is_consonant (info[j]) || + (disallow_double_halants && info[j].indic_category() == OT_H)) + break; + if (info[j].indic_category() != OT_H && j > i) { + /* Move Halant to after last consonant. */ + hb_glyph_info_t t = info[i]; + memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0])); + info[j] = t; + } + break; + } + } + + /* Attach misc marks to previous char to move with them. */ + { + indic_position_t last_pos = POS_START; + for (unsigned int i = start; i < end; i++) + { + if ((FLAG_SAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS))) + { + info[i].indic_position() = last_pos; + if (unlikely (info[i].indic_category() == OT_H && + info[i].indic_position() == POS_PRE_M)) + { + /* + * Uniscribe doesn't move the Halant with Left Matra. + * TEST: U+092B,U+093F,U+094DE + * We follow. This is important for the Sinhala + * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA + * where U+0DD9 is a left matra and U+0DCA is the virama. + * We don't want to move the virama with the left matra. + * TEST: U+0D9A,U+0DDA + */ + 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; + } + } + } else if (info[i].indic_position() != POS_SMVD) { + last_pos = (indic_position_t) info[i].indic_position(); + } + } + } + /* For post-base consonants let them own anything before them + * since the last consonant or matra. */ + { + unsigned int last = base; + for (unsigned int i = base + 1; i < end; i++) + if (is_consonant (info[i])) + { + for (unsigned int j = last + 1; j < i; j++) + if (info[j].indic_position() < POS_SMVD) + info[j].indic_position() = info[i].indic_position(); + last = i; + } else if (info[i].indic_category() == OT_M) + last = i; + } + + + { + /* Use syllable() for sort accounting temporarily. */ + unsigned int syllable = info[start].syllable(); + for (unsigned int i = start; i < end; i++) + info[i].syllable() = i - start; + + /* Sit tight, rock 'n roll! */ + hb_stable_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; + } + /* Things are out-of-control for post base positions, they may shuffle + * around like crazy. In old-spec mode, we move halants around, so in + * that case merge all clusters after base. Otherwise, check the sort + * order and merge as needed. + * For pre-base stuff, we handle cluster issues in final reordering. + * + * We could use buffer->sort() for this, if there was no special + * reordering of pre-base stuff happening later... + */ + if (indic_plan->is_old_spec || end - base > 127) + buffer->merge_clusters (base, end); + else + { + /* Note! syllable() is a one-byte field. */ + for (unsigned int i = base; i < end; i++) + if (info[i].syllable() != 255) + { + unsigned int max = i; + unsigned int j = start + info[i].syllable(); + while (j != i) + { + max = MAX (max, j); + unsigned int next = start + info[j].syllable(); + info[j].syllable() = 255; /* So we don't process j later again. */ + j = next; + } + if (i != max) + buffer->merge_clusters (i, max + 1); + } + } + + /* Put syllable back in. */ + for (unsigned int i = start; i < end; i++) + info[i].syllable() = syllable; + } + + /* 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 |= indic_plan->mask_array[RPHF]; + + /* Pre-base */ + mask = indic_plan->mask_array[HALF]; + if (!indic_plan->is_old_spec && + indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) + mask |= indic_plan->mask_array[BLWF]; + for (unsigned int i = start; i < base; i++) + info[i].mask |= mask; + /* Base */ + mask = 0; + if (base < end) + info[base].mask |= mask; + /* Post-base */ + mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF]; + for (unsigned int i = base + 1; i < end; i++) + info[i].mask |= mask; + } + + if (indic_plan->is_old_spec && + buffer->props.script == HB_SCRIPT_DEVANAGARI) + { + /* Old-spec eye-lash Ra needs special handling. From the + * spec: + * + * "The feature 'below-base form' is applied to consonants + * having below-base forms and following the base consonant. + * The exception is vattu, which may appear below half forms + * as well as below the base glyph. The feature 'below-base + * form' will be applied to all such occurrences of Ra as well." + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+0915 + * with Sanskrit 2003 font. + * + * However, note that Ra,Halant,ZWJ is the correct way to + * request eyelash form of Ra, so we wouldbn't inhibit it + * in that sequence. + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915 + */ + for (unsigned int i = start; i + 1 < base; i++) + if (info[i ].indic_category() == OT_Ra && + info[i+1].indic_category() == OT_H && + (i + 2 == base || + info[i+2].indic_category() != OT_ZWJ)) + { + info[i ].mask |= indic_plan->mask_array[BLWF]; + info[i+1].mask |= indic_plan->mask_array[BLWF]; + } + } + + unsigned int pref_len = 2; + if (indic_plan->mask_array[PREF] && base + pref_len < end) + { + /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */ + for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { + hb_codepoint_t glyphs[2]; + for (unsigned int j = 0; j < pref_len; j++) + glyphs[j] = info[i + j].codepoint; + if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) + { + for (unsigned int j = 0; j < pref_len; j++) + info[i++].mask |= indic_plan->mask_array[PREF]; + + /* Mark the subsequent stuff with 'cfar'. Used in Khmer. + * Read the feature spec. + * This allows distinguishing the following cases with MS Khmer fonts: + * U+1784,U+17D2,U+179A,U+17D2,U+1782 + * U+1784,U+17D2,U+1782,U+17D2,U+179A + */ + if (indic_plan->mask_array[CFAR]) + for (; i < end; i++) + info[i].mask |= indic_plan->mask_array[CFAR]; + + break; + } + } + } + + /* Apply ZWJ/ZWNJ effects */ + 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; + + do { + j--; + + /* ZWJ/ZWNJ should disable CJCT. They do that by simply + * being there, since we don't skip them for the CJCT + * feature (ie. F_MANUAL_ZWJ) */ + + /* A ZWNJ disables HALF. */ + if (non_joiner) + info[j].mask &= ~indic_plan->mask_array[HALF]; + + } while (j > start && !is_consonant (info[j])); + } +} + +static void +initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We treat placeholder/dotted-circle as if they are consonants, so we + * should just chain. Only if not in compatibility mode that is... */ + + if (hb_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 (plan, face, buffer, start, end); +} + +static void +initial_reordering_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) + { + case vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ + case consonant_syllable: + initial_reordering_consonant_syllable (plan, face, buffer, start, end); + break; + + case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ + case standalone_cluster: + initial_reordering_standalone_cluster (plan, face, buffer, start, end); + break; + + case symbol_cluster: + case non_indic_cluster: + break; + } +} + +static inline void +insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) +{ + /* Note: This loop is extra overhead, but should not be measurable. */ + bool has_broken_syllables = false; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if ((info[i].syllable() & 0x0F) == broken_cluster) + { + has_broken_syllables = true; + break; + } + if (likely (!has_broken_syllables)) + return; + + + hb_codepoint_t dottedcircle_glyph; + if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + set_indic_properties (dottedcircle); + dottedcircle.codepoint = dottedcircle_glyph; + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len && !buffer->in_error) + { + unsigned int syllable = buffer->cur().syllable(); + syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + { + last_syllable = syllable; + + hb_glyph_info_t ginfo = dottedcircle; + ginfo.cluster = buffer->cur().cluster; + ginfo.mask = buffer->cur().mask; + ginfo.syllable() = buffer->cur().syllable(); + /* TODO Set glyph_props? */ + + /* Insert dottedcircle after possible Repha. */ + while (buffer->idx < buffer->len && !buffer->in_error && + last_syllable == buffer->cur().syllable() && + buffer->cur().indic_category() == OT_Repha) + buffer->next_glyph (); + + buffer->output_info (ginfo); + } + else + buffer->next_glyph (); + } + + buffer->swap_buffers (); +} + +static void +initial_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + update_consonant_positions (plan, font, buffer); + insert_dotted_circles (plan, font, buffer); + + foreach_syllable (buffer, start, end) + initial_reordering_syllable (plan, font->face, buffer, start, end); +} + +static void +final_reordering_syllable (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + + /* This function relies heavily on halant glyphs. Lots of ligation + * and possibly multiplication substitutions happened prior to this + * phase, and that might have messed up our properties. Recover + * from a particular case of that where we're fairly sure that a + * class of OT_H is desired but has been lost. */ + if (indic_plan->virama_glyph) + { + unsigned int virama_glyph = indic_plan->virama_glyph; + for (unsigned int i = start; i < end; i++) + if (info[i].codepoint == virama_glyph && + _hb_glyph_info_ligated (&info[i]) && + _hb_glyph_info_multiplied (&info[i])) + { + /* This will make sure that this glyph passes is_halant_or_coeng() test. */ + info[i].indic_category() = OT_H; + _hb_glyph_info_clear_ligated_and_multiplied (&info[i]); + } + } + + + /* 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. + */ + + bool try_pref = !!indic_plan->mask_array[PREF]; + + /* Find base again */ + unsigned int base; + for (base = start; base < end; base++) + if (info[base].indic_position() >= POS_BASE_C) + { + if (try_pref && base + 1 < end) + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + { + if (!(_hb_glyph_info_substituted (&info[i]) && + _hb_glyph_info_ligated_and_didnt_multiply (&info[i]))) + { + /* Ok, this was a 'pref' candidate but didn't form any. + * Base is around here... */ + base = i; + while (base < end && is_halant_or_coeng (info[base])) + base++; + info[base].indic_position() = POS_BASE_C; + + try_pref = false; + } + break; + } + } + /* For Malayalam, skip over unformed below- (but NOT post-) forms. */ + if (buffer->props.script == HB_SCRIPT_MALAYALAM) + { + for (unsigned int i = base + 1; i < end; i++) + { + while (i < end && is_joiner (info[i])) + i++; + if (i == end || !is_halant_or_coeng (info[i])) + break; + i++; /* Skip halant. */ + while (i < end && is_joiner (info[i])) + i++; + if (i < end && is_consonant (info[i]) && info[i].indic_position() == POS_BELOW_C) + { + base = i; + info[base].indic_position() = POS_BASE_C; + } + } + } + + if (start < base && info[base].indic_position() > POS_BASE_C) + base--; + break; + } + if (base == end && start < base && + is_one_of (info[base - 1], FLAG (OT_ZWJ))) + base--; + if (base < end) + while (start < base && + is_one_of (info[base], (FLAG (OT_N) | HALANT_OR_COENG_FLAGS))) + 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 + * whether half-forms had been formed. Actual position for the matra is + * 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. + */ + + if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */ + { + /* If we lost track of base, alas, position before last thingy. */ + unsigned int new_pos = base == end ? base - 2 : base - 1; + + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + while (new_pos > start && + !(is_one_of (info[new_pos], (FLAG (OT_M) | HALANT_OR_COENG_FLAGS)))) + new_pos--; + + /* If we found no Halant we are done. + * Otherwise only proceed if the Halant does + * not belong to the Matra itself! */ + if (is_halant_or_coeng (info[new_pos]) && + info[new_pos].indic_position() != POS_PRE_M) + { + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos + 1 < end && is_joiner (info[new_pos + 1])) + new_pos++; + } + else + new_pos = start; /* No move. */ + } + + if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) + { + /* Now go see if there's actually any matras... */ + for (unsigned int i = new_pos; i > start; i--) + if (info[i - 1].indic_position () == POS_PRE_M) + { + unsigned int old_pos = i - 1; + if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ + base--; + + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + /* Note: this merge_clusters() is intentionally *after* the reordering. + * Indic matra reordering is special and tricky... */ + buffer->merge_clusters (new_pos, MIN (end, base + 1)); + + new_pos--; + } + } else { + for (unsigned int i = start; i < base; i++) + if (info[i].indic_position () == POS_PRE_M) { + buffer->merge_clusters (i, MIN (end, base + 1)); + break; + } + } + } + + + /* 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. + */ + + /* Two cases: + * + * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then + * we should only move it if the sequence ligated to the repha form. + * + * - If repha is encoded separately and in the logical position, we should only + * move it if it did NOT ligate. If it ligated, it's probably the font trying + * to make it work without the reordering. + */ + if (start + 1 < end && + info[start].indic_position() == POS_RA_TO_BECOME_REPH && + ((info[start].indic_category() == OT_Repha) ^ + _hb_glyph_info_ligated_and_didnt_multiply (&info[start]))) + { + unsigned int new_reph_pos; + reph_position_t reph_pos = indic_plan->config->reph_pos; + + assert (reph_pos != REPH_POS_DONT_CARE); + + /* 1. If reph should be positioned after post-base consonant forms, + * proceed to step 5. + */ + if (reph_pos == REPH_POS_AFTER_POST) + { + 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 && !is_halant_or_coeng (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) + { + /* ->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_POS_AFTER_MAIN) + { + new_reph_pos = base; + while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 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_POS_AFTER_SUB) + { + new_reph_pos = base; + while (new_reph_pos < end && + !( FLAG_SAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | 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: + { + /* Copied from step 2. */ + new_reph_pos = start + 1; + while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) + { + /* ->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; + } + } + + /* 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 (!hb_options ().uniscribe_bug_compatible && + unlikely (is_halant_or_coeng (info[new_reph_pos]))) { + 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 */ + buffer->merge_clusters (start, new_reph_pos + 1); + 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; + + if (start < base && base <= new_reph_pos) + base--; + } + } + + + /* o Reorder pre-base reordering consonants: + * + * If a pre-base reordering consonant is found, reorder it according to + * the following rules: + */ + + if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base reordering Ra. */ + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + { + /* 1. Only reorder a glyph produced by substitution during application + * of the <pref> feature. (Note that a font may shape a Ra consonant with + * the feature generally but block it in certain contexts.) + */ + /* Note: We just check that something got substituted. We don't check that + * the <pref> feature actually did it... + * + * Reorder pref only if it ligated. */ + if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i])) + { + /* + * 2. Try to find a target position the same way as for pre-base matra. + * If it is found, reorder pre-base consonant glyph. + * + * 3. If position is not found, reorder immediately before main + * consonant. + */ + + unsigned int new_pos = base; + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + while (new_pos > start && + !(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS))) + new_pos--; + + /* In Khmer coeng model, a H,Ra can go *after* matras. If it goes after a + * split matra, it should be reordered to *before* the left part of such matra. */ + if (new_pos > start && info[new_pos - 1].indic_category() == OT_M) + { + unsigned int old_pos = i; + for (unsigned int j = base + 1; j < old_pos; j++) + if (info[j].indic_category() == OT_M) + { + new_pos--; + break; + } + } + } + + if (new_pos > start && is_halant_or_coeng (info[new_pos - 1])) + { + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos < end && is_joiner (info[new_pos])) + new_pos++; + } + + { + unsigned int old_pos = i; + + buffer->merge_clusters (new_pos, old_pos + 1); + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + if (new_pos <= base && base < old_pos) + base++; + } + } + + break; + } + } + + + /* Apply 'init' to the Left Matra if it's a word start. */ + if (info[start].indic_position () == POS_PRE_M && + (!start || + !(FLAG_SAFE (_hb_glyph_info_get_general_category (&info[start - 1])) & + FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))) + info[start].mask |= indic_plan->mask_array[INIT]; + + + /* + * Finish off the clusters and go home! + */ + if (hb_options ().uniscribe_bug_compatible) + { + switch ((hb_tag_t) plan->props.script) + { + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_SINHALA: + break; + + default: + /* Uniscribe merges the entire cluster... Except for Tamil & Sinhala. + * 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. */ + buffer->merge_clusters (start, end); + break; + } + } +} + + +static void +final_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + if (unlikely (!count)) return; + + foreach_syllable (buffer, start, end) + final_reordering_syllable (plan, buffer, start, end); + + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); +} + + +static void +clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + info[i].syllable() = 0; +} + + +static bool +decompose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* Don't decompose these. */ + case 0x0931u : return false; /* DEVANAGARI LETTER RRA */ + case 0x0B94u : return false; /* TAMIL LETTER AU */ + + + /* + * Decompose split matras that don't have Unicode decompositions. + */ + + /* Khmer */ + case 0x17BEu : *a = 0x17C1u; *b= 0x17BEu; return true; + case 0x17BFu : *a = 0x17C1u; *b= 0x17BFu; return true; + case 0x17C0u : *a = 0x17C1u; *b= 0x17C0u; return true; + case 0x17C4u : *a = 0x17C1u; *b= 0x17C4u; return true; + case 0x17C5u : *a = 0x17C1u; *b= 0x17C5u; return true; + +#if 0 + /* Gujarati */ + /* This one has no decomposition in Unicode, but needs no decomposition either. */ + /* case 0x0AC9u : return false; */ + + /* Oriya */ + case 0x0B57u : *a = no decomp, -> RIGHT; return true; +#endif + } + + if ((ab == 0x0DDAu || hb_in_range (ab, 0x0DDCu, 0x0DDEu))) + { + /* + * Sinhala split matras... Let the fun begin. + * + * These four characters have Unicode decompositions. However, Uniscribe + * decomposes them "Khmer-style", that is, it uses the character itself to + * get the second half. The first half of all four decompositions is always + * U+0DD9. + * + * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are + * broken with Uniscribe. But we need to support them. As such, we only + * do the Uniscribe-style decomposition if the character is transformed into + * its "sec.half" form by the 'pstf' feature. Otherwise, we fall back to + * Unicode decomposition. + * + * Note that we can't unconditionally use Unicode decomposition. That would + * break some other fonts, that are designed to work with Uniscribe, and + * don't have positioning features for the Unicode-style decomposition. + * + * Argh... + * + * The Uniscribe behavior is now documented in the newly published Sinhala + * spec in 2012: + * + * http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shaping + */ + + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data; + + hb_codepoint_t glyph; + + if (hb_options ().uniscribe_bug_compatible || + (c->font->get_nominal_glyph (ab, &glyph) && + indic_plan->pstf.would_substitute (&glyph, 1, c->font->face))) + { + /* Ok, safe to use Uniscribe-style decomposition. */ + *a = 0x0DD9u; + *b = ab; + return true; + } + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + /* Composition-exclusion exceptions that we want to recompose. */ + if (a == 0x09AFu && b == 0x09BCu) { *ab = 0x09DFu; return true; } + + return (bool) c->unicode->compose (a, b, ab); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic = +{ + "indic", + collect_features_indic, + override_features_indic, + data_create_indic, + data_destroy_indic, + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + decompose_indic, + compose_indic, + setup_masks_indic, + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh new file mode 100644 index 000000000..29fdf9a1a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh @@ -0,0 +1,400 @@ + +#line 1 "hb-ot-shape-complex-myanmar-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_MYANMAR_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH + +#include "hb-private.hh" + + +#line 36 "hb-ot-shape-complex-myanmar-machine.hh" +static const unsigned char _myanmar_syllable_machine_trans_keys[] = { + 1u, 31u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, + 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, 5u, 8u, + 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 3u, 30u, 3u, 29u, 1u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 8u, 8u, 0 +}; + +static const char _myanmar_syllable_machine_key_spans[] = { + 31, 28, 25, 4, 25, 23, 21, 21, + 27, 27, 27, 27, 16, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 25, 4, + 25, 23, 21, 21, 27, 27, 27, 27, + 28, 27, 30, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 1 +}; + +static const short _myanmar_syllable_machine_index_offsets[] = { + 0, 32, 61, 87, 92, 118, 142, 164, + 186, 214, 242, 270, 298, 315, 343, 371, + 399, 427, 455, 483, 511, 539, 567, 593, + 598, 624, 648, 670, 692, 720, 748, 776, + 804, 833, 861, 892, 920, 948, 976, 1004, + 1032, 1060, 1088, 1116, 1144 +}; + +static const char _myanmar_syllable_machine_indicies[] = { + 1, 1, 2, 3, 4, 4, 0, 5, + 0, 6, 1, 0, 0, 0, 0, 7, + 0, 8, 1, 0, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 0, + 21, 22, 23, 23, 20, 24, 20, 25, + 20, 20, 20, 20, 20, 20, 20, 26, + 20, 20, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 20, 23, 23, 20, + 24, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 37, 20, 20, 20, 20, 20, + 20, 31, 20, 20, 20, 35, 20, 23, + 23, 20, 24, 20, 23, 23, 20, 24, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 31, 20, 20, 20, 35, 20, 38, 20, + 23, 23, 20, 24, 20, 31, 20, 20, + 20, 20, 20, 20, 20, 39, 20, 20, + 20, 20, 20, 20, 31, 20, 23, 23, + 20, 24, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 39, 20, 20, 20, 20, + 20, 20, 31, 20, 23, 23, 20, 24, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 31, 20, 21, 20, 23, 23, 20, 24, + 20, 25, 20, 20, 20, 20, 20, 20, + 20, 40, 20, 20, 40, 20, 20, 20, + 31, 41, 20, 20, 35, 20, 21, 20, + 23, 23, 20, 24, 20, 25, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 31, 20, 20, 20, + 35, 20, 21, 20, 23, 23, 20, 24, + 20, 25, 20, 20, 20, 20, 20, 20, + 20, 40, 20, 20, 20, 20, 20, 20, + 31, 41, 20, 20, 35, 20, 21, 20, + 23, 23, 20, 24, 20, 25, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 31, 41, 20, 20, + 35, 20, 1, 1, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 1, 20, 21, 20, 23, 23, 20, + 24, 20, 25, 20, 20, 20, 20, 20, + 20, 20, 26, 20, 20, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 20, 21, + 20, 23, 23, 20, 24, 20, 25, 20, + 20, 20, 20, 20, 20, 20, 34, 20, + 20, 20, 20, 20, 20, 31, 32, 33, + 34, 35, 20, 21, 20, 23, 23, 20, + 24, 20, 25, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 31, 32, 33, 34, 35, 20, 21, + 20, 23, 23, 20, 24, 20, 25, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 31, 32, 33, + 20, 35, 20, 21, 20, 23, 23, 20, + 24, 20, 25, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, + 20, 31, 20, 33, 20, 35, 20, 21, + 20, 23, 23, 20, 24, 20, 25, 20, + 20, 20, 20, 20, 20, 20, 34, 20, + 20, 27, 20, 29, 20, 31, 32, 33, + 34, 35, 20, 21, 20, 23, 23, 20, + 24, 20, 25, 20, 20, 20, 20, 20, + 20, 20, 34, 20, 20, 27, 20, 20, + 20, 31, 32, 33, 34, 35, 20, 21, + 20, 23, 23, 20, 24, 20, 25, 20, + 20, 20, 20, 20, 20, 20, 34, 20, + 20, 27, 28, 29, 20, 31, 32, 33, + 34, 35, 20, 21, 22, 23, 23, 20, + 24, 20, 25, 20, 20, 20, 20, 20, + 20, 20, 26, 20, 20, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 20, 3, + 3, 42, 5, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 43, 42, 42, 42, + 42, 42, 42, 13, 42, 42, 42, 17, + 42, 3, 3, 42, 5, 42, 3, 3, + 42, 5, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 13, 42, 42, 42, 17, 42, + 44, 42, 3, 3, 42, 5, 42, 13, + 42, 42, 42, 42, 42, 42, 42, 45, + 42, 42, 42, 42, 42, 42, 13, 42, + 3, 3, 42, 5, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 45, 42, 42, + 42, 42, 42, 42, 13, 42, 3, 3, + 42, 5, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 13, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 46, 42, 42, 46, 42, + 42, 42, 13, 47, 42, 42, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 13, 42, + 42, 42, 17, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 46, 42, 42, 42, 42, + 42, 42, 13, 47, 42, 42, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 13, 47, + 42, 42, 17, 42, 21, 22, 23, 23, + 20, 24, 20, 25, 20, 20, 20, 20, + 20, 20, 20, 48, 20, 20, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 20, 21, 49, 23, 23, 20, 24, 20, + 25, 20, 20, 20, 20, 20, 20, 20, + 26, 20, 20, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 20, 1, 1, 2, + 3, 3, 3, 42, 5, 42, 6, 1, + 42, 42, 42, 42, 1, 42, 8, 1, + 42, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 8, 42, 42, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 16, + 42, 42, 42, 42, 42, 42, 13, 14, + 15, 16, 17, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 13, 14, 15, 16, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 13, 14, + 15, 42, 17, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 13, 42, 15, 42, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 16, + 42, 42, 9, 42, 11, 42, 13, 14, + 15, 16, 17, 42, 2, 42, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 16, 42, 42, 9, 42, + 42, 42, 13, 14, 15, 16, 17, 42, + 2, 42, 3, 3, 42, 5, 42, 6, + 42, 42, 42, 42, 42, 42, 42, 16, + 42, 42, 9, 10, 11, 42, 13, 14, + 15, 16, 17, 42, 2, 3, 3, 3, + 42, 5, 42, 6, 42, 42, 42, 42, + 42, 42, 42, 8, 42, 42, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 42, + 51, 50, 0 +}; + +static const char _myanmar_syllable_machine_trans_targs[] = { + 0, 1, 22, 0, 0, 23, 29, 32, + 35, 36, 40, 41, 42, 25, 38, 39, + 37, 28, 43, 44, 0, 2, 12, 0, + 3, 9, 13, 14, 18, 19, 20, 5, + 16, 17, 15, 8, 21, 4, 6, 7, + 10, 11, 0, 24, 26, 27, 30, 31, + 33, 34, 0, 0 +}; + +static const char _myanmar_syllable_machine_trans_actions[] = { + 3, 0, 0, 4, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 10 +}; + +static const char _myanmar_syllable_machine_to_state_actions[] = { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; + +static const char _myanmar_syllable_machine_from_state_actions[] = { + 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; + +static const short _myanmar_syllable_machine_eof_trans[] = { + 0, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 21, 21, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 51 +}; + +static const int myanmar_syllable_machine_start = 0; +static const int myanmar_syllable_machine_first_final = 0; +static const int myanmar_syllable_machine_error = -1; + +static const int myanmar_syllable_machine_en_main = 0; + + +#line 36 "hb-ot-shape-complex-myanmar-machine.rl" + + + +#line 93 "hb-ot-shape-complex-myanmar-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 289 "hb-ot-shape-complex-myanmar-machine.hh" + { + cs = myanmar_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 114 "hb-ot-shape-complex-myanmar-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + +#line 306 "hb-ot-shape-complex-myanmar-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _myanmar_syllable_machine_from_state_actions[cs] ) { + case 2: +#line 1 "NONE" + {ts = p;} + break; +#line 320 "hb-ot-shape-complex-myanmar-machine.hh" + } + + _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); + _inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs]; + + _slen = _myanmar_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) && + ( info[p].myanmar_category()) <= _keys[1] ? + ( info[p].myanmar_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _myanmar_syllable_machine_trans_targs[_trans]; + + if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { + case 7: +#line 85 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p+1;{ found_syllable (consonant_syllable); }} + break; + case 5: +#line 86 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p+1;{ found_syllable (non_myanmar_cluster); }} + break; + case 10: +#line 87 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p+1;{ found_syllable (punctuation_cluster); }} + break; + case 4: +#line 88 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p+1;{ found_syllable (broken_cluster); }} + break; + case 3: +#line 89 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p+1;{ found_syllable (non_myanmar_cluster); }} + break; + case 6: +#line 85 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p;p--;{ found_syllable (consonant_syllable); }} + break; + case 8: +#line 88 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p;p--;{ found_syllable (broken_cluster); }} + break; + case 9: +#line 89 "hb-ot-shape-complex-myanmar-machine.rl" + {te = p;p--;{ found_syllable (non_myanmar_cluster); }} + break; +#line 370 "hb-ot-shape-complex-myanmar-machine.hh" + } + +_again: + switch ( _myanmar_syllable_machine_to_state_actions[cs] ) { + case 1: +#line 1 "NONE" + {ts = 0;} + break; +#line 379 "hb-ot-shape-complex-myanmar-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _myanmar_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 123 "hb-ot-shape-complex-myanmar-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl new file mode 100644 index 000000000..9649a916f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl @@ -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_SHAPE_COMPLEX_MYANMAR_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH + +#include "hb-private.hh" + +%%{ + machine myanmar_syllable_machine; + alphtype unsigned char; + write data; +}%% + +%%{ + +# Same order as enum myanmar_category_t. Not sure how to avoid duplication. +A = 10; +As = 18; +C = 1; +D = 19; +D0 = 20; +DB = 3; +GB = 11; +H = 4; +IV = 2; +MH = 21; +MR = 22; +MW = 23; +MY = 24; +PT = 25; +V = 8; +VAbv = 26; +VBlw = 27; +VPre = 28; +VPst = 29; +VS = 30; +ZWJ = 6; +ZWNJ = 5; +Ra = 16; +P = 31; + +j = ZWJ|ZWNJ; # Joiners +k = (Ra As H); # Kinzi + +c = C|Ra; # is_consonant + +medial_group = MY? MR? MW? MH? As?; +main_vowel_group = VPre* VAbv* VBlw* A* (DB As?)?; +post_vowel_group = VPst MH? As* VAbv* A* (DB As?)?; +pwo_tone_group = PT A* DB? As?; + +complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* V* j?; +syllable_tail = (H | complex_syllable_tail); + +consonant_syllable = k? (c|IV|D|GB).VS? (H (c|IV).VS?)* syllable_tail; +punctuation_cluster = P V; +broken_cluster = k? VS? syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (consonant_syllable); }; + j => { found_syllable (non_myanmar_cluster); }; + punctuation_cluster => { found_syllable (punctuation_cluster); }; + broken_cluster => { found_syllable (broken_cluster); }; + other => { found_syllable (non_myanmar_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].myanmar_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc new file mode 100644 index 000000000..bb68622e2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc @@ -0,0 +1,545 @@ +/* + * Copyright © 2011,2012,2013 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" + +/* buffer var allocations */ +#define myanmar_category() complex_var_u8_0() /* myanmar_category_t */ +#define myanmar_position() complex_var_u8_1() /* myanmar_position_t */ + + +/* + * Myanmar shaper. + */ + +static const hb_tag_t +basic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after initial_reordering. + */ + HB_TAG('r','p','h','f'), + HB_TAG('p','r','e','f'), + HB_TAG('b','l','w','f'), + HB_TAG('p','s','t','f'), +}; +static const hb_tag_t +other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after final_reordering. + */ + HB_TAG('p','r','e','s'), + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('p','s','t','s'), + /* Positioning features, though we don't care about the types. */ + HB_TAG('d','i','s','t'), + /* Pre-release version of Windows 8 Myanmar font had abvm,blwm + * features. The released Windows 8 version of the font (as well + * as the released spec) used 'mark' instead. The Windows 8 + * shaper however didn't apply 'mark' but did apply 'mkmk'. + * Perhaps it applied abvm/blwm. This was fixed in a Windows 8 + * update, so now it applies mark/mkmk. We are guessing that + * it still applies abvm/blwm too. + */ + HB_TAG('a','b','v','m'), + HB_TAG('b','l','w','m'), +}; + +static void +setup_syllables (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +initial_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +final_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_myanmar (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables); + + map->add_global_bool_feature (HB_TAG('l','o','c','l')); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->add_global_bool_feature (HB_TAG('c','c','m','p')); + + + map->add_gsub_pause (initial_reordering); + for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) + { + map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); + map->add_gsub_pause (NULL); + } + map->add_gsub_pause (final_reordering); + for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) + map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); +} + +static void +override_features_myanmar (hb_ot_shape_planner_t *plan) +{ + plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL); +} + + +enum syllable_type_t { + consonant_syllable, + punctuation_cluster, + broken_cluster, + non_myanmar_cluster, +}; + +#include "hb-ot-shape-complex-myanmar-machine.hh" + + +/* Note: This enum is duplicated in the -machine.rl source file. + * Not sure how to avoid duplication. */ +enum myanmar_category_t { + OT_As = 18, /* Asat */ + OT_D = 19, /* Digits except zero */ + OT_D0 = 20, /* Digit zero */ + OT_DB = OT_N, /* Dot below */ + OT_GB = OT_PLACEHOLDER, + OT_MH = 21, /* Various consonant medial types */ + OT_MR = 22, /* Various consonant medial types */ + OT_MW = 23, /* Various consonant medial types */ + OT_MY = 24, /* Various consonant medial types */ + OT_PT = 25, /* Pwo and other tones */ + OT_VAbv = 26, + OT_VBlw = 27, + OT_VPre = 28, + OT_VPst = 29, + OT_VS = 30, /* Variation selectors */ + OT_P = 31 /* Punctuation */ +}; + + +static inline bool +is_one_of (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_SAFE (info.myanmar_category()) & flags); +} + +static inline bool +is_consonant (const hb_glyph_info_t &info) +{ + return is_one_of (info, CONSONANT_FLAGS); +} + + +static inline void +set_myanmar_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + indic_category_t cat = (indic_category_t) (type & 0x7Fu); + indic_position_t pos = (indic_position_t) (type >> 8); + + /* Myanmar + * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze + */ + if (unlikely (hb_in_range (u, 0xFE00u, 0xFE0Fu))) + cat = (indic_category_t) OT_VS; + + switch (u) + { + case 0x104Eu: + cat = (indic_category_t) OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */ + break; + + case 0x002Du: case 0x00A0u: case 0x00D7u: case 0x2012u: + case 0x2013u: case 0x2014u: case 0x2015u: case 0x2022u: + case 0x25CCu: case 0x25FBu: case 0x25FCu: case 0x25FDu: + case 0x25FEu: + cat = (indic_category_t) OT_GB; + break; + + case 0x1004u: case 0x101Bu: case 0x105Au: + cat = (indic_category_t) OT_Ra; + break; + + case 0x1032u: case 0x1036u: + cat = (indic_category_t) OT_A; + break; + + case 0x1039u: + cat = (indic_category_t) OT_H; + break; + + case 0x103Au: + cat = (indic_category_t) OT_As; + break; + + case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u: + case 0x1045u: case 0x1046u: case 0x1047u: case 0x1048u: + case 0x1049u: case 0x1090u: case 0x1091u: case 0x1092u: + case 0x1093u: case 0x1094u: case 0x1095u: case 0x1096u: + case 0x1097u: case 0x1098u: case 0x1099u: + cat = (indic_category_t) OT_D; + break; + + case 0x1040u: + cat = (indic_category_t) OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */ + break; + + case 0x103Eu: case 0x1060u: + cat = (indic_category_t) OT_MH; + break; + + case 0x103Cu: + cat = (indic_category_t) OT_MR; + break; + + case 0x103Du: case 0x1082u: + cat = (indic_category_t) OT_MW; + break; + + case 0x103Bu: case 0x105Eu: case 0x105Fu: + cat = (indic_category_t) OT_MY; + break; + + case 0x1063u: case 0x1064u: case 0x1069u: case 0x106Au: + case 0x106Bu: case 0x106Cu: case 0x106Du: case 0xAA7Bu: + cat = (indic_category_t) OT_PT; + break; + + case 0x1038u: case 0x1087u: case 0x1088u: case 0x1089u: + case 0x108Au: case 0x108Bu: case 0x108Cu: case 0x108Du: + case 0x108Fu: case 0x109Au: case 0x109Bu: case 0x109Cu: + cat = (indic_category_t) OT_SM; + break; + + case 0x104Au: case 0x104Bu: + cat = (indic_category_t) OT_P; + break; + + case 0xAA74u: case 0xAA75u: case 0xAA76u: + /* https://github.com/roozbehp/unicode-data/issues/3 */ + cat = (indic_category_t) OT_C; + break; + } + + if (cat == OT_M) + { + switch ((int) pos) + { + case POS_PRE_C: cat = (indic_category_t) OT_VPre; + pos = POS_PRE_M; break; + case POS_ABOVE_C: cat = (indic_category_t) OT_VAbv; break; + case POS_BELOW_C: cat = (indic_category_t) OT_VBlw; break; + case POS_POST_C: cat = (indic_category_t) OT_VPst; break; + } + } + + info.myanmar_category() = (myanmar_category_t) cat; + info.myanmar_position() = pos; +} + + + +static void +setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_myanmar_properties (info[i]); +} + +static void +setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + find_syllables (buffer); +} + +static int +compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->myanmar_position(); + int b = pb->myanmar_position(); + + return a < b ? -1 : a == b ? 0 : +1; +} + + +/* Rules from: + * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm */ + +static void +initial_reordering_consonant_syllable (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + unsigned int base = end; + bool has_reph = false; + + { + unsigned int limit = start; + if (start + 3 <= end && + info[start ].myanmar_category() == OT_Ra && + info[start+1].myanmar_category() == OT_As && + info[start+2].myanmar_category() == OT_H) + { + limit += 3; + base = start; + has_reph = true; + } + + { + if (!has_reph) + base = limit; + + for (unsigned int i = limit; i < end; i++) + if (is_consonant (info[i])) + { + base = i; + break; + } + } + } + + /* Reorder! */ + { + unsigned int i = start; + for (; i < start + (has_reph ? 3 : 0); i++) + info[i].myanmar_position() = POS_AFTER_MAIN; + for (; i < base; i++) + info[i].myanmar_position() = POS_PRE_C; + if (i < end) + { + info[i].myanmar_position() = POS_BASE_C; + i++; + } + indic_position_t pos = POS_AFTER_MAIN; + /* The following loop may be ugly, but it implements all of + * Myanmar reordering! */ + for (; i < end; i++) + { + if (info[i].myanmar_category() == OT_MR) /* Pre-base reordering */ + { + info[i].myanmar_position() = POS_PRE_C; + continue; + } + if (info[i].myanmar_position() < POS_BASE_C) /* Left matra */ + { + continue; + } + + if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == OT_VBlw) + { + pos = POS_BELOW_C; + info[i].myanmar_position() = pos; + continue; + } + + if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_A) + { + info[i].myanmar_position() = POS_BEFORE_SUB; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_VBlw) + { + info[i].myanmar_position() = pos; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() != OT_A) + { + pos = POS_AFTER_SUB; + info[i].myanmar_position() = pos; + continue; + } + info[i].myanmar_position() = pos; + } + } + + /* Sit tight, rock 'n roll! */ + buffer->sort (start, end, compare_myanmar_order); +} + +static void +initial_reordering_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) { + + case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case consonant_syllable: + initial_reordering_consonant_syllable (buffer, start, end); + break; + + case punctuation_cluster: + case non_myanmar_cluster: + break; + } +} + +static inline void +insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) +{ + /* Note: This loop is extra overhead, but should not be measurable. */ + bool has_broken_syllables = false; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if ((info[i].syllable() & 0x0F) == broken_cluster) + { + has_broken_syllables = true; + break; + } + if (likely (!has_broken_syllables)) + return; + + + hb_codepoint_t dottedcircle_glyph; + if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + set_myanmar_properties (dottedcircle); + dottedcircle.codepoint = dottedcircle_glyph; + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len && !buffer->in_error) + { + unsigned int syllable = buffer->cur().syllable(); + syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + { + last_syllable = syllable; + + hb_glyph_info_t ginfo = dottedcircle; + ginfo.cluster = buffer->cur().cluster; + ginfo.mask = buffer->cur().mask; + ginfo.syllable() = buffer->cur().syllable(); + + buffer->output_info (ginfo); + } + else + buffer->next_glyph (); + } + + buffer->swap_buffers (); +} + +static void +initial_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + insert_dotted_circles (plan, font, buffer); + + foreach_syllable (buffer, start, end) + initial_reordering_syllable (plan, font->face, buffer, start, end); +} + +static void +final_reordering (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + + /* Zero syllables now... */ + for (unsigned int i = 0; i < count; i++) + info[i].syllable() = 0; + + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); +} + + +/* Uniscribe seems to have a shaper for 'mymr' that is like the + * generic shaper, except that it zeros mark advances GDEF_LATE. */ +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old = +{ + "default", + NULL, /* collect_features */ + NULL, /* override_features */ + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar = +{ + "myanmar", + collect_features_myanmar, + override_features_myanmar, + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_myanmar, + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh new file mode 100644 index 000000000..39572dfe0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh @@ -0,0 +1,374 @@ +/* + * Copyright © 2010,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_PRIVATE_HH +#define HB_OT_SHAPE_COMPLEX_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-ot-shape-private.hh" +#include "hb-ot-shape-normalize-private.hh" + + + +/* buffer var allocations, used by complex shapers */ +#define complex_var_u8_0() var2.u8[2] +#define complex_var_u8_1() var2.u8[3] + + +enum hb_ot_shape_zero_width_marks_type_t { + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE +}; + + +/* Master OT shaper list */ +#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 (hebrew) \ + HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \ + HB_COMPLEX_SHAPER_IMPLEMENT (indic) \ + HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \ + HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ + HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \ + HB_COMPLEX_SHAPER_IMPLEMENT (use) \ + /* ^--- Add new shapers here */ + + +struct hb_ot_complex_shaper_t +{ + char name[8]; + + /* collect_features() + * Called during shape_plan(). + * Shapers should use plan->map to add their features and callbacks. + * May be NULL. + */ + void (*collect_features) (hb_ot_shape_planner_t *plan); + + /* override_features() + * Called during shape_plan(). + * Shapers should use plan->map to override features and add callbacks after + * common features are added. + * May be NULL. + */ + void (*override_features) (hb_ot_shape_planner_t *plan); + + + /* data_create() + * Called at the end of shape_plan(). + * Whatever shapers return will be accessible through plan->data later. + * If NULL is returned, means a plan failure. + */ + void *(*data_create) (const hb_ot_shape_plan_t *plan); + + /* data_destroy() + * Called when the shape_plan is being destroyed. + * plan->data is passed here for destruction. + * If NULL is returned, means a plan failure. + * May be NULL. + */ + void (*data_destroy) (void *data); + + + /* preprocess_text() + * Called during shape(). + * Shapers can use to modify text before shaping starts. + * May be NULL. + */ + void (*preprocess_text) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* postprocess_glyphs() + * Called during shape(). + * Shapers can use to modify glyphs after shaping ends. + * May be NULL. + */ + void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + + hb_ot_shape_normalization_mode_t normalization_preference; + + /* decompose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + + /* compose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + + /* setup_masks() + * Called during shape(). + * Shapers should use map to get feature masks and set on buffer. + * Shapers may NOT modify characters. + * May be NULL. + */ + void (*setup_masks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* disable_otl() + * Called during shape(). + * If set and returns true, GDEF/GSUB/GPOS of the font are ignored + * and fallback operations used. + * May be NULL. + */ + bool (*disable_otl) (const hb_ot_shape_plan_t *plan); + + hb_ot_shape_zero_width_marks_type_t zero_width_marks; + + bool fallback_position; +}; + +#define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name; +HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS +#undef HB_COMPLEX_SHAPER_IMPLEMENT + + +static inline const hb_ot_complex_shaper_t * +hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner) +{ + switch ((hb_tag_t) planner->props.script) + { + default: + return &_hb_ot_complex_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_SYRIAC: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* For Arabic script, use the Arabic shaper even if no OT script tag was found. + * This is because we do fallback shaping for Arabic script (and not others). + * But note that Arabic shaping is applicable only to horizontal layout; for + * vertical text, just use the generic shaper instead. */ + if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT || + planner->props.script == HB_SCRIPT_ARABIC) && + HB_DIRECTION_IS_HORIZONTAL(planner->props.direction)) + return &_hb_ot_complex_shaper_arabic; + else + return &_hb_ot_complex_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_THAI: + case HB_SCRIPT_LAO: + + return &_hb_ot_complex_shaper_thai; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HANGUL: + + return &_hb_ot_complex_shaper_hangul; + + + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: + + return &_hb_ot_complex_shaper_tibetan; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HEBREW: + + return &_hb_ot_complex_shaper_hebrew; + + + /* ^--- Add new shapers here */ + +#if 0 + /* Unicode-4.1 additions */ + case HB_SCRIPT_NEW_TAI_LUE: +#endif + + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: + case HB_SCRIPT_DEVANAGARI: + case HB_SCRIPT_GUJARATI: + case HB_SCRIPT_GURMUKHI: + case HB_SCRIPT_KANNADA: + case HB_SCRIPT_MALAYALAM: + case HB_SCRIPT_ORIYA: + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_TELUGU: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SINHALA: + + /* If the designer designed the font for the 'DFLT' script, + * use the default shaper. Otherwise, use the specific shaper. + * Note that for some simple scripts, there may not be *any* + * GSUB/GPOS needed, so there may be no scripts found! */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T')) + return &_hb_ot_complex_shaper_default; + else + return &_hb_ot_complex_shaper_indic; + + case HB_SCRIPT_KHMER: + /* A number of Khmer fonts in the wild don't have a 'pref' feature, + * and as such won't shape properly via the Indic shaper; + * however, they typically have 'liga' / 'clig' features that implement + * the necessary "reordering" by means of ligature substitutions. + * So we send such pref-less fonts through the generic shaper instead. */ + if (planner->map.found_script[0] && + hb_ot_layout_language_find_feature (planner->face, HB_OT_TAG_GSUB, + planner->map.script_index[0], + planner->map.language_index[0], + HB_TAG ('p','r','e','f'), + NULL)) + return &_hb_ot_complex_shaper_indic; + else + return &_hb_ot_complex_shaper_default; + + case HB_SCRIPT_MYANMAR: + if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2')) + return &_hb_ot_complex_shaper_myanmar; + else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r')) + return &_hb_ot_complex_shaper_myanmar_old; + else + return &_hb_ot_complex_shaper_default; + + + /* Unicode-2.0 additions */ + //case HB_SCRIPT_TIBETAN: + + /* Unicode-3.0 additions */ + //case HB_SCRIPT_MONGOLIAN: + //case HB_SCRIPT_SINHALA: + + /* Unicode-3.2 additions */ + case HB_SCRIPT_BUHID: + case HB_SCRIPT_HANUNOO: + 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_BUGINESE: + case HB_SCRIPT_KHAROSHTHI: + case HB_SCRIPT_SYLOTI_NAGRI: + case HB_SCRIPT_TIFINAGH: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_BALINESE: + //case HB_SCRIPT_NKO: + //case HB_SCRIPT_PHAGS_PA: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_CHAM: + case HB_SCRIPT_KAYAH_LI: + case HB_SCRIPT_LEPCHA: + case HB_SCRIPT_REJANG: + case HB_SCRIPT_SAURASHTRA: + case HB_SCRIPT_SUNDANESE: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: + case HB_SCRIPT_JAVANESE: + case HB_SCRIPT_KAITHI: + case HB_SCRIPT_MEETEI_MAYEK: + case HB_SCRIPT_TAI_THAM: + case HB_SCRIPT_TAI_VIET: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_BATAK: + case HB_SCRIPT_BRAHMI: + //case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_CHAKMA: + case HB_SCRIPT_SHARADA: + case HB_SCRIPT_TAKRI: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_DUPLOYAN: + case HB_SCRIPT_GRANTHA: + case HB_SCRIPT_KHOJKI: + case HB_SCRIPT_KHUDAWADI: + case HB_SCRIPT_MAHAJANI: + //case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MODI: + case HB_SCRIPT_PAHAWH_HMONG: + //case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SIDDHAM: + case HB_SCRIPT_TIRHUTA: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_AHOM: + //case HB_SCRIPT_MULTANI: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_BHAIKSUKI: + case HB_SCRIPT_MARCHEN: + case HB_SCRIPT_NEWA: + + /* If the designer designed the font for the 'DFLT' script, + * use the default shaper. Otherwise, use the specific shaper. + * Note that for some simple scripts, there may not be *any* + * GSUB/GPOS needed, so there may be no scripts found! */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T')) + return &_hb_ot_complex_shaper_default; + else + return &_hb_ot_complex_shaper_use; + } +} + + +#endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc new file mode 100644 index 000000000..e6f80f59e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc @@ -0,0 +1,382 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-private.hh" + + +/* Thai / Lao shaper */ + + +/* PUA shaping */ + + +enum thai_consonant_type_t +{ + NC, + AC, + RC, + DC, + NOT_CONSONANT, + NUM_CONSONANT_TYPES = NOT_CONSONANT +}; + +static thai_consonant_type_t +get_consonant_type (hb_codepoint_t u) +{ + if (u == 0x0E1Bu || u == 0x0E1Du || u == 0x0E1Fu/* || u == 0x0E2Cu*/) + return AC; + if (u == 0x0E0Du || u == 0x0E10u) + return RC; + if (u == 0x0E0Eu || u == 0x0E0Fu) + return DC; + if (hb_in_range (u, 0x0E01u, 0x0E2Eu)) + return NC; + return NOT_CONSONANT; +} + + +enum thai_mark_type_t +{ + AV, + BV, + T, + NOT_MARK, + NUM_MARK_TYPES = NOT_MARK +}; + +static thai_mark_type_t +get_mark_type (hb_codepoint_t u) +{ + if (u == 0x0E31u || hb_in_range (u, 0x0E34u, 0x0E37u) || + u == 0x0E47u || hb_in_range (u, 0x0E4Du, 0x0E4Eu)) + return AV; + if (hb_in_range (u, 0x0E38u, 0x0E3Au)) + return BV; + if (hb_in_range (u, 0x0E48u, 0x0E4Cu)) + return T; + return NOT_MARK; +} + + +enum thai_action_t +{ + NOP, + SD, /* Shift combining-mark down */ + SL, /* Shift combining-mark left */ + SDL, /* Shift combining-mark down-left */ + RD /* Remove descender from base */ +}; + +static hb_codepoint_t +thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) +{ + struct thai_pua_mapping_t { + hb_codepoint_t u; + hb_codepoint_t win_pua; + hb_codepoint_t mac_pua; + } const *pua_mappings = NULL; + static const thai_pua_mapping_t SD_mappings[] = { + {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */ + {0x0E49u, 0xF70Bu, 0xF88Eu}, /* MAI THO */ + {0x0E4Au, 0xF70Cu, 0xF891u}, /* MAI TRI */ + {0x0E4Bu, 0xF70Du, 0xF894u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF70Eu, 0xF897u}, /* THANTHAKHAT */ + {0x0E38u, 0xF718u, 0xF89Bu}, /* SARA U */ + {0x0E39u, 0xF719u, 0xF89Cu}, /* SARA UU */ + {0x0E3Au, 0xF71Au, 0xF89Du}, /* PHINTHU */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SDL_mappings[] = { + {0x0E48u, 0xF705u, 0xF88Cu}, /* MAI EK */ + {0x0E49u, 0xF706u, 0xF88Fu}, /* MAI THO */ + {0x0E4Au, 0xF707u, 0xF892u}, /* MAI TRI */ + {0x0E4Bu, 0xF708u, 0xF895u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF709u, 0xF898u}, /* THANTHAKHAT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SL_mappings[] = { + {0x0E48u, 0xF713u, 0xF88Au}, /* MAI EK */ + {0x0E49u, 0xF714u, 0xF88Du}, /* MAI THO */ + {0x0E4Au, 0xF715u, 0xF890u}, /* MAI TRI */ + {0x0E4Bu, 0xF716u, 0xF893u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF717u, 0xF896u}, /* THANTHAKHAT */ + {0x0E31u, 0xF710u, 0xF884u}, /* MAI HAN-AKAT */ + {0x0E34u, 0xF701u, 0xF885u}, /* SARA I */ + {0x0E35u, 0xF702u, 0xF886u}, /* SARA II */ + {0x0E36u, 0xF703u, 0xF887u}, /* SARA UE */ + {0x0E37u, 0xF704u, 0xF888u}, /* SARA UEE */ + {0x0E47u, 0xF712u, 0xF889u}, /* MAITAIKHU */ + {0x0E4Du, 0xF711u, 0xF899u}, /* NIKHAHIT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t RD_mappings[] = { + {0x0E0Du, 0xF70Fu, 0xF89Au}, /* YO YING */ + {0x0E10u, 0xF700u, 0xF89Eu}, /* THO THAN */ + {0x0000u, 0x0000u, 0x0000u} + }; + + switch (action) { + case NOP: return u; + case SD: pua_mappings = SD_mappings; break; + case SDL: pua_mappings = SDL_mappings; break; + case SL: pua_mappings = SL_mappings; break; + case RD: pua_mappings = RD_mappings; break; + } + for (; pua_mappings->u; pua_mappings++) + if (pua_mappings->u == u) + { + hb_codepoint_t glyph; + if (hb_font_get_glyph (font, pua_mappings->win_pua, 0, &glyph)) + return pua_mappings->win_pua; + if (hb_font_get_glyph (font, pua_mappings->mac_pua, 0, &glyph)) + return pua_mappings->mac_pua; + break; + } + return u; +} + + +static enum thai_above_state_t +{ /* Cluster above looks like: */ + T0, /* ⣤ */ + T1, /* ⣼ */ + T2, /* ⣾ */ + T3, /* ⣿ */ + NUM_ABOVE_STATES +} thai_above_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + T0, /* NC */ + T1, /* AC */ + T0, /* RC */ + T0, /* DC */ + T3, /* NOT_CONSONANT */ +}; + +static const struct thai_above_state_machine_edge_t { + thai_action_t action; + thai_above_state_t next_state; +} thai_above_state_machine[NUM_ABOVE_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*T0*/ {{NOP,T3}, {NOP,T0}, {SD, T3}}, +/*T1*/ {{SL, T2}, {NOP,T1}, {SDL,T2}}, +/*T2*/ {{NOP,T3}, {NOP,T2}, {SL, T3}}, +/*T3*/ {{NOP,T3}, {NOP,T3}, {NOP,T3}}, +}; + + +static enum thai_below_state_t +{ + B0, /* No descender */ + B1, /* Removable descender */ + B2, /* Strict descender */ + NUM_BELOW_STATES +} thai_below_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + B0, /* NC */ + B0, /* AC */ + B1, /* RC */ + B2, /* DC */ + B2, /* NOT_CONSONANT */ +}; + +static const struct thai_below_state_machine_edge_t { + thai_action_t action; + thai_below_state_t next_state; +} thai_below_state_machine[NUM_BELOW_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*B0*/ {{NOP,B0}, {NOP,B2}, {NOP, B0}}, +/*B1*/ {{NOP,B1}, {RD, B2}, {NOP, B1}}, +/*B2*/ {{NOP,B2}, {SD, B2}, {NOP, B2}}, +}; + + +static void +do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ + thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT]; + thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT]; + unsigned int base = 0; + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + thai_mark_type_t mt = get_mark_type (info[i].codepoint); + + if (mt == NOT_MARK) { + thai_consonant_type_t ct = get_consonant_type (info[i].codepoint); + above_state = thai_above_start_state[ct]; + below_state = thai_below_start_state[ct]; + base = i; + continue; + } + + const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt]; + const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt]; + above_state = above_edge.next_state; + below_state = below_edge.next_state; + + /* At least one of the above/below actions is NOP. */ + thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action; + + if (action == RD) + info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font); + else + info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font); + } +} + + +static void +preprocess_text_thai (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + /* This function implements the shaping logic documented here: + * + * http://linux.thai.net/~thep/th-otf/shaping.html + * + * The first shaping rule listed there is needed even if the font has Thai + * OpenType tables. The rest do fallback positioning based on PUA codepoints. + * We implement that only if there exist no Thai GSUB in the font. + */ + + /* 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 AA, *and* move the + * NIKHAHIT 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 user wanted, but the rendering is nevertheless nikhahit above + * chattawa. + * + * Same for Lao. + * + * Note: + * + * Uniscribe also does some below-marks reordering. Namely, it positions U+0E3A + * after U+0E38 and U+0E39. We do that by modifying the ccc for U+0E3A. + * See unicode->modified_combining_class (). Lao does NOT have a U+0E3A + * equivalent. + */ + + + /* + * 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 + * + * Testing shows that Uniscribe reorder the following marks: + * Thai: <0E31,0E34..0E37,0E47..0E4E> + * Lao: <0EB1,0EB4..0EB7,0EC7..0ECE> + * + * 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) & ~0x0080u) == 0x0E33u) +#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du) +#define SARA_AA_FROM_SARA_AM(x) ((x) - 1) +#define IS_TONE_MARK(x) (hb_in_ranges ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u)) + + buffer->clear_output (); + unsigned int count = buffer->len; + for (buffer->idx = 0; buffer->idx < count && !buffer->in_error;) + { + 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; + + /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */ + unsigned int end = buffer->out_len; + _hb_glyph_info_set_general_category (&buffer->out_info[end - 2], HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK); + + /* Ok, let's see... */ + unsigned int start = end - 2; + while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint)) + start--; + + if (start + 2 < end) + { + /* Move Nikhahit (end-2) to the beginning */ + buffer->merge_out_clusters (start, end); + 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; + } + else + { + /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the + * previous cluster. */ + if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start - 1, end); + } + } + buffer->swap_buffers (); + + /* If font has Thai GSUB, we are done. */ + if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0]) + do_thai_pua_shaping (plan, buffer, font); +} + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai = +{ + "thai", + NULL, /* collect_features */ + NULL, /* override_features */ + NULL, /* data_create */ + NULL, /* data_destroy */ + preprocess_text_thai, + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + false,/* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc new file mode 100644 index 000000000..aadf59f5a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc @@ -0,0 +1,63 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-private.hh" + + +static const hb_tag_t tibetan_features[] = +{ + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('a','b','v','m'), + HB_TAG('b','l','w','m'), + HB_TAG_NONE +}; + +static void +collect_features_tibetan (hb_ot_shape_planner_t *plan) +{ + for (const hb_tag_t *script_features = tibetan_features; script_features && *script_features; script_features++) + plan->map.add_global_bool_feature (*script_features); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan = +{ + "default", + collect_features_tibetan, + NULL, /* override_features */ + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh new file mode 100644 index 000000000..44e5d0d56 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh @@ -0,0 +1,450 @@ + +#line 1 "hb-ot-shape-complex-use-machine.rl" +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH + +#include "hb-private.hh" + + +#line 38 "hb-ot-shape-complex-use-machine.hh" +static const unsigned char _use_syllable_machine_trans_keys[] = { + 1u, 1u, 0u, 39u, 21u, 21u, 8u, 39u, 8u, 39u, 1u, 1u, 8u, 39u, 8u, 39u, + 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, + 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 13u, 21u, + 4u, 4u, 13u, 13u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, + 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, + 8u, 39u, 8u, 39u, 8u, 39u, 1u, 1u, 1u, 39u, 8u, 39u, 21u, 42u, 41u, 42u, + 42u, 42u, 0 +}; + +static const char _use_syllable_machine_key_spans[] = { + 1, 40, 1, 32, 32, 1, 32, 32, + 32, 19, 19, 19, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 9, + 1, 1, 32, 32, 32, 32, 19, 19, + 19, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 1, 39, 32, 22, 2, + 1 +}; + +static const short _use_syllable_machine_index_offsets[] = { + 0, 2, 43, 45, 78, 111, 113, 146, + 179, 212, 232, 252, 272, 305, 338, 371, + 404, 437, 470, 503, 536, 569, 602, 635, + 645, 647, 649, 682, 715, 748, 781, 801, + 821, 841, 874, 907, 940, 973, 1006, 1039, + 1072, 1105, 1138, 1171, 1173, 1213, 1246, 1269, + 1272 +}; + +static const char _use_syllable_machine_indicies[] = { + 1, 0, 2, 3, 4, 2, 5, 3, + 4, 4, 6, 4, 4, 1, 7, 4, + 4, 4, 2, 2, 8, 9, 4, 4, + 10, 11, 12, 13, 14, 15, 16, 10, + 17, 18, 19, 20, 21, 22, 4, 23, + 24, 25, 4, 27, 26, 29, 28, 28, + 30, 31, 28, 28, 28, 28, 28, 28, + 28, 28, 32, 33, 34, 35, 36, 37, + 38, 39, 33, 40, 32, 41, 42, 43, + 44, 28, 45, 46, 47, 28, 29, 28, + 28, 30, 31, 28, 28, 28, 28, 28, + 28, 28, 28, 48, 33, 34, 35, 36, + 37, 38, 39, 33, 40, 41, 41, 42, + 43, 44, 28, 45, 46, 47, 28, 30, + 49, 29, 28, 28, 30, 31, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 33, + 34, 35, 36, 37, 38, 39, 33, 40, + 41, 41, 42, 43, 44, 28, 45, 46, + 47, 28, 29, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 33, 34, 35, 36, 37, 28, 28, 28, + 28, 28, 28, 42, 43, 44, 28, 45, + 46, 47, 28, 29, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 34, 35, 36, 37, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 45, 46, 47, 28, 29, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 35, 36, 37, 28, + 29, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 36, 37, 28, 29, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 37, 28, + 29, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 35, 36, 37, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 45, 46, 47, + 28, 29, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 35, 36, 37, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 46, + 47, 28, 29, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 35, 36, 37, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 47, 28, 29, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 34, 35, 36, 37, 28, 28, + 28, 28, 28, 28, 42, 43, 44, 28, + 45, 46, 47, 28, 29, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 34, 35, 36, 37, 28, + 28, 28, 28, 28, 28, 28, 43, 44, + 28, 45, 46, 47, 28, 29, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 34, 35, 36, 37, + 28, 28, 28, 28, 28, 28, 28, 28, + 44, 28, 45, 46, 47, 28, 29, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 33, 34, 35, 36, + 37, 28, 39, 33, 28, 28, 28, 42, + 43, 44, 28, 45, 46, 47, 28, 29, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 33, 34, 35, + 36, 37, 28, 28, 33, 28, 28, 28, + 42, 43, 44, 28, 45, 46, 47, 28, + 29, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 33, 34, + 35, 36, 37, 38, 39, 33, 28, 28, + 28, 42, 43, 44, 28, 45, 46, 47, + 28, 29, 28, 28, 30, 31, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 33, + 34, 35, 36, 37, 38, 39, 33, 40, + 28, 41, 42, 43, 44, 28, 45, 46, + 47, 28, 29, 28, 28, 30, 31, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 33, 34, 35, 36, 37, 38, 39, 33, + 40, 32, 41, 42, 43, 44, 28, 45, + 46, 47, 28, 51, 50, 50, 50, 50, + 50, 50, 50, 52, 50, 5, 53, 51, + 50, 6, 54, 54, 1, 55, 54, 54, + 54, 54, 54, 54, 54, 54, 56, 10, + 11, 12, 13, 14, 15, 16, 10, 17, + 19, 19, 20, 21, 22, 54, 23, 24, + 25, 54, 6, 54, 54, 1, 55, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 10, 11, 12, 13, 14, 15, 16, 10, + 17, 19, 19, 20, 21, 22, 54, 23, + 24, 25, 54, 6, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 10, 11, 12, 13, 14, 54, 54, + 54, 54, 54, 54, 20, 21, 22, 54, + 23, 24, 25, 54, 6, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 11, 12, 13, 14, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 23, 24, 25, 54, 6, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 12, 13, 14, + 54, 6, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 13, 14, 54, 6, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 14, + 54, 6, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 12, 13, 14, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 23, 24, + 25, 54, 6, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 12, 13, 14, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 24, 25, 54, 6, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 12, 13, 14, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 25, 54, 6, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 11, 12, 13, 14, 54, + 54, 54, 54, 54, 54, 20, 21, 22, + 54, 23, 24, 25, 54, 6, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 11, 12, 13, 14, + 54, 54, 54, 54, 54, 54, 54, 21, + 22, 54, 23, 24, 25, 54, 6, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 11, 12, 13, + 14, 54, 54, 54, 54, 54, 54, 54, + 54, 22, 54, 23, 24, 25, 54, 6, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 10, 11, 12, + 13, 14, 54, 16, 10, 54, 54, 54, + 20, 21, 22, 54, 23, 24, 25, 54, + 6, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 10, 11, + 12, 13, 14, 54, 54, 10, 54, 54, + 54, 20, 21, 22, 54, 23, 24, 25, + 54, 6, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 10, + 11, 12, 13, 14, 15, 16, 10, 54, + 54, 54, 20, 21, 22, 54, 23, 24, + 25, 54, 6, 54, 54, 1, 55, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 10, 11, 12, 13, 14, 15, 16, 10, + 17, 54, 19, 20, 21, 22, 54, 23, + 24, 25, 54, 1, 57, 3, 54, 54, + 54, 3, 54, 54, 6, 54, 54, 1, + 55, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 10, 11, 12, 13, 14, 15, + 16, 10, 17, 18, 19, 20, 21, 22, + 54, 23, 24, 25, 54, 6, 54, 54, + 1, 55, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 10, 11, 12, 13, 14, + 15, 16, 10, 17, 18, 19, 20, 21, + 22, 54, 23, 24, 25, 54, 59, 58, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 59, 60, 58, 59, 60, 58, + 60, 58, 0 +}; + +static const char _use_syllable_machine_trans_targs[] = { + 1, 26, 2, 3, 1, 23, 1, 43, + 44, 46, 28, 29, 30, 31, 32, 39, + 40, 41, 45, 42, 36, 37, 38, 33, + 34, 35, 1, 1, 1, 1, 4, 5, + 22, 7, 8, 9, 10, 11, 18, 19, + 20, 21, 15, 16, 17, 12, 13, 14, + 6, 1, 1, 24, 25, 1, 1, 0, + 27, 1, 1, 47, 48 +}; + +static const char _use_syllable_machine_trans_actions[] = { + 1, 2, 0, 0, 5, 0, 6, 0, + 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 7, 8, 9, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 11, 12, 0, 0, 13, 14, 0, + 2, 15, 16, 0, 0 +}; + +static const char _use_syllable_machine_to_state_actions[] = { + 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 +}; + +static const char _use_syllable_machine_from_state_actions[] = { + 0, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 +}; + +static const short _use_syllable_machine_eof_trans[] = { + 1, 0, 27, 29, 29, 50, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 51, + 54, 51, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 58, 55, 55, 59, 59, + 59 +}; + +static const int use_syllable_machine_start = 1; +static const int use_syllable_machine_first_final = 1; +static const int use_syllable_machine_error = -1; + +static const int use_syllable_machine_en_main = 1; + + +#line 38 "hb-ot-shape-complex-use-machine.rl" + + + +#line 138 "hb-ot-shape-complex-use-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 315 "hb-ot-shape-complex-use-machine.hh" + { + cs = use_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 159 "hb-ot-shape-complex-use-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + +#line 332 "hb-ot-shape-complex-use-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _use_syllable_machine_from_state_actions[cs] ) { + case 4: +#line 1 "NONE" + {ts = p;} + break; +#line 346 "hb-ot-shape-complex-use-machine.hh" + } + + _keys = _use_syllable_machine_trans_keys + (cs<<1); + _inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs]; + + _slen = _use_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].use_category()) && + ( info[p].use_category()) <= _keys[1] ? + ( info[p].use_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _use_syllable_machine_trans_targs[_trans]; + + if ( _use_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _use_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 8: +#line 127 "hb-ot-shape-complex-use-machine.rl" + {te = p+1;{ found_syllable (independent_cluster); }} + break; + case 10: +#line 129 "hb-ot-shape-complex-use-machine.rl" + {te = p+1;{ found_syllable (standard_cluster); }} + break; + case 6: +#line 133 "hb-ot-shape-complex-use-machine.rl" + {te = p+1;{ found_syllable (broken_cluster); }} + break; + case 5: +#line 134 "hb-ot-shape-complex-use-machine.rl" + {te = p+1;{ found_syllable (non_cluster); }} + break; + case 7: +#line 127 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (independent_cluster); }} + break; + case 11: +#line 128 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (virama_terminated_cluster); }} + break; + case 9: +#line 129 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (standard_cluster); }} + break; + case 13: +#line 130 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }} + break; + case 12: +#line 131 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (numeral_cluster); }} + break; + case 16: +#line 132 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (symbol_cluster); }} + break; + case 14: +#line 133 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (broken_cluster); }} + break; + case 15: +#line 134 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (non_cluster); }} + break; + case 1: +#line 133 "hb-ot-shape-complex-use-machine.rl" + {{p = ((te))-1;}{ found_syllable (broken_cluster); }} + break; +#line 420 "hb-ot-shape-complex-use-machine.hh" + } + +_again: + switch ( _use_syllable_machine_to_state_actions[cs] ) { + case 3: +#line 1 "NONE" + {ts = 0;} + break; +#line 429 "hb-ot-shape-complex-use-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _use_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _use_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 168 "hb-ot-shape-complex-use-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl new file mode 100644 index 000000000..f6b814b1d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl @@ -0,0 +1,173 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH + +#include "hb-private.hh" + +%%{ + machine use_syllable_machine; + alphtype unsigned char; + write data; +}%% + +%%{ + +# Same order as enum use_category_t. Not sure how to avoid duplication. + +O = 0; # OTHER + +B = 1; # BASE +IND = 3; # BASE_IND +N = 4; # BASE_NUM +GB = 5; # BASE_OTHER +CGJ = 6; # CGJ +#F = 7; # CONS_FINAL +FM = 8; # CONS_FINAL_MOD +#M = 9; # CONS_MED +#CM = 10; # CONS_MOD +SUB = 11; # CONS_SUB +H = 12; # HALANT + +HN = 13; # HALANT_NUM +ZWNJ = 14; # Zero width non-joiner +ZWJ = 15; # Zero width joiner +WJ = 16; # Word joiner +Rsv = 17; # Reserved characters +R = 18; # REPHA +S = 19; # SYM +#SM = 20; # SYM_MOD +VS = 21; # VARIATION_SELECTOR +#V = 36; # VOWEL +#VM = 40; # VOWEL_MOD + +FAbv = 24; # CONS_FINAL_ABOVE +FBlw = 25; # CONS_FINAL_BELOW +FPst = 26; # CONS_FINAL_POST +MAbv = 27; # CONS_MED_ABOVE +MBlw = 28; # CONS_MED_BELOW +MPst = 29; # CONS_MED_POST +MPre = 30; # CONS_MED_PRE +CMAbv = 31; # CONS_MOD_ABOVE +CMBlw = 32; # CONS_MOD_BELOW +VAbv = 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST +VBlw = 34; # VOWEL_BELOW / VOWEL_BELOW_POST +VPst = 35; # VOWEL_POST UIPC = Right +VPre = 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST +VMAbv = 37; # VOWEL_MOD_ABOVE +VMBlw = 38; # VOWEL_MOD_BELOW +VMPst = 39; # VOWEL_MOD_POST +VMPre = 23; # VOWEL_MOD_PRE +SMAbv = 41; # SYM_MOD_ABOVE +SMBlw = 42; # SYM_MOD_BELOW + + +consonant_modifiers = CMAbv* CMBlw* ((H B | SUB) VS? CMAbv? CMBlw*)*; +medial_consonants = MPre? MAbv? MBlw? MPst?; +dependent_vowels = VPre* VAbv* VBlw* VPst*; +vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*; +final_consonants = FAbv* FBlw* FPst* FM?; + +virama_terminated_cluster = + R? (B | GB) VS? + consonant_modifiers + H +; +standard_cluster = + R? (B | GB) VS? + consonant_modifiers + medial_consonants + dependent_vowels + vowel_modifiers + final_consonants +; + +broken_cluster = + R? + consonant_modifiers + medial_consonants + dependent_vowels + vowel_modifiers + final_consonants +; + +number_joiner_terminated_cluster = N VS? (HN N VS?)* HN; +numeral_cluster = N VS? (HN N VS?)*; +symbol_cluster = S VS? SMAbv* SMBlw*; +independent_cluster = (IND | O | Rsv | WJ) VS?; +other = any; + +main := |* + independent_cluster => { found_syllable (independent_cluster); }; + virama_terminated_cluster => { found_syllable (virama_terminated_cluster); }; + standard_cluster => { found_syllable (standard_cluster); }; + number_joiner_terminated_cluster => { found_syllable (number_joiner_terminated_cluster); }; + numeral_cluster => { found_syllable (numeral_cluster); }; + symbol_cluster => { found_syllable (symbol_cluster); }; + broken_cluster => { found_syllable (broken_cluster); }; + other => { found_syllable (non_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +static void +find_syllables (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].use_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use-private.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-use-private.hh new file mode 100644 index 000000000..ae428cb5e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-private.hh @@ -0,0 +1,96 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH +#define HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH + +#include "hb-private.hh" + + +#include "hb-ot-shape-complex-private.hh" + + +#define USE_TABLE_ELEMENT_TYPE uint8_t + +/* Cateories used in the Universal Shaping Engine spec: + * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm + */ +/* Note: This enum is duplicated in the -machine.rl source file. + * Not sure how to avoid duplication. */ +enum use_category_t { + USE_O = 0, /* OTHER */ + + USE_B = 1, /* BASE */ + USE_IND = 3, /* BASE_IND */ + USE_N = 4, /* BASE_NUM */ + USE_GB = 5, /* BASE_OTHER */ + USE_CGJ = 6, /* CGJ */ +// USE_F = 7, /* CONS_FINAL */ + USE_FM = 8, /* CONS_FINAL_MOD */ +// USE_M = 9, /* CONS_MED */ +// USE_CM = 10, /* CONS_MOD */ + USE_SUB = 11, /* CONS_SUB */ + USE_H = 12, /* HALANT */ + + USE_HN = 13, /* HALANT_NUM */ + USE_ZWNJ = 14, /* Zero width non-joiner */ + USE_ZWJ = 15, /* Zero width joiner */ + USE_WJ = 16, /* Word joiner */ + USE_Rsv = 17, /* Reserved characters */ + USE_R = 18, /* REPHA */ + USE_S = 19, /* SYM */ +// USE_SM = 20, /* SYM_MOD */ + USE_VS = 21, /* VARIATION_SELECTOR */ +// USE_V = 36, /* VOWEL */ +// USE_VM = 40, /* VOWEL_MOD */ + + USE_FAbv = 24, /* CONS_FINAL_ABOVE */ + USE_FBlw = 25, /* CONS_FINAL_BELOW */ + USE_FPst = 26, /* CONS_FINAL_POST */ + USE_MAbv = 27, /* CONS_MED_ABOVE */ + USE_MBlw = 28, /* CONS_MED_BELOW */ + USE_MPst = 29, /* CONS_MED_POST */ + USE_MPre = 30, /* CONS_MED_PRE */ + USE_CMAbv = 31, /* CONS_MOD_ABOVE */ + USE_CMBlw = 32, /* CONS_MOD_BELOW */ + USE_VAbv = 33, /* VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST */ + USE_VBlw = 34, /* VOWEL_BELOW / VOWEL_BELOW_POST */ + USE_VPst = 35, /* VOWEL_POST UIPC = Right */ + USE_VPre = 22, /* VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST */ + USE_VMAbv = 37, /* VOWEL_MOD_ABOVE */ + USE_VMBlw = 38, /* VOWEL_MOD_BELOW */ + USE_VMPst = 39, /* VOWEL_MOD_POST */ + USE_VMPre = 23, /* VOWEL_MOD_PRE */ + USE_SMAbv = 41, /* SYM_MOD_ABOVE */ + USE_SMBlw = 42 /* SYM_MOD_BELOW */ +}; + +HB_INTERNAL USE_TABLE_ELEMENT_TYPE +hb_use_get_categories (hb_codepoint_t u); + +#endif /* HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc new file mode 100644 index 000000000..38c46d002 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc @@ -0,0 +1,734 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-9.0.0.txt + * # Date: 2016-05-21, 02:46:00 GMT [RP] + * # IndicPositionalCategory-9.0.0.txt + * # Date: 2016-02-25, 00:48:00 GMT [RP] + * # Blocks-9.0.0.txt + * # Date: 2016-02-05, 23:48:00 GMT [KW] + * UnicodeData.txt does not have a header. + */ + +#include "hb-ot-shape-complex-use-private.hh" + +#define B USE_B /* BASE */ +#define CGJ USE_CGJ /* CGJ */ +#define FM USE_FM /* CONS_FINAL_MOD */ +#define GB USE_GB /* BASE_OTHER */ +#define H USE_H /* HALANT */ +#define HN USE_HN /* HALANT_NUM */ +#define IND USE_IND /* BASE_IND */ +#define N USE_N /* BASE_NUM */ +#define O USE_O /* OTHER */ +#define R USE_R /* REPHA */ +#define Rsv USE_Rsv /* Reserved */ +#define S USE_S /* SYM */ +#define SUB USE_SUB /* CONS_SUB */ +#define VS USE_VS /* VARIATION_SELECTOR */ +#define WJ USE_WJ /* Word_Joiner */ +#define ZWJ USE_ZWJ /* ZWJ */ +#define ZWNJ USE_ZWNJ /* ZWNJ */ +#define CMBlw USE_CMBlw +#define CMAbv USE_CMAbv +#define FBlw USE_FBlw +#define FPst USE_FPst +#define FAbv USE_FAbv +#define MPre USE_MPre +#define MBlw USE_MBlw +#define MPst USE_MPst +#define MAbv USE_MAbv +#define SMBlw USE_SMBlw +#define SMAbv USE_SMAbv +#define VPre USE_VPre +#define VBlw USE_VBlw +#define VPst USE_VPst +#define VAbv USE_VAbv +#define VMPre USE_VMPre +#define VMBlw USE_VMBlw +#define VMPst USE_VMPst +#define VMAbv USE_VMAbv + +static const USE_TABLE_ELEMENT_TYPE use_table[] = { + + +#define use_offset_0x0028u 0 + + + /* Basic Latin */ + O, O, O, O, O, GB, O, O, + /* 0030 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + +#define use_offset_0x00a0u 24 + + + /* Latin-1 Supplement */ + + /* 00A0 */ GB, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 00B0 */ O, O, FM, FM, O, O, O, O, O, O, O, O, O, O, O, O, + /* 00C0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 00D0 */ O, O, O, O, O, O, O, GB, + +#define use_offset_0x0900u 80 + + + /* Devanagari */ + + /* 0900 */ VMAbv, VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0920 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0930 */ B, B, B, B, B, B, B, B, B, B, VAbv, VPst, CMBlw, B, VPst, VPre, + /* 0940 */ VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VPst, VPst, VPst, VPst, H, VPre, VPst, + /* 0950 */ O, VMAbv, VMBlw, O, O, VAbv, VBlw, VBlw, B, B, B, B, B, B, B, B, + /* 0960 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0970 */ O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + + /* Bengali */ + + /* 0980 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 0990 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 09A0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 09B0 */ B, O, B, O, O, O, B, B, B, B, O, O, CMBlw, B, VPst, VPre, + /* 09C0 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, O, O, VPre, VPre, H, IND, O, + /* 09D0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, B, B, O, B, + /* 09E0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Gurmukhi */ + + /* 0A00 */ O, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, O, O, O, O, B, + /* 0A10 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0A20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 0A30 */ B, O, B, B, O, B, B, O, B, B, O, O, CMBlw, O, VPst, VPre, + /* 0A40 */ VPst, VBlw, VBlw, O, O, O, O, VAbv, VAbv, O, O, VAbv, VAbv, H, O, O, + /* 0A50 */ O, O, O, O, O, O, O, O, O, B, B, B, B, O, B, O, + /* 0A60 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0A70 */ VMAbv, CMAbv, GB, GB, O, MBlw, O, O, O, O, O, O, O, O, O, O, + + /* Gujarati */ + + /* 0A80 */ O, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, B, B, B, O, B, + /* 0A90 */ B, B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0AA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 0AB0 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VPre, + /* 0AC0 */ VPst, VBlw, VBlw, VBlw, VBlw, VAbv, O, VAbv, VAbv, VAbv, O, VPst, VPst, H, O, O, + /* 0AD0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 0AE0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0AF0 */ O, O, O, O, O, O, O, O, O, B, O, O, O, O, O, O, + + /* Oriya */ + + /* 0B00 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 0B10 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0B20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 0B30 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, + /* 0B40 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, O, O, VPre, VPre, H, O, O, + /* 0B50 */ O, O, O, O, O, O, VAbv, VAbv, O, O, O, O, B, B, O, B, + /* 0B60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0B70 */ O, B, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Tamil */ + + /* 0B80 */ O, O, VMAbv, IND, O, B, B, B, B, B, B, O, O, O, B, B, + /* 0B90 */ B, O, B, B, B, B, O, O, O, B, B, O, B, O, B, B, + /* 0BA0 */ O, O, O, B, B, O, O, O, B, B, B, O, O, O, B, B, + /* 0BB0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, VPst, VPst, + /* 0BC0 */ VAbv, VPst, VPst, O, O, O, VPre, VPre, VPre, O, VPre, VPre, VPre, H, O, O, + /* 0BD0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, O, O, O, O, + /* 0BE0 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0BF0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Telugu */ + + /* 0C00 */ VMAbv, VMPst, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0C10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0C20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 0C30 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, VAbv, VAbv, + /* 0C40 */ VAbv, VPst, VPst, VPst, VPst, O, VAbv, VAbv, VAbv, O, VAbv, VAbv, VAbv, H, O, O, + /* 0C50 */ O, O, O, O, O, VAbv, VBlw, O, B, B, B, O, O, O, O, O, + /* 0C60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0C70 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Kannada */ + + /* 0C80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0C90 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0CA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 0CB0 */ B, B, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, + /* 0CC0 */ VAbv, VPst, VPst, VPst, VPst, O, VAbv, VAbv, VAbv, O, VAbv, VAbv, VAbv, H, O, O, + /* 0CD0 */ O, O, O, O, O, VPst, VPst, O, O, O, O, O, O, O, B, O, + /* 0CE0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0CF0 */ O, R, R, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Malayalam */ + + /* 0D00 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0D10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0D20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0D30 */ B, B, B, B, B, B, B, B, B, B, B, O, O, B, VPst, VPst, + /* 0D40 */ VPst, VPst, VPst, VBlw, VBlw, O, VPre, VPre, VPre, O, VPre, VPre, VPre, H, R, O, + /* 0D50 */ O, O, O, O, IND, IND, IND, VPst, O, O, O, O, O, O, O, B, + /* 0D60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0D70 */ O, O, O, O, O, O, O, O, O, O, IND, IND, IND, IND, IND, IND, + + /* Sinhala */ + + /* 0D80 */ O, O, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B, + /* 0D90 */ B, B, B, B, B, B, B, O, O, O, B, B, B, B, B, B, + /* 0DA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 0DB0 */ B, B, O, B, B, B, B, B, B, B, B, B, O, B, O, O, + /* 0DC0 */ B, B, B, B, B, B, B, O, O, O, H, O, O, O, O, VPst, + /* 0DD0 */ VPst, VPst, VAbv, VAbv, VBlw, O, VBlw, O, VPst, VPre, VPre, VPre, VPre, VPre, VPre, VPst, + /* 0DE0 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, + /* 0DF0 */ O, O, VPst, VPst, O, O, O, O, + +#define use_offset_0x1000u 1352 + + + /* Myanmar */ + + /* 1000 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1010 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1020 */ B, B, B, B, B, B, B, B, B, B, B, VPst, VPst, VAbv, VAbv, VBlw, + /* 1030 */ VBlw, VPre, VAbv, VAbv, VAbv, VAbv, VMAbv, VMBlw, VMPst, H, VAbv, MPst, MPre, MBlw, MBlw, B, + /* 1040 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, GB, O, + /* 1050 */ B, B, B, B, B, B, VPst, VPst, VBlw, VBlw, B, B, B, B, MBlw, MBlw, + /* 1060 */ MBlw, B, VPst, VMPst, VMPst, B, B, VPst, VPst, VMPst, VMPst, VMPst, VMPst, VMPst, B, B, + /* 1070 */ B, VAbv, VAbv, VAbv, VAbv, B, B, B, B, B, B, B, B, B, B, B, + /* 1080 */ B, B, MBlw, VPst, VPre, VAbv, VAbv, VMPst, VMPst, VMPst, VMPst, VMPst, VMPst, VMBlw, B, VMPst, + /* 1090 */ B, B, B, B, B, B, B, B, B, B, VMPst, VMPst, VPst, VAbv, O, O, + +#define use_offset_0x1700u 1512 + + + /* Tagalog */ + + /* 1700 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, B, B, + /* 1710 */ B, B, VAbv, VBlw, VBlw, O, O, O, O, O, O, O, O, O, O, O, + + /* Hanunoo */ + + /* 1720 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1730 */ B, B, VAbv, VBlw, VBlw, O, O, O, O, O, O, O, O, O, O, O, + + /* Buhid */ + + /* 1740 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1750 */ B, B, VAbv, VBlw, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Tagbanwa */ + + /* 1760 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, B, B, + /* 1770 */ B, O, VAbv, VBlw, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Khmer */ + + /* 1780 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1790 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 17A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 17B0 */ B, B, B, B, O, O, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VPre, VPre, + /* 17C0 */ VPre, VPre, VPre, VPre, VPre, VPre, VMAbv, VMPst, VPst, VMAbv, VMAbv, FM, FAbv, CMAbv, FM, FM, + /* 17D0 */ FM, VAbv, H, FM, O, O, O, O, O, O, O, O, B, VAbv, O, O, + /* 17E0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + +#define use_offset_0x1900u 1752 + + + /* Limbu */ + + /* 1900 */ GB, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, + /* 1920 */ VAbv, VAbv, VBlw, VPst, VPst, VAbv, VAbv, VAbv, VAbv, SUB, SUB, SUB, O, O, O, O, + /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FM, O, O, O, O, + /* 1940 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, + + /* Tai Le */ + + /* 1950 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1960 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, + /* 1970 */ B, B, B, B, B, O, O, O, O, O, O, O, O, O, O, O, + + /* New Tai Lue */ + + /* 1980 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 19A0 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, + /* 19B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 19C0 */ B, B, B, B, B, B, B, B, VMPst, VMPst, O, O, O, O, O, O, + /* 19D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 19E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 19F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Buginese */ + + /* 1A00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1A10 */ B, B, B, B, B, B, B, VAbv, VBlw, VPre, VPst, VAbv, O, O, O, O, + + /* Tai Tham */ + + /* 1A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1A30 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1A40 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1A50 */ B, B, B, B, B, MPre, MBlw, FPst, FAbv, FAbv, FAbv, FBlw, FBlw, FBlw, FBlw, O, + /* 1A60 */ H, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre, + /* 1A70 */ VPre, VPre, VPre, VAbv, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, FM, FM, FM, O, O, FM, + /* 1A80 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 1A90 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + +#define use_offset_0x1b00u 2168 + + + /* Balinese */ + + /* 1B00 */ VMAbv, VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, + /* 1B10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre, + /* 1B40 */ VPre, VPre, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O, + /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 1B60 */ O, O, O, O, O, O, O, O, O, O, O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, + /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Sundanese */ + + /* 1B80 */ VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1B90 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1BA0 */ B, SUB, SUB, SUB, VAbv, VBlw, VPre, VPst, VAbv, VAbv, VPst, H, SUB, SUB, B, B, + /* 1BB0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + + /* Batak */ + + /* 1BC0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1BD0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1BE0 */ B, B, B, B, B, B, CMAbv, VPst, VAbv, VAbv, VPst, VPst, VPst, VAbv, VPst, VAbv, + /* 1BF0 */ FAbv, FAbv, VPst, VPst, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Lepcha */ + + /* 1C00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPre, VPst, VPst, VBlw, FAbv, FAbv, FAbv, + /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FM, CMBlw, O, O, O, O, O, O, O, O, + /* 1C40 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, B, B, + +#define use_offset_0x1cd0u 2504 + + + /* Vedic Extensions */ + + /* 1CD0 */ VMAbv, VMAbv, VMAbv, O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw, + /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, O, O, O, O, VMBlw, O, O, + /* 1CF0 */ O, O, VMPst, VMPst, VMAbv, O, O, O, VMAbv, VMAbv, O, O, O, O, O, O, + +#define use_offset_0x1df8u 2552 + + + /* Combining Diacritical Marks Supplement */ + O, O, O, FM, O, O, O, O, + +#define use_offset_0x2008u 2560 + + + /* General Punctuation */ + O, O, O, O, ZWNJ, ZWJ, O, O, + /* 2010 */ GB, GB, GB, GB, GB, O, O, O, + +#define use_offset_0x2060u 2576 + + /* 2060 */ WJ, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Superscripts and Subscripts */ + + /* 2070 */ O, O, O, O, FM, O, O, O, O, O, O, O, O, O, O, O, + /* 2080 */ O, O, FM, FM, FM, O, O, O, + +#define use_offset_0xa800u 2616 + + + /* Syloti Nagri */ + + /* A800 */ B, B, O, B, B, B, VAbv, B, B, B, B, VMAbv, B, B, B, B, + /* A810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, O, O, O, O, + /* A830 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Phags-pa */ + + /* A840 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A850 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A860 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A870 */ B, B, B, B, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Saurashtra */ + + /* A880 */ VMPst, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A890 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A8A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A8B0 */ B, B, B, B, FPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, + /* A8C0 */ VPst, VPst, VPst, VPst, H, VMAbv, O, O, O, O, O, O, O, O, O, O, + /* A8D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + + /* Devanagari Extended */ + + /* A8E0 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, + /* A8F0 */ VMAbv, VMAbv, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Kayah Li */ + + /* A900 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A920 */ B, B, B, B, B, B, VAbv, VAbv, VAbv, VAbv, VAbv, VMBlw, VMBlw, VMBlw, O, O, + + /* Rejang */ + + /* A930 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A940 */ B, B, B, B, B, B, B, VBlw, VBlw, VBlw, VAbv, VBlw, VBlw, VBlw, VBlw, FAbv, + /* A950 */ FAbv, FAbv, FPst, VPst, O, O, O, O, O, O, O, O, O, O, O, O, + /* A960 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* A970 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Javanese */ + + /* A980 */ VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, + /* A990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A9A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, SUB, MPst, MPst, + /* A9C0 */ H, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* A9D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + + /* Myanmar Extended-B */ + + /* A9E0 */ B, B, B, B, B, VAbv, O, B, B, B, B, B, B, B, B, B, + /* A9F0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, + + /* Cham */ + + /* AA00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AA10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AA20 */ B, B, B, B, B, B, B, B, B, VAbv, VAbv, VAbv, VAbv, VBlw, VAbv, VPre, + /* AA30 */ VPre, VAbv, VBlw, MPst, MPre, MBlw, MBlw, O, O, O, O, O, O, O, O, O, + /* AA40 */ B, B, B, FAbv, B, B, B, B, B, B, B, B, FAbv, FPst, O, O, + /* AA50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + + /* Myanmar Extended-A */ + + /* AA60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AA70 */ O, B, B, B, GB, GB, GB, O, O, O, B, VMPst, VMAbv, VMPst, B, B, + + /* Tai Viet */ + + /* AA80 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AA90 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AAA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* AAB0 */ VAbv, B, VAbv, VAbv, VBlw, B, B, VAbv, VAbv, B, B, B, B, B, VAbv, VMAbv, + /* AAC0 */ B, VMAbv, B, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* AAD0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Meetei Mayek Extensions */ + + /* AAE0 */ B, B, B, B, B, B, B, B, B, B, B, VPre, VBlw, VAbv, VPre, VPst, + /* AAF0 */ O, O, O, O, O, VMPst, H, O, + +#define use_offset_0xabc0u 3376 + + + /* Meetei Mayek */ + + /* ABC0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* ABD0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* ABE0 */ B, B, B, VPst, VPst, VAbv, VPst, VPst, VBlw, VPst, VPst, O, VMPst, VBlw, O, O, + /* ABF0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + +#define use_offset_0xfe00u 3440 + + + /* Variation Selectors */ + + /* FE00 */ VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, + +#define use_offset_0x10a00u 3456 + + + /* Kharoshthi */ + + /* 10A00 */ B, VBlw, VBlw, VBlw, O, VAbv, VBlw, O, O, O, O, O, VBlw, VBlw, VMBlw, VMAbv, + /* 10A10 */ B, B, B, B, O, B, B, B, O, B, B, B, B, B, B, B, + /* 10A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 10A30 */ B, B, B, B, O, O, O, O, CMAbv, CMBlw, CMBlw, O, O, O, O, H, + /* 10A40 */ B, B, B, B, B, B, B, B, + +#define use_offset_0x11000u 3528 + + + /* Brahmi */ + + /* 11000 */ VMPst, VMAbv, VMPst, R, R, B, B, B, B, B, B, B, B, B, B, B, + /* 11010 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11020 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11030 */ B, B, B, B, B, B, B, B, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, + /* 11040 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, O, O, O, O, O, O, O, O, O, + /* 11050 */ O, O, N, N, N, N, N, N, N, N, N, N, N, N, N, N, + /* 11060 */ N, N, N, N, N, N, B, B, B, B, B, B, B, B, B, B, + /* 11070 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Kaithi */ + + /* 11080 */ VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11090 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 110A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 110B0 */ VPst, VPre, VPst, VBlw, VBlw, VAbv, VAbv, VPst, VPst, H, CMBlw, O, O, O, O, O, + +#define use_offset_0x11100u 3720 + + + /* Chakma */ + + /* 11100 */ VMAbv, VMAbv, VMAbv, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11110 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11120 */ B, B, B, B, B, B, B, VAbv, VAbv, VAbv, VBlw, VBlw, VPre, VAbv, VAbv, VAbv, + /* 11130 */ VAbv, VBlw, VBlw, H, VAbv, O, B, B, B, B, B, B, B, B, B, B, + /* 11140 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Mahajani */ + + /* 11150 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11160 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11170 */ B, B, B, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Sharada */ + + /* 11180 */ VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, + /* 111C0 */ H, B, R, R, O, O, O, O, O, O, CMBlw, VAbv, VBlw, O, O, O, + /* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + + /* Sinhala Archaic Numbers */ + + /* 111E0 */ O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 111F0 */ B, B, B, B, B, O, O, O, O, O, O, O, O, O, O, O, + + /* Khojki */ + + /* 11200 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11210 */ B, B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11220 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPst, VPst, VBlw, + /* 11230 */ VAbv, VAbv, VAbv, VAbv, VMAbv, H, CMAbv, CMAbv, O, O, O, O, O, O, VMAbv, O, + +#define use_offset_0x11280u 4040 + + + /* Multani */ + + /* 11280 */ B, B, B, B, B, B, B, O, B, O, B, B, B, B, O, B, + /* 11290 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, B, + /* 112A0 */ B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, O, + + /* Khudawadi */ + + /* 112B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 112C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 112D0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VMAbv, + /* 112E0 */ VPst, VPre, VPst, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, CMBlw, VBlw, O, O, O, O, O, + /* 112F0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + + /* Grantha */ + + /* 11300 */ VMAbv, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 11310 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11320 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 11330 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VPst, + /* 11340 */ VAbv, VPst, VPst, VPst, VPst, O, O, VPre, VPre, O, O, VPre, VPre, H, O, O, + /* 11350 */ O, O, O, O, O, O, O, VPst, O, O, O, O, O, O, O, O, + /* 11360 */ B, B, VPst, VPst, O, O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, O, O, O, + /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, O, O, O, + +#define use_offset_0x11400u 4288 + + + /* Newa */ + + /* 11400 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11410 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11420 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11430 */ B, B, B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, + /* 11440 */ VPst, VPst, H, VMAbv, VMAbv, VMPst, CMBlw, B, O, O, O, O, O, O, O, O, + /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 11460 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11470 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Tirhuta */ + + /* 11480 */ O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11490 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 114A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 114B0 */ VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VPre, VAbv, VPre, VPre, VPst, VPre, VMAbv, + /* 114C0 */ VMAbv, VMPst, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, + /* 114D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + +#define use_offset_0x11580u 4512 + + + /* Siddham */ + + /* 11580 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11590 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 115A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VPst, + /* 115B0 */ VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, VPre, VPre, VMAbv, VMAbv, VMPst, H, + /* 115C0 */ CMBlw, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 115D0 */ O, O, O, O, O, O, O, O, B, B, B, B, VBlw, VBlw, O, O, + /* 115E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 115F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Modi */ + + /* 11600 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11610 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11620 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11630 */ VPst, VPst, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPst, VPst, VMAbv, VMPst, H, + /* 11640 */ VAbv, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11650 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 11660 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11670 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Takri */ + + /* 11680 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11690 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 116A0 */ B, B, B, B, B, B, B, B, B, B, B, VMAbv, VMPst, VAbv, VPre, VPst, + /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, O, O, O, O, O, O, O, O, + /* 116C0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 116D0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 116E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 116F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + + /* Ahom */ + + /* 11700 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11710 */ B, B, B, B, B, B, B, B, B, B, O, O, O, MBlw, MPre, MAbv, + /* 11720 */ VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VAbv, VBlw, VAbv, VAbv, VAbv, O, O, O, O, + /* 11730 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, + +#define use_offset_0x11c00u 4960 + + + /* Bhaiksuki */ + + /* 11C00 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, + /* 11C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11C20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VPst, + /* 11C30 */ VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VBlw, O, VAbv, VAbv, VAbv, VAbv, VMAbv, VMAbv, VMPst, H, + /* 11C40 */ B, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11C50 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11C60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, + + /* Marchen */ + + /* 11C70 */ O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11C80 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11C90 */ O, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, + /* 11CA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, + /* 11CB0 */ VBlw, VPre, VBlw, VAbv, VPst, VMAbv, VMAbv, O, + +}; /* Table items: 5144; occupancy: 72% */ + +USE_TABLE_ELEMENT_TYPE +hb_use_get_categories (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u]; + if (hb_in_range (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u]; + if (hb_in_range (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u]; + if (unlikely (u == 0x034Fu)) return CGJ; + break; + + case 0x1u: + if (hb_in_range (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u]; + if (hb_in_range (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u]; + if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u]; + if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u]; + if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u]; + if (hb_in_range (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u]; + break; + + case 0x2u: + if (hb_in_range (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u]; + if (hb_in_range (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u]; + if (unlikely (u == 0x25CCu)) return GB; + break; + + case 0xAu: + if (hb_in_range (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u]; + if (hb_in_range (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u]; + break; + + case 0xFu: + if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u]; + break; + + case 0x10u: + if (hb_in_range (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u]; + break; + + case 0x11u: + if (hb_in_range (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u]; + if (hb_in_range (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u]; + if (hb_in_range (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u]; + if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u]; + if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u]; + if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u]; + if (unlikely (u == 0x1107Fu)) return HN; + break; + + default: + break; + } + return USE_O; +} + +#undef B +#undef CGJ +#undef FM +#undef GB +#undef H +#undef HN +#undef IND +#undef N +#undef O +#undef R +#undef Rsv +#undef S +#undef SUB +#undef VS +#undef WJ +#undef ZWJ +#undef ZWNJ +#undef CMBlw +#undef CMAbv +#undef FBlw +#undef FPst +#undef FAbv +#undef MPre +#undef MBlw +#undef MPst +#undef MAbv +#undef SMBlw +#undef SMAbv +#undef VPre +#undef VBlw +#undef VPst +#undef VAbv +#undef VMPre +#undef VMBlw +#undef VMPst +#undef VMAbv + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc new file mode 100644 index 000000000..5b19d5d74 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc @@ -0,0 +1,632 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-use-private.hh" +#include "hb-ot-shape-complex-arabic-private.hh" + +/* buffer var allocations */ +#define use_category() complex_var_u8_0() + + +/* + * Universal Shaping Engine. + * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm + */ + +static const hb_tag_t +basic_features[] = +{ + /* + * Basic features. + * These features are applied all at once, before reordering. + */ + HB_TAG('r','k','r','f'), + HB_TAG('a','b','v','f'), + HB_TAG('b','l','w','f'), + HB_TAG('h','a','l','f'), + HB_TAG('p','s','t','f'), + HB_TAG('v','a','t','u'), + HB_TAG('c','j','c','t'), +}; +static const hb_tag_t +arabic_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), + /* The spec doesn't specify these but we apply anyway, since our Arabic shaper + * does. These are only used in Syriac spec. */ + HB_TAG('m','e','d','2'), + HB_TAG('f','i','n','2'), + HB_TAG('f','i','n','3'), +}; +/* Same order as arabic_features. Don't need Syriac stuff.*/ +enum joining_form_t { + ISOL, + INIT, + MEDI, + FINA, + _NONE +}; +static const hb_tag_t +other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after reordering. + */ + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('h','a','l','n'), + HB_TAG('p','r','e','s'), + HB_TAG('p','s','t','s'), + /* Positioning features, though we don't care about the types. */ + HB_TAG('d','i','s','t'), + HB_TAG('a','b','v','m'), + HB_TAG('b','l','w','m'), +}; + +static void +setup_syllables (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +clear_substitution_flags (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_rphf (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_pref (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +reorder (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_use (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables); + + /* "Default glyph pre-processing group" */ + map->add_global_bool_feature (HB_TAG('l','o','c','l')); + map->add_global_bool_feature (HB_TAG('c','c','m','p')); + map->add_global_bool_feature (HB_TAG('n','u','k','t')); + map->add_global_bool_feature (HB_TAG('a','k','h','n')); + + /* "Reordering group" */ + map->add_gsub_pause (clear_substitution_flags); + map->add_feature (HB_TAG('r','p','h','f'), 1, F_MANUAL_ZWJ); + map->add_gsub_pause (record_rphf); + map->add_gsub_pause (clear_substitution_flags); + map->add_feature (HB_TAG('p','r','e','f'), 1, F_GLOBAL | F_MANUAL_ZWJ); + map->add_gsub_pause (record_pref); + + /* "Orthographic unit shaping group" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) + map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); + + map->add_gsub_pause (reorder); + + /* "Topographical features" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++) + map->add_feature (arabic_features[i], 1, F_NONE); + map->add_gsub_pause (NULL); + + /* "Standard typographic presentation" and "Positional feature application" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) + map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); +} + +struct use_shape_plan_t +{ + ASSERT_POD (); + + hb_mask_t rphf_mask; + + arabic_shape_plan_t *arabic_plan; +}; + +static bool +has_arabic_joining (hb_script_t script) +{ + /* List of scripts that have data in arabic-table. */ + switch ((int) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_SYRIAC: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + + return true; + + default: + return false; + } +} + +static void * +data_create_use (const hb_ot_shape_plan_t *plan) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t)); + if (unlikely (!use_plan)) + return NULL; + + use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f')); + + if (has_arabic_joining (plan->props.script)) + { + use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); + if (unlikely (!use_plan->arabic_plan)) + { + free (use_plan); + return NULL; + } + } + + return use_plan; +} + +static void +data_destroy_use (void *data) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) data; + + if (use_plan->arabic_plan) + data_destroy_arabic (use_plan->arabic_plan); + + free (data); +} + +enum syllable_type_t { + independent_cluster, + virama_terminated_cluster, + standard_cluster, + number_joiner_terminated_cluster, + numeral_cluster, + symbol_cluster, + broken_cluster, + non_cluster, +}; + +#include "hb-ot-shape-complex-use-machine.hh" + + +static void +setup_masks_use (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + /* Do this before allocating use_category(). */ + if (use_plan->arabic_plan) + { + setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script); + } + + HB_BUFFER_ALLOCATE_VAR (buffer, use_category); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].use_category() = hb_use_get_categories (info[i].codepoint); +} + +static void +setup_rphf_mask (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return; + + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start); + for (unsigned int i = start; i < start + limit; i++) + info[i].mask |= mask; + } +} + +static void +setup_topographical_masks (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + if (use_plan->arabic_plan) + return; + + ASSERT_STATIC (INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4); + hb_mask_t masks[4], all_masks = 0; + for (unsigned int i = 0; i < 4; i++) + { + masks[i] = plan->map.get_1_mask (arabic_features[i]); + if (masks[i] == plan->map.get_global_mask ()) + masks[i] = 0; + all_masks |= masks[i]; + } + if (!all_masks) + return; + hb_mask_t other_masks = ~all_masks; + + unsigned int last_start = 0; + joining_form_t last_form = _NONE; + hb_glyph_info_t *info = buffer->info; + foreach_syllable (buffer, start, end) + { + syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F); + switch (syllable_type) + { + case independent_cluster: + case symbol_cluster: + case non_cluster: + /* These don't join. Nothing to do. */ + last_form = _NONE; + break; + + case virama_terminated_cluster: + case standard_cluster: + case number_joiner_terminated_cluster: + case numeral_cluster: + case broken_cluster: + + bool join = last_form == FINA || last_form == ISOL; + + if (join) + { + /* Fixup previous syllable's form. */ + last_form = last_form == FINA ? MEDI : INIT; + for (unsigned int i = last_start; i < start; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + } + + /* Form for this syllable. */ + last_form = join ? FINA : ISOL; + for (unsigned int i = start; i < end; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + + break; + } + + last_start = start; + } +} + +static void +setup_syllables (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + find_syllables (buffer); + setup_rphf_mask (plan, buffer); + setup_topographical_masks (plan, buffer); +} + +static void +clear_substitution_flags (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_clear_substituted (&info[i]); +} + +static void +record_rphf (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return; + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted repha as USE_R. */ + for (unsigned int i = start; i < end && (info[i].mask & mask); i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE_R; + break; + } + } +} + +static void +record_pref (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted pref as VPre, as they behave the same way. */ + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE_VPre; + break; + } + } +} + +static inline bool +is_halant (const hb_glyph_info_t &info) +{ + return info.use_category() == USE_H && !_hb_glyph_info_ligated (&info); +} + +static void +reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) +{ + syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + /* Only a few syllable types need reordering. */ + if (unlikely (!(FLAG_SAFE (syllable_type) & + (FLAG (virama_terminated_cluster) | + FLAG (standard_cluster) | + FLAG (broken_cluster) | + 0)))) + return; + + hb_glyph_info_t *info = buffer->info; + +#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB)) + + /* Move things forward. */ + if (info[start].use_category() == USE_R && end - start > 1) + { + /* Got a repha. Reorder it to after first base, before first halant. */ + for (unsigned int i = start + 1; i < end; i++) + if ((FLAG_UNSAFE (info[i].use_category()) & (BASE_FLAGS)) || is_halant (info[i])) + { + /* If we hit a halant, move before it; otherwise it's a base: move to it's + * place, and shift things in between backward. */ + + if (is_halant (info[i])) + i--; + + buffer->merge_clusters (start, i + 1); + hb_glyph_info_t t = info[start]; + memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); + info[i] = t; + + break; + } + } + + /* Move things back. */ + unsigned int j = end; + for (unsigned int i = start; i < end; i++) + { + uint32_t flag = FLAG_UNSAFE (info[i].use_category()); + if ((flag & (BASE_FLAGS)) || is_halant (info[i])) + { + /* If we hit a halant, move after it; otherwise it's a base: move to it's + * place, and shift things in between backward. */ + if (is_halant (info[i])) + j = i + 1; + else + j = i; + } + else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) && + /* Only move the first component of a MultipleSubst. */ + 0 == _hb_glyph_info_get_lig_comp (&info[i]) && + j < i) + { + buffer->merge_clusters (j, i + 1); + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); + info[j] = t; + } + } +} + +static inline void +insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) +{ + /* Note: This loop is extra overhead, but should not be measurable. */ + bool has_broken_syllables = false; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if ((info[i].syllable() & 0x0F) == broken_cluster) + { + has_broken_syllables = true; + break; + } + if (likely (!has_broken_syllables)) + return; + + hb_glyph_info_t dottedcircle = {0}; + if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint)) + return; + dottedcircle.use_category() = hb_use_get_categories (0x25CC); + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len && !buffer->in_error) + { + unsigned int syllable = buffer->cur().syllable(); + syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + { + last_syllable = syllable; + + hb_glyph_info_t ginfo = dottedcircle; + ginfo.cluster = buffer->cur().cluster; + ginfo.mask = buffer->cur().mask; + ginfo.syllable() = buffer->cur().syllable(); + /* TODO Set glyph_props? */ + + /* Insert dottedcircle after possible Repha. */ + while (buffer->idx < buffer->len && !buffer->in_error && + last_syllable == buffer->cur().syllable() && + buffer->cur().use_category() == USE_R) + buffer->next_glyph (); + + buffer->output_info (ginfo); + } + else + buffer->next_glyph (); + } + + buffer->swap_buffers (); +} + +static void +reorder (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + insert_dotted_circles (plan, font, buffer); + + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + reorder_syllable (buffer, start, end); + + /* Zero syllables now... */ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + info[i].syllable() = 0; + + HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); +} + +static bool +decompose_use (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* Chakma: + * Special case where the Unicode decomp gives matras in the wrong order + * for cluster validation. + */ + case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true; + case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true; + + /* + * Decompose split matras that don't have Unicode decompositions. + */ + + /* Limbu */ + case 0x1925u : *a = 0x1920u; *b= 0x1923u; return true; + case 0x1926u : *a = 0x1920u; *b= 0x1924u; return true; + + /* Balinese */ + case 0x1B3Cu : *a = 0x1B42u; *b= 0x1B3Cu; return true; + +#if 0 + /* Lepcha */ + case 0x1C29u : *a = no decomp, -> LEFT; return true; + + /* Javanese */ + case 0xA9C0u : *a = no decomp, -> RIGHT; return true; + + /* Sharada */ + case 0x111BFu : *a = no decomp, -> ABOVE; return true; +#endif + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_use (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + return (bool)c->unicode->compose (a, b, ab); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use = +{ + "use", + collect_features_use, + NULL, /* override_features */ + data_create_use, + data_destroy_use, + NULL, /* preprocess_text */ + NULL, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + decompose_use, + compose_use, + setup_masks_use, + NULL, /* disable_otl */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; diff --git a/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh b/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh new file mode 100644 index 000000000..e134224df --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh @@ -0,0 +1,53 @@ +/* + * 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_FALLBACK_PRIVATE_HH +#define HB_OT_SHAPE_FALLBACK_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-ot-shape-private.hh" + + +HB_INTERNAL void _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void _hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc new file mode 100644 index 000000000..ea8312b22 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc @@ -0,0 +1,553 @@ +/* + * 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 "hb-ot-shape-fallback-private.hh" +#include "hb-ot-layout-gsubgpos-private.hh" + +static unsigned int +recategorize_combining_class (hb_codepoint_t u, + unsigned int klass) +{ + if (klass >= 200) + return klass; + + /* Thai / Lao need some per-character work. */ + if ((u & ~0xFF) == 0x0E00u) + { + if (unlikely (klass == 0)) + { + switch (u) + { + case 0x0E31u: + case 0x0E34u: + case 0x0E35u: + case 0x0E36u: + case 0x0E37u: + case 0x0E47u: + case 0x0E4Cu: + case 0x0E4Du: + case 0x0E4Eu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + break; + + case 0x0EB1u: + case 0x0EB4u: + case 0x0EB5u: + case 0x0EB6u: + case 0x0EB7u: + case 0x0EBBu: + case 0x0ECCu: + case 0x0ECDu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE; + break; + + case 0x0EBCu: + klass = HB_UNICODE_COMBINING_CLASS_BELOW; + break; + } + } else { + /* Thai virama is below-right */ + if (u == 0x0E3Au) + klass = HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + } + } + + switch (klass) + { + + /* Hebrew */ + + case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */ + case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */ + case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */ + case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */ + case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats */ + case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */ + case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */ + return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */ + case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT; + + case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */ + break; + + + /* Arabic and Syriac */ + + case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */ + case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */ + case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */ + case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */ + case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */ + case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */ + case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + + /* Thai */ + + case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + + /* Lao */ + + case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + + /* Tibetan */ + + case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + } + + return klass; +} + +void +_hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + unsigned int combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + combining_class = recategorize_combining_class (info[i].codepoint, combining_class); + _hb_glyph_info_set_modified_combining_class (&info[i], combining_class); + } +} + + +static void +zero_mark_advances (hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + { + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + } +} + +static inline void +position_mark (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + hb_glyph_extents_t &base_extents, + unsigned int i, + unsigned int combining_class) +{ + hb_glyph_extents_t mark_extents; + if (!font->get_glyph_extents (buffer->info[i].codepoint, + &mark_extents)) + return; + + hb_position_t y_gap = font->y_scale / 16; + + hb_glyph_position_t &pos = buffer->pos[i]; + pos.x_offset = pos.y_offset = 0; + + + /* We dont position LEFT and RIGHT marks. */ + + /* X positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + if (buffer->props.direction == HB_DIRECTION_LTR) { + pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } else if (buffer->props.direction == HB_DIRECTION_RTL) { + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } + HB_FALLTHROUGH; + + default: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + /* Center align. */ + pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + /* Left align. */ + pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Right align. */ + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing; + break; + } + + /* Y positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + /* Add gap, fall-through. */ + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + pos.y_offset = base_extents.y_bearing + base_extents.height - mark_extents.y_bearing; + /* Never shift up "below" marks. */ + if ((y_gap > 0) == (pos.y_offset > 0)) + { + base_extents.height -= pos.y_offset; + pos.y_offset = 0; + } + base_extents.height += mark_extents.height; + break; + + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Add gap, fall-through. */ + base_extents.y_bearing += y_gap; + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + pos.y_offset = base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height); + /* Don't shift down "above" marks too much. */ + if ((y_gap > 0) != (pos.y_offset > 0)) + { + unsigned int correction = -pos.y_offset / 2; + base_extents.y_bearing += correction; + base_extents.height -= correction; + pos.y_offset += correction; + } + base_extents.y_bearing -= mark_extents.height; + base_extents.height += mark_extents.height; + break; + } +} + +static inline void +position_around_base (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int base, + unsigned int end) +{ + hb_direction_t horiz_dir = HB_DIRECTION_INVALID; + hb_glyph_extents_t base_extents; + if (!font->get_glyph_extents (buffer->info[base].codepoint, + &base_extents)) + { + /* If extents don't work, zero marks and go home. */ + zero_mark_advances (buffer, base + 1, end); + return; + } + base_extents.x_bearing += buffer->pos[base].x_offset; + base_extents.y_bearing += buffer->pos[base].y_offset; + + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[base]); + unsigned int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]); + + hb_position_t x_offset = 0, y_offset = 0; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[base].x_advance; + y_offset -= buffer->pos[base].y_advance; + } + + hb_glyph_extents_t component_extents = base_extents; + unsigned int last_lig_component = (unsigned int) -1; + unsigned int last_combining_class = 255; + hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = base + 1; i < end; i++) + if (_hb_glyph_info_get_modified_combining_class (&info[i])) + { + if (num_lig_components > 1) { + unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&info[i]); + unsigned int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1; + /* Conditions for attaching to the last component. */ + if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components) + this_lig_component = num_lig_components - 1; + if (last_lig_component != this_lig_component) + { + last_lig_component = this_lig_component; + last_combining_class = 255; + component_extents = base_extents; + if (unlikely (horiz_dir == HB_DIRECTION_INVALID)) { + if (HB_DIRECTION_IS_HORIZONTAL (plan->props.direction)) + horiz_dir = plan->props.direction; + else + horiz_dir = hb_script_get_horizontal_direction (plan->props.script); + } + if (horiz_dir == HB_DIRECTION_LTR) + component_extents.x_bearing += (this_lig_component * component_extents.width) / num_lig_components; + else + component_extents.x_bearing += ((num_lig_components - 1 - this_lig_component) * component_extents.width) / num_lig_components; + component_extents.width /= num_lig_components; + } + } + + unsigned int this_combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + if (last_combining_class != this_combining_class) + { + last_combining_class = this_combining_class; + cluster_extents = component_extents; + } + + position_mark (plan, font, buffer, cluster_extents, i, this_combining_class); + + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + buffer->pos[i].x_offset += x_offset; + buffer->pos[i].y_offset += y_offset; + + } else { + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[i].x_advance; + y_offset -= buffer->pos[i].y_advance; + } else { + x_offset += buffer->pos[i].x_advance; + y_offset += buffer->pos[i].y_advance; + } + } +} + +static inline void +position_cluster (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + if (end - start < 2) + return; + + /* Find the base glyph */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))) + { + /* Find mark glyphs */ + unsigned int j; + for (j = i + 1; j < end; j++) + if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[j]))) + break; + + position_around_base (plan, font, buffer, i, j); + + i = j - 1; + } +} + +void +_hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int start = 0; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) { + position_cluster (plan, font, buffer, start, i); + start = i; + } + position_cluster (plan, font, buffer, start, count); +} + + +/* Performs old-style TrueType kerning. */ +void +_hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (!plan->has_kern) return; + + OT::hb_apply_context_t c (1, font, buffer); + c.set_lookup_mask (plan->kern_mask); + c.set_lookup_props (OT::LookupFlag::IgnoreMarks); + OT::hb_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input; + skippy_iter.init (&c); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int idx = 0; idx < count;) + { + skippy_iter.reset (idx, 1); + if (!skippy_iter.next ()) + { + idx++; + continue; + } + + hb_position_t x_kern, y_kern; + font->get_glyph_kerning_for_direction (info[idx].codepoint, + info[skippy_iter.idx].codepoint, + buffer->props.direction, + &x_kern, &y_kern); + + if (x_kern) + { + hb_position_t kern1 = x_kern >> 1; + hb_position_t kern2 = x_kern - kern1; + pos[idx].x_advance += kern1; + pos[skippy_iter.idx].x_advance += kern2; + pos[skippy_iter.idx].x_offset += kern2; + } + + if (y_kern) + { + hb_position_t kern1 = y_kern >> 1; + hb_position_t kern2 = y_kern - kern1; + pos[idx].y_advance += kern1; + pos[skippy_iter.idx].y_advance += kern2; + pos[skippy_iter.idx].y_offset += kern2; + } + + idx = skippy_iter.idx; + } +} + + +/* Adjusts width of various spaces. */ +void +_hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (!HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + return; + + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i])) + { + hb_unicode_funcs_t::space_t space_type = _hb_glyph_info_get_unicode_space_fallback_type (&info[i]); + hb_codepoint_t glyph; + typedef hb_unicode_funcs_t t; + switch (space_type) + { + case t::NOT_SPACE: /* Shouldn't happen. */ + case t::SPACE: + break; + + case t::SPACE_EM: + case t::SPACE_EM_2: + case t::SPACE_EM_3: + case t::SPACE_EM_4: + case t::SPACE_EM_5: + case t::SPACE_EM_6: + case t::SPACE_EM_16: + pos[i].x_advance = (font->x_scale + ((int) space_type)/2) / (int) space_type; + break; + + case t::SPACE_4_EM_18: + pos[i].x_advance = font->x_scale * 4 / 18; + break; + + case t::SPACE_FIGURE: + for (char u = '0'; u <= '9'; u++) + if (font->get_nominal_glyph (u, &glyph)) + { + pos[i].x_advance = font->get_glyph_h_advance (glyph); + break; + } + break; + + case t::SPACE_PUNCTUATION: + if (font->get_nominal_glyph ('.', &glyph)) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + else if (font->get_nominal_glyph (',', &glyph)) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + break; + + case t::SPACE_NARROW: + /* Half-space? + * Unicode doc http://www.unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM. + * However, in my testing, many fonts have their regular space being about that + * size. To me, a percentage of the space width makes more sense. Half is as + * good as any. */ + pos[i].x_advance /= 2; + break; + } + } +} diff --git a/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh b/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh new file mode 100644 index 000000000..c744e2645 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh @@ -0,0 +1,69 @@ +/* + * 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" + + +/* buffer var allocations, used during the normalization process */ +#define glyph_index() var1.u32 + +struct hb_ot_shape_plan_t; + +enum hb_ot_shape_normalization_mode_t { + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED, + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */ + + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS +}; + +HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, + hb_buffer_t *buffer, + hb_font_t *font); + + +struct hb_ot_shape_normalize_context_t +{ + const hb_ot_shape_plan_t *plan; + hb_buffer_t *buffer; + hb_font_t *font; + hb_unicode_funcs_t *unicode; + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); +}; + + +#endif /* HB_OT_SHAPE_NORMALIZE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc new file mode 100644 index 000000000..107617e81 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc @@ -0,0 +1,415 @@ +/* + * 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 "hb-ot-shape-normalize-private.hh" +#include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-private.hh" + + +/* + * HIGHLEVEL DESIGN: + * + * This file exports one main function: _hb_ot_shape_normalize(). + * + * This function closely reflects the Unicode Normalization Algorithm, + * 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 desired, + * so far it's like Unicode Normalization. However, the decomposition and + * recomposition only happens if the font supports the resulting characters. + * + * The goals are: + * + * - Try to render all canonically equivalent strings similarly. To really + * achieve this we have to always do the full decomposition and then + * selectively recompose from there. It's kinda too expensive though, so + * we skip some cases. For example, if composed is desired, we simply + * don't touch 1-character clusters that are supported by the font, even + * 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, 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 canonical + * decomposition, well, use the decomposition. + * + * - The complex shapers can customize the compose and decompose functions to + * offload some of their requirements to the normalizer. For example, the + * Indic shaper may want to disallow recomposing of two matras. + */ + +static bool +decompose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return (bool) c->unicode->compose (a, b, ab); +} + +static inline void +set_glyph (hb_glyph_info_t &info, hb_font_t *font) +{ + font->get_nominal_glyph (info.codepoint, &info.glyph_index()); +} + +static inline void +output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) +{ + buffer->cur().glyph_index() = glyph; + buffer->output_glyph (unichar); /* This is very confusing indeed. */ + _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer); +} + +static inline void +next_char (hb_buffer_t *buffer, hb_codepoint_t glyph) +{ + buffer->cur().glyph_index() = glyph; + buffer->next_glyph (); +} + +static inline void +skip_char (hb_buffer_t *buffer) +{ + buffer->skip_glyph (); +} + +/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ +static inline unsigned int +decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab) +{ + hb_codepoint_t a, b, a_glyph, b_glyph; + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + + if (!c->decompose (c, ab, &a, &b) || + (b && !font->get_nominal_glyph (b, &b_glyph))) + return 0; + + bool has_a = (bool) font->get_nominal_glyph (a, &a_glyph); + if (shortest && has_a) { + /* Output a and b */ + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + unsigned int ret; + if ((ret = decompose (c, shortest, a))) { + if (b) { + output_char (buffer, b, b_glyph); + return ret + 1; + } + return ret; + } + + if (has_a) { + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + return 0; +} + +static inline void +decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest) +{ + hb_buffer_t * const buffer = c->buffer; + hb_codepoint_t u = buffer->cur().codepoint; + hb_codepoint_t glyph; + + if (shortest && c->font->get_nominal_glyph (u, &glyph)) + { + next_char (buffer, glyph); + return; + } + + if (decompose (c, shortest, u)) + { + skip_char (buffer); + return; + } + + if (!shortest && c->font->get_nominal_glyph (u, &glyph)) + { + next_char (buffer, glyph); + return; + } + + if (_hb_glyph_info_is_unicode_space (&buffer->cur())) + { + hb_codepoint_t space_glyph; + hb_unicode_funcs_t::space_t space_type = buffer->unicode->space_fallback_type (u); + if (space_type != hb_unicode_funcs_t::NOT_SPACE && c->font->get_nominal_glyph (0x0020u, &space_glyph)) + { + _hb_glyph_info_set_unicode_space_fallback_type (&buffer->cur(), space_type); + next_char (buffer, space_glyph); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK; + return; + } + } + + if (u == 0x2011u) + { + /* U+2011 is the only sensible character that is a no-break version of another character + * and not a space. The space ones are handled already. Handle this lone one. */ + hb_codepoint_t other_glyph; + if (c->font->get_nominal_glyph (0x2010u, &other_glyph)) + { + next_char (buffer, other_glyph); + return; + } + } + + next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ +} + +static inline void +handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit) +{ + /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */ + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + for (; buffer->idx < end - 1 && !buffer->in_error;) { + if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { + /* The next two lines are some ugly lines... But work. */ + if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) + { + buffer->replace_glyphs (2, 1, &buffer->cur().codepoint); + } + else + { + /* Just pass on the two characters separately, let GSUB do its magic. */ + set_glyph (buffer->cur(), font); + buffer->next_glyph (); + set_glyph (buffer->cur(), font); + buffer->next_glyph (); + } + /* Skip any further variation selectors. */ + while (buffer->idx < end && unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint))) + { + set_glyph (buffer->cur(), font); + buffer->next_glyph (); + } + } else { + set_glyph (buffer->cur(), font); + buffer->next_glyph (); + } + } + if (likely (buffer->idx < end)) { + set_glyph (buffer->cur(), font); + buffer->next_glyph (); + } +} + +static inline void +decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit) +{ + hb_buffer_t * const buffer = c->buffer; + for (unsigned int i = buffer->idx; i < end && !buffer->in_error; i++) + if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) { + handle_variation_selector_cluster (c, end, short_circuit); + return; + } + + while (buffer->idx < end && !buffer->in_error) + decompose_current_character (c, short_circuit); +} + +static inline void +decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit) +{ + if (likely (c->buffer->idx + 1 == end)) + decompose_current_character (c, might_short_circuit); + else + decompose_multi_char_cluster (c, end, always_short_circuit); +} + + +static int +compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + 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 (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (unlikely (!buffer->len)) return; + + _hb_buffer_assert_unicode_vars (buffer); + + hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference; + const hb_ot_shape_normalize_context_t c = { + plan, + buffer, + font, + buffer->unicode, + plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, + plan->shaper->compose ? plan->shaper->compose : compose_unicode + }; + + bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE; + bool might_short_circuit = always_short_circuit || + (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && + mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT); + unsigned int count; + + /* We do a fairly straightforward yet custom normalization process in three + * separate rounds: decompose, reorder, recompose (if desired). Currently + * this makes two buffer swaps. We can make it faster by moving the last + * two rounds into the inner loop for the first round, but it's more readable + * this way. */ + + + /* First round, decompose */ + + buffer->clear_output (); + count = buffer->len; + for (buffer->idx = 0; buffer->idx < count && !buffer->in_error;) + { + unsigned int end; + for (end = buffer->idx + 1; end < count; end++) + if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) + break; + + decompose_cluster (&c, end, might_short_circuit, always_short_circuit); + } + buffer->swap_buffers (); + + + /* Second round, reorder (inplace) */ + + count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) + continue; + + unsigned int end; + for (end = i + 1; end < count; end++) + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) + break; + + /* We are going to do a O(n^2). Only do this if the sequence is short. */ + if (end - i > 10) { + i = end; + continue; + } + + buffer->sort (i, end, compare_combining_class); + + i = end; + } + + + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED) + return; + + /* Third round, recompose */ + + /* As noted in the comment earlier, we don't try to combine + * ccc=0 chars with their previous Starter. */ + + buffer->clear_output (); + count = buffer->len; + unsigned int starter = 0; + buffer->next_glyph (); + while (buffer->idx < count && !buffer->in_error) + { + hb_codepoint_t composed, glyph; + if (/* We don't try to compose a non-mark character with it's preceding starter. + * This is both an optimization to avoid trying to compose every two neighboring + * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul + * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ + HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())) && + /* 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. */ + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + font->get_nominal_glyph (composed, &glyph)) + { + /* Composes. */ + buffer->next_glyph (); /* Copy to out-buffer. */ + if (unlikely (buffer->in_error)) + return; + buffer->merge_out_clusters (starter, buffer->out_len); + buffer->out_len--; /* Remove the second composable. */ + /* Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + buffer->out_info[starter].glyph_index() = glyph; + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); + + continue; + } + + /* Blocked, or doesn't compose. */ + buffer->next_glyph (); + + if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) + starter = buffer->out_len - 1; + } + buffer->swap_buffers (); + +} diff --git a/gfx/harfbuzz/src/hb-ot-shape-private.hh b/gfx/harfbuzz/src/hb-ot-shape-private.hh new file mode 100644 index 000000000..594e54c02 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh @@ -0,0 +1,106 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_PRIVATE_HH +#define HB_OT_SHAPE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-ot-map-private.hh" +#include "hb-ot-layout-private.hh" + + + +struct hb_ot_shape_plan_t +{ + hb_segment_properties_t props; + const struct hb_ot_complex_shaper_t *shaper; + hb_ot_map_t map; + const void *data; + hb_mask_t rtlm_mask, frac_mask, numr_mask, dnom_mask; + hb_mask_t kern_mask; + unsigned int has_frac : 1; + unsigned int has_kern : 1; + unsigned int has_mark : 1; + + inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const + { + unsigned int table_index; + switch (table_tag) { + case HB_OT_TAG_GSUB: table_index = 0; break; + case HB_OT_TAG_GPOS: table_index = 1; break; + default: return; + } + map.collect_lookups (table_index, lookups); + } + inline void substitute (hb_font_t *font, hb_buffer_t *buffer) const { map.substitute (this, font, buffer); } + inline void position (hb_font_t *font, hb_buffer_t *buffer) const { map.position (this, font, buffer); } + + void finish (void) { map.finish (); } +}; + +struct hb_ot_shape_planner_t +{ + /* In the order that they are filled in. */ + hb_face_t *face; + hb_segment_properties_t props; + const struct hb_ot_complex_shaper_t *shaper; + hb_ot_map_builder_t map; + + hb_ot_shape_planner_t (const hb_shape_plan_t *master_plan) : + face (master_plan->face_unsafe), + props (master_plan->props), + shaper (NULL), + map (face, &props) {} + ~hb_ot_shape_planner_t (void) { map.finish (); } + + inline void compile (hb_ot_shape_plan_t &plan, + const int *coords, + unsigned int num_coords) + { + plan.props = props; + plan.shaper = shaper; + map.compile (plan.map, coords, num_coords); + + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); + plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); + plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); + + plan.kern_mask = plan.map.get_mask (HB_DIRECTION_IS_HORIZONTAL (plan.props.direction) ? + HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n')); + + plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); + plan.has_kern = !!plan.kern_mask; + plan.has_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); + } + + private: + NO_COPY (hb_ot_shape_planner_t); +}; + + +#endif /* HB_OT_SHAPE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape.cc b/gfx/harfbuzz/src/hb-ot-shape.cc new file mode 100644 index 000000000..ddd6662e8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -0,0 +1,910 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#define HB_SHAPER ot +#define hb_ot_shaper_face_data_t hb_ot_layout_t +#define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t +#include "hb-shaper-impl-private.hh" + +#include "hb-ot-shape-private.hh" +#include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-fallback-private.hh" +#include "hb-ot-shape-normalize-private.hh" + +#include "hb-ot-layout-private.hh" +#include "hb-unicode-private.hh" +#include "hb-set-private.hh" + + +static hb_tag_t common_features[] = { + HB_TAG('c','c','m','p'), + HB_TAG('l','o','c','l'), + HB_TAG('m','a','r','k'), + HB_TAG('m','k','m','k'), + HB_TAG('r','l','i','g'), +}; + + +static 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'), + HB_TAG('r','c','l','t'), +}; + + + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features) +{ + hb_ot_map_builder_t *map = &planner->map; + + map->add_global_bool_feature (HB_TAG('r','v','r','n')); + map->add_gsub_pause (NULL); + + switch (props->direction) { + case HB_DIRECTION_LTR: + map->add_global_bool_feature (HB_TAG ('l','t','r','a')); + map->add_global_bool_feature (HB_TAG ('l','t','r','m')); + break; + case HB_DIRECTION_RTL: + map->add_global_bool_feature (HB_TAG ('r','t','l','a')); + map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE); + break; + case HB_DIRECTION_TTB: + case HB_DIRECTION_BTT: + case HB_DIRECTION_INVALID: + default: + break; + } + + map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE); + map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE); + map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE); + + if (planner->shaper->collect_features) + planner->shaper->collect_features (planner); + + for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) + map->add_global_bool_feature (common_features[i]); + + if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) + for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) + map->add_feature (horizontal_features[i], 1, F_GLOBAL | + (horizontal_features[i] == HB_TAG('k','e','r','n') ? + F_HAS_FALLBACK : F_NONE)); + else + { + /* We really want to find a 'vert' feature if there's any in the font, no + * matter which script/langsys it is listed (or not) under. + * See various bugs referenced from: + * https://github.com/behdad/harfbuzz/issues/63 */ + map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH); + } + + if (planner->shaper->override_features) + planner->shaper->override_features (planner); + + for (unsigned int i = 0; i < num_user_features; i++) { + const hb_feature_t *feature = &user_features[i]; + map->add_feature (feature->tag, feature->value, + (feature->start == 0 && feature->end == (unsigned int) -1) ? + F_GLOBAL : F_NONE); + } +} + + +/* + * shaper face data + */ + +hb_ot_shaper_face_data_t * +_hb_ot_shaper_face_data_create (hb_face_t *face) +{ + return _hb_ot_layout_create (face); +} + +void +_hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) +{ + _hb_ot_layout_destroy (data); +} + + +/* + * shaper font data + */ + +struct hb_ot_shaper_font_data_t {}; + +hb_ot_shaper_font_data_t * +_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data) +{ +} + + +/* + * shaper shape_plan data + */ + +hb_ot_shaper_shape_plan_data_t * +_hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords) +{ + hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t)); + if (unlikely (!plan)) + return NULL; + + hb_ot_shape_planner_t planner (shape_plan); + + planner.shaper = hb_ot_shape_complex_categorize (&planner); + + hb_ot_shape_collect_features (&planner, &shape_plan->props, + user_features, num_user_features); + + planner.compile (*plan, coords, num_coords); + + if (plan->shaper->data_create) { + plan->data = plan->shaper->data_create (plan); + if (unlikely (!plan->data)) + return NULL; + } + + return plan; +} + +void +_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan) +{ + if (plan->shaper->data_destroy) + plan->shaper->data_destroy (const_cast<void *> (plan->data)); + + plan->finish (); + + free (plan); +} + + +/* + * shaper + */ + +struct hb_ot_shape_context_t +{ + 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 */ + bool fallback_positioning; + bool fallback_glyph_classes; + hb_direction_t target_direction; +}; + + + +/* Main shaper */ + + +/* Prepare */ + +static void +hb_set_unicode_props (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_set_unicode_props (&info[i], buffer); +} + +static void +hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) +{ + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || + buffer->context_len[0] || + _hb_glyph_info_get_general_category (&buffer->info[0]) != + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + return; + + if (!font->has_glyph (0x25CCu)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); + + buffer->clear_output (); + + buffer->idx = 0; + hb_glyph_info_t info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + buffer->output_info (info); + while (buffer->idx < buffer->len && !buffer->in_error) + buffer->next_glyph (); + + buffer->swap_buffers (); +} + +static void +hb_form_clusters (hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + return; + + /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */ + unsigned int base = 0; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + { + if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) && + !_hb_glyph_info_is_joiner (&info[i]))) + { + buffer->merge_clusters (base, i); + base = i; + } + } + buffer->merge_clusters (base, count); +} + +static void +hb_ensure_native_direction (hb_buffer_t *buffer) +{ + hb_direction_t direction = buffer->props.direction; + + /* TODO vertical: + * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType + * Ogham fonts are supposed to be implemented BTT or not. Need to research that + * first. */ + if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || + (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) + { + /* Same loop as hb_form_clusters(). + * Since form_clusters() merged clusters already, we don't merge. */ + unsigned int base = 0; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + { + if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) + { + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + buffer->merge_clusters (base, i); + buffer->reverse_range (base, i); + + base = i; + } + } + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + buffer->merge_clusters (base, count); + buffer->reverse_range (base, count); + + buffer->reverse (); + + buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); + } +} + + +/* Substitute */ + +static inline void +hb_ot_mirror_chars (hb_ot_shape_context_t *c) +{ + if (HB_DIRECTION_IS_FORWARD (c->target_direction)) + return; + + hb_buffer_t *buffer = c->buffer; + hb_unicode_funcs_t *unicode = buffer->unicode; + hb_mask_t rtlm_mask = c->plan->rtlm_mask; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint))) + info[i].mask |= rtlm_mask; + else + info[i].codepoint = codepoint; + } +} + +static inline void +hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) +{ + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + !c->plan->has_frac) + return; + + hb_buffer_t *buffer = c->buffer; + + /* TODO look in pre/post context text also. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ + { + unsigned int start = i, end = i + 1; + while (start && + _hb_glyph_info_get_general_category (&info[start - 1]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + start--; + while (end < count && + _hb_glyph_info_get_general_category (&info[end]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + end++; + + for (unsigned int j = start; j < i; j++) + info[j].mask |= c->plan->numr_mask | c->plan->frac_mask; + info[i].mask |= c->plan->frac_mask; + for (unsigned int j = i + 1; j < end; j++) + info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask; + + i = end - 1; + } + } +} + +static inline void +hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_mask_t global_mask = map->get_global_mask (); + buffer->reset_masks (global_mask); +} + +static inline void +hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_ot_shape_setup_masks_fraction (c); + + if (c->plan->shaper->setup_masks) + c->plan->shaper->setup_masks (c->plan, buffer, c->font); + + for (unsigned int i = 0; i < c->num_user_features; i++) + { + const hb_feature_t *feature = &c->user_features[i]; + if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { + unsigned int shift; + hb_mask_t mask = map->get_mask (feature->tag, &shift); + buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); + } + } +} + +static void +hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int i = 0; + for (i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static void +hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int i = 0; + for (i = 0; i < count; i++) + { + if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) + break; + } + + /* No default-ignorables found; return. */ + if (i == count) + return; + + hb_codepoint_t space; + if (c->font->get_nominal_glyph (' ', &space)) + { + /* Replace default-ignorables with a zero-advance space glyph. */ + for (/*continue*/; i < count; i++) + { + if (_hb_glyph_info_is_default_ignorable (&info[i])) + info[i].codepoint = space; + } + } + else + { + /* Merge clusters and delete default-ignorables. + * NOTE! We can't use out-buffer as we have positioning data. */ + unsigned int j = i; + for (; i < count; i++) + { + if (_hb_glyph_info_is_default_ignorable (&info[i])) + { + /* Merge clusters. + * Same logic as buffer->delete_glyph(), but for in-place removal. */ + + unsigned int cluster = info[i].cluster; + if (i + 1 < count && cluster == info[i + 1].cluster) + continue; /* Cluster survives; do nothing. */ + + if (j) + { + /* Merge cluster backward. */ + if (cluster < info[j - 1].cluster) + { + unsigned int old_cluster = info[j - 1].cluster; + for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) + info[k - 1].cluster = cluster; + } + continue; + } + + if (i + 1 < count) + buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */ + + continue; + } + + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; + } + j++; + } + buffer->len = j; + } +} + + +static inline void +hb_ot_map_glyphs_fast (hb_buffer_t *buffer) +{ + /* Normalization process sets up glyph_index(), we just copy it. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].codepoint = info[i].glyph_index(); + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; +} + +static inline void +hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) +{ + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + for (unsigned int i = 0; i < count; i++) + { + hb_ot_layout_glyph_props_flags_t klass; + + /* Never mark default-ignorables as marks. + * They won't get in the way of lookups anyway, + * but having them as mark will cause them to be skipped + * over if the lookup-flag says so, but at least for the + * Mongolian variation selectors, looks like Uniscribe + * marks them as non-mark. Some Mongolian fonts without + * GDEF rely on this. Another notable character that + * this applies to is COMBINING GRAPHEME JOINER. */ + klass = (_hb_glyph_info_get_general_category (&info[i]) != + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || + _hb_glyph_info_is_default_ignorable (&info[i])) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : + HB_OT_LAYOUT_GLYPH_PROPS_MARK; + _hb_glyph_info_set_glyph_props (&info[i], klass); + } +} + +static inline void +hb_ot_substitute_default (hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_shape_initialize_masks (c); + + hb_ot_mirror_chars (c); + + HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); + + _hb_ot_shape_normalize (c->plan, buffer, c->font); + + hb_ot_shape_setup_masks (c); + + /* This is unfortunate to go here, but necessary... */ + if (c->fallback_positioning) + _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer); + + hb_ot_map_glyphs_fast (buffer); + + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); +} + +static inline void +hb_ot_substitute_complex (hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_layout_substitute_start (c->font, buffer); + + if (!hb_ot_layout_has_glyph_classes (c->face)) + hb_synthesize_glyph_classes (c); + + c->plan->substitute (c->font, buffer); + + return; +} + +static inline void +hb_ot_substitute (hb_ot_shape_context_t *c) +{ + hb_ot_substitute_default (c); + + _hb_buffer_allocate_gsubgpos_vars (c->buffer); + + hb_ot_substitute_complex (c); +} + +/* Position */ + +static inline void +adjust_mark_offsets (hb_glyph_position_t *pos) +{ + pos->x_offset -= pos->x_advance; + pos->y_offset -= pos->y_advance; +} + +static inline void +zero_mark_width (hb_glyph_position_t *pos) +{ + pos->x_advance = 0; + pos->y_advance = 0; +} + +static inline void +zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_mark (&info[i])) + { + if (adjust_offsets) + adjust_mark_offsets (&buffer->pos[i]); + zero_mark_width (&buffer->pos[i]); + } +} + +static inline void +hb_ot_position_default (hb_ot_shape_context_t *c) +{ + hb_direction_t direction = c->buffer->props.direction; + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + { + for (unsigned int i = 0; i < count; i++) + pos[i].x_advance = c->font->get_glyph_h_advance (info[i].codepoint); + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + else + { + for (unsigned int i = 0; i < count; i++) + { + pos[i].y_advance = c->font->get_glyph_v_advance (info[i].codepoint); + c->font->subtract_glyph_v_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + } + if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) + _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); +} + +static inline void +hb_ot_position_complex (hb_ot_shape_context_t *c) +{ + hb_ot_layout_position_start (c->font, c->buffer); + + unsigned int count = c->buffer->len; + + /* If the font has no GPOS, AND, no fallback positioning will + * happen, AND, direction is forward, then when zeroing mark + * widths, we shift the mark with it, such that the mark + * is positioned hanging over the previous glyph. When + * direction is backward we don't shift and it will end up + * hanging over the next glyph after the final reordering. + * If fallback positinoing happens or GPOS is present, we don't + * care. + */ + bool adjust_offsets_when_zeroing = c->fallback_positioning && + !c->plan->shaper->fallback_position && + HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); + + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + break; + } + + if (likely (!c->fallback_positioning)) + { + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + /* Change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->add_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + c->plan->position (c->font, c->buffer); + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + } + + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + break; + } + + /* Finishing off GPOS has to follow a certain order. */ + hb_ot_layout_position_finish_advances (c->font, c->buffer); + hb_ot_zero_width_default_ignorables (c); + hb_ot_layout_position_finish_offsets (c->font, c->buffer); +} + +static inline void +hb_ot_position (hb_ot_shape_context_t *c) +{ + c->buffer->clear_positions (); + + hb_ot_position_default (c); + + hb_ot_position_complex (c); + + if (c->fallback_positioning && c->plan->shaper->fallback_position) + _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer); + + if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) + hb_buffer_reverse (c->buffer); + + /* Visual fallback goes here. */ + + if (c->fallback_positioning) + _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer); + + _hb_buffer_deallocate_gsubgpos_vars (c->buffer); +} + + +/* Pull it all together! */ + +static void +hb_ot_shape_internal (hb_ot_shape_context_t *c) +{ + c->buffer->deallocate_var_all (); + c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_EXPANSION_FACTOR))) + { + c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_EXPANSION_FACTOR, + (unsigned) HB_BUFFER_MAX_LEN_MIN); + } + + bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan); + //c->fallback_substitute = disable_otl || !hb_ot_layout_has_substitution (c->face); + c->fallback_positioning = disable_otl || !hb_ot_layout_has_positioning (c->face); + c->fallback_glyph_classes = disable_otl || !hb_ot_layout_has_glyph_classes (c->face); + + /* Save the original direction, we use it later. */ + c->target_direction = c->buffer->props.direction; + + _hb_buffer_allocate_unicode_vars (c->buffer); + + c->buffer->clear_output (); + + hb_set_unicode_props (c->buffer); + hb_insert_dotted_circle (c->buffer, c->font); + hb_form_clusters (c->buffer); + + hb_ensure_native_direction (c->buffer); + + if (c->plan->shaper->preprocess_text) + c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); + + hb_ot_substitute (c); + hb_ot_position (c); + + hb_ot_hide_default_ignorables (c); + + if (c->plan->shaper->postprocess_glyphs) + c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); + + _hb_buffer_deallocate_unicode_vars (c->buffer); + + c->buffer->props.direction = c->target_direction; + + c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + c->buffer->deallocate_var_all (); +} + + +hb_bool_t +_hb_ot_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; + hb_ot_shape_internal (&c); + + return true; +} + + +/** + * hb_ot_shape_plan_collect_lookups: + * + * Since: 0.9.7 + **/ +void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */) +{ + /* XXX Does the first part always succeed? */ + HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes); +} + + +/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ +static void +add_char (hb_font_t *font, + hb_unicode_funcs_t *unicode, + hb_bool_t mirror, + hb_codepoint_t u, + hb_set_t *glyphs) +{ + hb_codepoint_t glyph; + if (font->get_nominal_glyph (u, &glyph)) + glyphs->add (glyph); + if (mirror) + { + hb_codepoint_t m = unicode->mirroring (u); + if (m != u && font->get_nominal_glyph (m, &glyph)) + glyphs->add (glyph); + } +} + + +/** + * hb_ot_shape_glyphs_closure: + * + * Since: 0.9.2 + **/ +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; + + const char *shapers[] = {"ot", NULL}; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, + features, num_features, shapers); + + bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); + + hb_set_t lookups; + lookups.init (); + hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, &lookups); + + /* And find transitive closure. */ + hb_set_t copy; + copy.init (); + do { + copy.set (glyphs); + for (hb_codepoint_t lookup_index = -1; hb_set_next (&lookups, &lookup_index);) + hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs); + } while (!copy.is_equal (glyphs)); + + hb_shape_plan_destroy (shape_plan); +} diff --git a/gfx/harfbuzz/src/hb-ot-shape.h b/gfx/harfbuzz/src/hb-ot-shape.h new file mode 100644 index 000000000..7b1bcc063 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H_IN +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_SHAPE_H +#define HB_OT_SHAPE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/* TODO port to shape-plan / set. */ +HB_EXTERN 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_EXTERN void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */); + +HB_END_DECLS + +#endif /* HB_OT_SHAPE_H */ diff --git a/gfx/harfbuzz/src/hb-ot-tag.cc b/gfx/harfbuzz/src/hb-ot-tag.cc new file mode 100644 index 000000000..5f21ac096 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-tag.cc @@ -0,0 +1,1024 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#include "hb-private.hh" + +#include <string.h> + + + +/* hb_script_t */ + +static hb_tag_t +hb_ot_old_tag_from_script (hb_script_t script) +{ + /* This seems to be accurate as of end of 2012. */ + + switch ((hb_tag_t) script) { + case HB_SCRIPT_INVALID: return HB_OT_TAG_DEFAULT_SCRIPT; + + /* KATAKANA and HIRAGANA both map to 'kana' */ + case HB_SCRIPT_HIRAGANA: return HB_TAG('k','a','n','a'); + + /* Spaces at the end are preserved, unlike ISO 15924 */ + case HB_SCRIPT_LAO: return HB_TAG('l','a','o',' '); + case HB_SCRIPT_YI: return HB_TAG('y','i',' ',' '); + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: return HB_TAG('n','k','o',' '); + /* Unicode-5.1 additions */ + case HB_SCRIPT_VAI: return HB_TAG('v','a','i',' '); + /* Unicode-5.2 additions */ + /* Unicode-6.0 additions */ + } + + /* Else, just change first char to lowercase and return */ + return ((hb_tag_t) script) | 0x20000000u; +} + +static hb_script_t +hb_ot_old_tag_to_script (hb_tag_t tag) +{ + if (unlikely (tag == HB_OT_TAG_DEFAULT_SCRIPT)) + return HB_SCRIPT_INVALID; + + /* This side of the conversion is fully algorithmic. */ + + /* Any spaces at the end of the tag are replaced by repeating the last + * letter. Eg 'nko ' -> 'Nkoo' */ + if (unlikely ((tag & 0x0000FF00u) == 0x00002000u)) + tag |= (tag >> 8) & 0x0000FF00u; /* Copy second letter to third */ + if (unlikely ((tag & 0x000000FFu) == 0x00000020u)) + tag |= (tag >> 8) & 0x000000FFu; /* Copy third letter to fourth */ + + /* Change first char to uppercase and return */ + return (hb_script_t) (tag & ~0x20000000u); +} + +static hb_tag_t +hb_ot_new_tag_from_script (hb_script_t script) +{ + switch ((hb_tag_t) script) { + case HB_SCRIPT_BENGALI: return HB_TAG('b','n','g','2'); + case HB_SCRIPT_DEVANAGARI: return HB_TAG('d','e','v','2'); + case HB_SCRIPT_GUJARATI: return HB_TAG('g','j','r','2'); + case HB_SCRIPT_GURMUKHI: return HB_TAG('g','u','r','2'); + case HB_SCRIPT_KANNADA: return HB_TAG('k','n','d','2'); + case HB_SCRIPT_MALAYALAM: return HB_TAG('m','l','m','2'); + case HB_SCRIPT_ORIYA: return HB_TAG('o','r','y','2'); + case HB_SCRIPT_TAMIL: return HB_TAG('t','m','l','2'); + case HB_SCRIPT_TELUGU: return HB_TAG('t','e','l','2'); + case HB_SCRIPT_MYANMAR: return HB_TAG('m','y','m','2'); + } + + return HB_OT_TAG_DEFAULT_SCRIPT; +} + +static hb_script_t +hb_ot_new_tag_to_script (hb_tag_t tag) +{ + switch (tag) { + case HB_TAG('b','n','g','2'): return HB_SCRIPT_BENGALI; + case HB_TAG('d','e','v','2'): return HB_SCRIPT_DEVANAGARI; + case HB_TAG('g','j','r','2'): return HB_SCRIPT_GUJARATI; + case HB_TAG('g','u','r','2'): return HB_SCRIPT_GURMUKHI; + case HB_TAG('k','n','d','2'): return HB_SCRIPT_KANNADA; + case HB_TAG('m','l','m','2'): return HB_SCRIPT_MALAYALAM; + case HB_TAG('o','r','y','2'): return HB_SCRIPT_ORIYA; + case HB_TAG('t','m','l','2'): return HB_SCRIPT_TAMIL; + case HB_TAG('t','e','l','2'): return HB_SCRIPT_TELUGU; + case HB_TAG('m','y','m','2'): return HB_SCRIPT_MYANMAR; + } + + return HB_SCRIPT_UNKNOWN; +} + +/* + * Complete list at: + * https://www.microsoft.com/typography/otspec/scripttags.htm + * https://www.microsoft.com/typography/otspec160/scripttagsProposed.htm + * + * Most of the script tags are the same as the ISO 15924 tag but lowercased. + * So we just do that, and handle the exceptional cases in a switch. + */ + +void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2) +{ + hb_tag_t new_tag; + + *script_tag_2 = HB_OT_TAG_DEFAULT_SCRIPT; + *script_tag_1 = hb_ot_old_tag_from_script (script); + + new_tag = hb_ot_new_tag_from_script (script); + if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) { + *script_tag_2 = *script_tag_1; + *script_tag_1 = new_tag; + } +} + +hb_script_t +hb_ot_tag_to_script (hb_tag_t tag) +{ + if (unlikely ((tag & 0x000000FFu) == '2')) + return hb_ot_new_tag_to_script (tag); + + return hb_ot_old_tag_to_script (tag); +} + + +/* hb_language_t */ + +typedef struct { + char language[4]; + hb_tag_t tag; +} LangTag; + +/* + * Complete list at: + * http://www.microsoft.com/typography/otspec/languagetags.htm + * + * Generated by intersecting the OpenType language tag list from + * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from + * 2008-08-04, matching on name, and finally adjusted manually. + * + * Updated on 2012-12-07 with more research into remaining codes. + * + * Updated on 2013-11-23 based on usage in SIL and Microsoft fonts, + * the new proposal from Microsoft, and latest ISO 639-3 names. + * + * Some items still missing. Those are commented out at the end. + * Keep sorted for bsearch. + * + * Updated as of 2015-05-06: OT1.7 on MS website has some newer + * items that we don't have here, eg. Zazaki. This is the new + * items in OpenType 1.7 (red items), most of which we have: + * http://www.microsoft.com/typography/otspec170/languagetags.htm + */ + +static const LangTag ot_languages[] = { + {"aa", HB_TAG('A','F','R',' ')}, /* Afar */ + {"ab", HB_TAG('A','B','K',' ')}, /* Abkhazian */ + {"abq", HB_TAG('A','B','A',' ')}, /* Abaza */ + {"acf", HB_TAG('F','A','N',' ')}, /* French Antillean */ + {"ach", HB_TAG('A','C','H',' ')}, /* Acoli */ + {"acr", HB_TAG('A','C','R',' ')}, /* Achi */ + {"ada", HB_TAG('D','N','G',' ')}, /* Dangme */ + {"ady", HB_TAG('A','D','Y',' ')}, /* Adyghe */ + {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {"ahg", HB_TAG('A','G','W',' ')}, /* Agaw */ + {"aii", HB_TAG('S','W','A',' ')}, /* Swadaya Aramaic */ + {"aio", HB_TAG('A','I','O',' ')}, /* Aiton */ + {"aiw", HB_TAG('A','R','I',' ')}, /* Aari */ + {"ak", HB_TAG('T','W','I',' ')}, /* Akan [macrolanguage] */ + {"aka", HB_TAG('A','K','A',' ')}, /* Akan */ + {"alt", HB_TAG('A','L','T',' ')}, /* [Southern] Altai */ + {"am", HB_TAG('A','M','H',' ')}, /* Amharic */ + {"amf", HB_TAG('H','B','N',' ')}, /* Hammer-Banna */ + {"an", HB_TAG('A','R','G',' ')}, /* Aragonese */ + {"ang", HB_TAG('A','N','G',' ')}, /* Old English (ca. 450-1100) */ + {"ar", HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */ + {"arb", HB_TAG('A','R','A',' ')}, /* Standard Arabic */ + {"arn", HB_TAG('M','A','P',' ')}, /* Mapudungun */ + {"ary", HB_TAG('M','O','R',' ')}, /* Moroccan Arabic */ + {"as", HB_TAG('A','S','M',' ')}, /* Assamese */ + {"ast", HB_TAG('A','S','T',' ')}, /* Asturian/Asturleonese/Bable/Leonese */ + {"ath", HB_TAG('A','T','H',' ')}, /* Athapaskan [family] */ + {"atj", HB_TAG('R','C','R',' ')}, /* R-Cree */ + {"atv", HB_TAG('A','L','T',' ')}, /* [Northern] Altai */ + {"av", HB_TAG('A','V','R',' ')}, /* Avaric */ + {"awa", HB_TAG('A','W','A',' ')}, /* Awadhi */ + {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ + {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ + {"azb", HB_TAG('A','Z','B',' ')}, /* South Azerbaijani */ + {"azj", HB_TAG('A','Z','E',' ')}, /* North Azerbaijani */ + {"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */ + {"bad", HB_TAG('B','A','D','0')}, /* Banda */ + {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [family] */ + {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi [macrolangauge] */ + {"ban", HB_TAG('B','A','N',' ')}, /* Balinese */ + {"bar", HB_TAG('B','A','R',' ')}, /* Bavarian */ + {"bbc", HB_TAG('B','B','C',' ')}, /* Batak Toba */ + {"bci", HB_TAG('B','A','U',' ')}, /* Baoulé */ + {"bcl", HB_TAG('B','I','K',' ')}, /* Central Bikol */ + {"bcq", HB_TAG('B','C','H',' ')}, /* Bench */ + {"bdy", HB_TAG('B','D','Y',' ')}, /* Bandjalang */ + {"be", HB_TAG('B','E','L',' ')}, /* Belarusian */ + {"bem", HB_TAG('B','E','M',' ')}, /* Bemba (Zambia) */ + {"ber", HB_TAG('B','E','R',' ')}, /* Berber [family] */ + {"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */ + {"bft", HB_TAG('B','L','T',' ')}, /* Balti */ + {"bfu", HB_TAG('L','A','H',' ')}, /* Lahuli */ + {"bfy", HB_TAG('B','A','G',' ')}, /* Baghelkhandi */ + {"bg", HB_TAG('B','G','R',' ')}, /* Bulgarian */ + {"bgc", HB_TAG('B','G','C',' ')}, /* Haryanvi */ + {"bgq", HB_TAG('B','G','Q',' ')}, /* Bagri */ + {"bhb", HB_TAG('B','H','I',' ')}, /* Bhili */ + {"bhk", HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) */ + {"bho", HB_TAG('B','H','O',' ')}, /* Bhojpuri */ + {"bi", HB_TAG('B','I','S',' ')}, /* Bislama */ + {"bik", HB_TAG('B','I','K',' ')}, /* Bikol [macrolanguage] */ + {"bin", HB_TAG('E','D','O',' ')}, /* Bini */ + {"bjj", HB_TAG('B','J','J',' ')}, /* Kanauji */ + {"bjt", HB_TAG('B','L','N',' ')}, /* Balanta-Ganja */ + {"bla", HB_TAG('B','K','F',' ')}, /* Blackfoot */ + {"ble", HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe */ + {"blk", HB_TAG('B','L','K',' ')}, /* Pa'O/Pa'o Karen */ + {"bln", HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol */ + {"bm", HB_TAG('B','M','B',' ')}, /* Bambara */ + {"bn", HB_TAG('B','E','N',' ')}, /* Bengali */ + {"bo", HB_TAG('T','I','B',' ')}, /* Tibetan */ + {"bpy", HB_TAG('B','P','Y',' ')}, /* Bishnupriya */ + {"bqi", HB_TAG('L','R','C',' ')}, /* Bakhtiari */ + {"br", HB_TAG('B','R','E',' ')}, /* Breton */ + {"bra", HB_TAG('B','R','I',' ')}, /* Braj Bhasha */ + {"brh", HB_TAG('B','R','H',' ')}, /* Brahui */ + {"brx", HB_TAG('B','R','X',' ')}, /* Bodo (India) */ + {"bs", HB_TAG('B','O','S',' ')}, /* Bosnian */ + {"btb", HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) */ + {"bto", HB_TAG('B','I','K',' ')}, /* Rinconada Bikol */ + {"bts", HB_TAG('B','T','S',' ')}, /* Batak Simalungun */ + {"bug", HB_TAG('B','U','G',' ')}, /* Buginese */ + {"bxr", HB_TAG('R','B','U',' ')}, /* Russian Buriat */ + {"byn", HB_TAG('B','I','L',' ')}, /* Bilen */ + {"ca", HB_TAG('C','A','T',' ')}, /* Catalan */ + {"cak", HB_TAG('C','A','K',' ')}, /* Kaqchikel */ + {"cbk", HB_TAG('C','B','K',' ')}, /* Chavacano */ + {"ce", HB_TAG('C','H','E',' ')}, /* Chechen */ + {"ceb", HB_TAG('C','E','B',' ')}, /* Cebuano */ + {"cgg", HB_TAG('C','G','G',' ')}, /* Chiga */ + {"ch", HB_TAG('C','H','A',' ')}, /* Chamorro */ + {"chk", HB_TAG('C','H','K','0')}, /* Chuukese */ + {"cho", HB_TAG('C','H','O',' ')}, /* Choctaw */ + {"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */ + {"chr", HB_TAG('C','H','R',' ')}, /* Cherokee */ + {"chy", HB_TAG('C','H','Y',' ')}, /* Cheyenne */ + {"ckb", HB_TAG('K','U','R',' ')}, /* Central Kurdish (Sorani) */ + {"ckt", HB_TAG('C','H','K',' ')}, /* Chukchi */ + {"cop", HB_TAG('C','O','P',' ')}, /* Coptic */ + {"cpp", HB_TAG('C','P','P',' ')}, /* Creoles */ + {"cr", HB_TAG('C','R','E',' ')}, /* Cree */ + {"cre", HB_TAG('Y','C','R',' ')}, /* Y-Cree */ + {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {"crj", HB_TAG('E','C','R',' ')}, /* [Southern] East Cree */ + {"crk", HB_TAG('W','C','R',' ')}, /* West-Cree */ + {"crl", HB_TAG('E','C','R',' ')}, /* [Northern] East Cree */ + {"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */ + {"crx", HB_TAG('C','R','R',' ')}, /* Carrier */ + {"cs", HB_TAG('C','S','Y',' ')}, /* Czech */ + {"csb", HB_TAG('C','S','B',' ')}, /* Kashubian */ + {"ctg", HB_TAG('C','T','G',' ')}, /* Chittagonian */ + {"cts", HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol */ + {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavic */ + {"cuk", HB_TAG('C','U','K',' ')}, /* San Blas Kuna */ + {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */ + {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */ + {"cy", HB_TAG('W','E','L',' ')}, /* Welsh */ + {"da", HB_TAG('D','A','N',' ')}, /* Danish */ + {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) */ + {"dar", HB_TAG('D','A','R',' ')}, /* Dargwa */ + {"dax", HB_TAG('D','A','X',' ')}, /* Dayi */ + {"de", HB_TAG('D','E','U',' ')}, /* German */ + {"dgo", HB_TAG('D','G','O',' ')}, /* Dogri */ + {"dhd", HB_TAG('M','A','W',' ')}, /* Dhundari */ + {"dhg", HB_TAG('D','H','G',' ')}, /* Dhangu */ + {"din", HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {"diq", HB_TAG('D','I','Q',' ')}, /* Dimli */ + {"dje", HB_TAG('D','J','R',' ')}, /* Zarma */ + {"djr", HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ + {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */ + {"dnj", HB_TAG('D','N','J',' ')}, /* Dan */ + {"doi", HB_TAG('D','G','R',' ')}, /* Dogri [macrolanguage] */ + {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ + {"duj", HB_TAG('D','U','J',' ')}, /* Dhuwal */ + {"dv", HB_TAG('D','I','V',' ')}, /* Dhivehi/Divehi/Maldivian */ + {"dyu", HB_TAG('J','U','L',' ')}, /* Jula */ + {"dz", HB_TAG('D','Z','N',' ')}, /* Dzongkha */ + {"ee", HB_TAG('E','W','E',' ')}, /* Ewe */ + {"efi", HB_TAG('E','F','I',' ')}, /* Efik */ + {"ekk", HB_TAG('E','T','I',' ')}, /* Standard Estonian */ + {"el", HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) */ + {"emk", HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan */ + {"en", HB_TAG('E','N','G',' ')}, /* English */ + {"enf", HB_TAG('F','N','E',' ')}, /* Forest Nenets */ + {"enh", HB_TAG('T','N','E',' ')}, /* Tundra Nenets */ + {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */ + {"eot", HB_TAG('B','T','I',' ')}, /* Beti (Côte d'Ivoire) */ + {"es", HB_TAG('E','S','P',' ')}, /* Spanish */ + {"esu", HB_TAG('E','S','U',' ')}, /* Central Yupik */ + {"et", HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */ + {"eu", HB_TAG('E','U','Q',' ')}, /* Basque */ + {"eve", HB_TAG('E','V','N',' ')}, /* Even */ + {"evn", HB_TAG('E','V','K',' ')}, /* Evenki */ + {"fa", HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {"fan", HB_TAG('F','A','N','0')}, /* Fang */ + {"fat", HB_TAG('F','A','T',' ')}, /* Fanti */ + {"ff", HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */ + {"fi", HB_TAG('F','I','N',' ')}, /* Finnish */ + {"fil", HB_TAG('P','I','L',' ')}, /* Filipino */ + {"fj", HB_TAG('F','J','I',' ')}, /* Fijian */ + {"flm", HB_TAG('H','A','L',' ')}, /* Halam */ + {"fo", HB_TAG('F','O','S',' ')}, /* Faroese */ + {"fon", HB_TAG('F','O','N',' ')}, /* Fon */ + {"fr", HB_TAG('F','R','A',' ')}, /* French */ + {"frc", HB_TAG('F','R','C',' ')}, /* Cajun French */ + {"frp", HB_TAG('F','R','P',' ')}, /* Arpitan/Francoprovençal */ + {"fuf", HB_TAG('F','T','A',' ')}, /* Futa */ + {"fur", HB_TAG('F','R','L',' ')}, /* Friulian */ + {"fuv", HB_TAG('F','U','V',' ')}, /* Nigerian Fulfulde */ + {"fy", HB_TAG('F','R','I',' ')}, /* Western Frisian */ + {"ga", HB_TAG('I','R','I',' ')}, /* Irish */ + {"gaa", HB_TAG('G','A','D',' ')}, /* Ga */ + {"gag", HB_TAG('G','A','G',' ')}, /* Gagauz */ + {"gbm", HB_TAG('G','A','W',' ')}, /* Garhwali */ + {"gd", HB_TAG('G','A','E',' ')}, /* Scottish Gaelic */ + {"gez", HB_TAG('G','E','Z',' ')}, /* Ge'ez */ + {"ggo", HB_TAG('G','O','N',' ')}, /* Southern Gondi */ + {"gih", HB_TAG('G','I','H',' ')}, /* Githabul */ + {"gil", HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ + {"gkp", HB_TAG('G','K','P',' ')}, /* Kpelle (Guinea) */ + {"gl", HB_TAG('G','A','L',' ')}, /* Galician */ + {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */ + {"gle", HB_TAG('I','R','T',' ')}, /* Irish Traditional */ + {"glk", HB_TAG('G','L','K',' ')}, /* Gilaki */ + {"gn", HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ + {"gnn", HB_TAG('G','N','N',' ')}, /* Gumatj */ + {"gno", HB_TAG('G','O','N',' ')}, /* Northern Gondi */ + {"gog", HB_TAG('G','O','G',' ')}, /* Gogo */ + {"gon", HB_TAG('G','O','N',' ')}, /* Gondi [macrolanguage] */ + {"grt", HB_TAG('G','R','O',' ')}, /* Garo */ + {"gru", HB_TAG('S','O','G',' ')}, /* Sodo Gurage */ + {"gsw", HB_TAG('A','L','S',' ')}, /* Alsatian */ + {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */ + {"guc", HB_TAG('G','U','C',' ')}, /* Wayuu */ + {"guf", HB_TAG('G','U','F',' ')}, /* Gupapuyngu */ + {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */ +/*{"guk", HB_TAG('G','U','K',' ')},*/ /* Gumuz (in SIL fonts) */ + {"guz", HB_TAG('G','U','Z',' ')}, /* Ekegusii/Gusii */ + {"gv", HB_TAG('M','N','X',' ')}, /* Manx */ + {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */ + {"har", HB_TAG('H','R','I',' ')}, /* Harari */ + {"haw", HB_TAG('H','A','W',' ')}, /* Hawaiian */ + {"hay", HB_TAG('H','A','Y',' ')}, /* Haya */ + {"haz", HB_TAG('H','A','Z',' ')}, /* Hazaragi */ + {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */ + {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */ + {"hil", HB_TAG('H','I','L',' ')}, /* Hiligaynon */ + {"hmn", HB_TAG('H','M','N',' ')}, /* Hmong */ + {"hnd", HB_TAG('H','N','D',' ')}, /* [Southern] Hindko */ + {"hne", HB_TAG('C','H','H',' ')}, /* Chattisgarhi */ + {"hno", HB_TAG('H','N','D',' ')}, /* [Northern] Hindko */ + {"ho", HB_TAG('H','M','O',' ')}, /* Hiri Motu */ + {"hoc", HB_TAG('H','O',' ',' ')}, /* Ho */ + {"hoj", HB_TAG('H','A','R',' ')}, /* Harauti */ + {"hr", HB_TAG('H','R','V',' ')}, /* Croatian */ + {"hsb", HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ + {"ht", HB_TAG('H','A','I',' ')}, /* Haitian/Haitian Creole */ + {"hu", HB_TAG('H','U','N',' ')}, /* Hungarian */ + {"hy", HB_TAG('H','Y','E',' ')}, /* Armenian */ + {"hz", HB_TAG('H','E','R',' ')}, /* Herero */ + {"ia", HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */ + {"iba", HB_TAG('I','B','A',' ')}, /* Iban */ + {"ibb", HB_TAG('I','B','B',' ')}, /* Ibibio */ + {"id", HB_TAG('I','N','D',' ')}, /* Indonesian */ + {"ie", HB_TAG('I','L','E',' ')}, /* Interlingue/Occidental */ + {"ig", HB_TAG('I','B','O',' ')}, /* Igbo */ + {"igb", HB_TAG('E','B','I',' ')}, /* Ebira */ + {"ii", HB_TAG('Y','I','M',' ')}, /* Yi Modern */ + {"ijc", HB_TAG('I','J','O',' ')}, /* Izon */ + {"ijo", HB_TAG('I','J','O',' ')}, /* Ijo [family] */ + {"ik", HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] */ + {"ilo", HB_TAG('I','L','O',' ')}, /* Ilokano */ + {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */ + {"io", HB_TAG('I','D','O',' ')}, /* Ido */ + {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */ + {"it", HB_TAG('I','T','A',' ')}, /* Italian */ + {"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */ + {"ja", HB_TAG('J','A','N',' ')}, /* Japanese */ + {"jam", HB_TAG('J','A','M',' ')}, /* Jamaican Creole English */ + {"jbo", HB_TAG('J','B','O',' ')}, /* Lojban */ + {"jv", HB_TAG('J','A','V',' ')}, /* Javanese */ + {"ka", HB_TAG('K','A','T',' ')}, /* Georgian */ + {"kaa", HB_TAG('K','R','K',' ')}, /* Karakalpak */ + {"kab", HB_TAG('K','A','B','0')}, /* Kabyle */ + {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */ + {"kat", HB_TAG('K','G','E',' ')}, /* Khutsuri Georgian */ + {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */ + {"kde", HB_TAG('K','D','E',' ')}, /* Makonde */ + {"kdr", HB_TAG('K','R','M',' ')}, /* Karaim */ + {"kdt", HB_TAG('K','U','Y',' ')}, /* Kuy */ + {"kea", HB_TAG('K','E','A',' ')}, /* Kabuverdianu (Crioulo) */ + {"kek", HB_TAG('K','E','K',' ')}, /* Kekchi */ + {"kex", HB_TAG('K','K','N',' ')}, /* Kokni */ + {"kfa", HB_TAG('K','O','D',' ')}, /* Kodagu */ + {"kfr", HB_TAG('K','A','C',' ')}, /* Kachchi */ + {"kfx", HB_TAG('K','U','L',' ')}, /* Kulvi */ + {"kfy", HB_TAG('K','M','N',' ')}, /* Kumaoni */ + {"kg", HB_TAG('K','O','N',' ')}, /* Kongo [macrolanguage] */ + {"kha", HB_TAG('K','S','I',' ')}, /* Khasi */ + {"khb", HB_TAG('X','B','D',' ')}, /* Lü */ + {"kht", HB_TAG('K','H','N',' ')}, /* Khamti (Microsoft fonts) */ +/*{"kht", HB_TAG('K','H','T',' ')},*/ /* Khamti (OpenType spec and SIL fonts) */ + {"khw", HB_TAG('K','H','W',' ')}, /* Khowar */ + {"ki", HB_TAG('K','I','K',' ')}, /* Gikuyu/Kikuyu */ + {"kiu", HB_TAG('K','I','U',' ')}, /* Kirmanjki */ + {"kj", HB_TAG('K','U','A',' ')}, /* Kuanyama/Kwanyama */ + {"kjd", HB_TAG('K','J','D',' ')}, /* Southern Kiwai */ + {"kjh", HB_TAG('K','H','A',' ')}, /* Khakass */ + {"kjp", HB_TAG('K','J','P',' ')}, /* Pwo Eastern Karen */ + {"kk", HB_TAG('K','A','Z',' ')}, /* Kazakh */ + {"kl", HB_TAG('G','R','N',' ')}, /* Kalaallisut */ + {"kln", HB_TAG('K','A','L',' ')}, /* Kalenjin */ + {"km", HB_TAG('K','H','M',' ')}, /* Central Khmer */ + {"kmb", HB_TAG('M','B','N',' ')}, /* Kimbundu */ + {"kmw", HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ + {"kn", HB_TAG('K','A','N',' ')}, /* Kannada */ + {"knn", HB_TAG('K','O','K',' ')}, /* Konkani */ + {"ko", HB_TAG('K','O','R',' ')}, /* Korean */ + {"koi", HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ + {"kok", HB_TAG('K','O','K',' ')}, /* Konkani [macrolanguage] */ + {"kon", HB_TAG('K','O','N','0')}, /* Kongo */ + {"kos", HB_TAG('K','O','S',' ')}, /* Kosraean */ + {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ + {"kpv", HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ + {"kpy", HB_TAG('K','Y','K',' ')}, /* Koryak */ + {"kqy", HB_TAG('K','R','T',' ')}, /* Koorete */ + {"kr", HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */ + {"kri", HB_TAG('K','R','I',' ')}, /* Krio */ + {"krl", HB_TAG('K','R','L',' ')}, /* Karelian */ + {"kru", HB_TAG('K','U','U',' ')}, /* Kurukh */ + {"ks", HB_TAG('K','S','H',' ')}, /* Kashmiri */ + {"ksh", HB_TAG('K','S','H','0')}, /* Ripuarian, Kölsch */ +/*{"ksw", HB_TAG('K','R','N',' ')},*/ /* S'gaw Karen (Microsoft fonts?) */ + {"ksw", HB_TAG('K','S','W',' ')}, /* S'gaw Karen (OpenType spec and SIL fonts) */ + {"ktb", HB_TAG('K','E','B',' ')}, /* Kebena */ + {"ktu", HB_TAG('K','O','N',' ')}, /* Kikongo */ + {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */ + {"kum", HB_TAG('K','U','M',' ')}, /* Kumyk */ + {"kv", HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */ + {"kvd", HB_TAG('K','U','I',' ')}, /* Kui (Indonesia) */ + {"kw", HB_TAG('C','O','R',' ')}, /* Cornish */ + {"kxc", HB_TAG('K','M','S',' ')}, /* Komso */ + {"kxu", HB_TAG('K','U','I',' ')}, /* Kui (India) */ + {"ky", HB_TAG('K','I','R',' ')}, /* Kirghiz/Kyrgyz */ + {"kyu", HB_TAG('K','Y','U',' ')}, /* Western Kayah */ + {"la", HB_TAG('L','A','T',' ')}, /* Latin */ + {"lad", HB_TAG('J','U','D',' ')}, /* Ladino */ + {"lb", HB_TAG('L','T','Z',' ')}, /* Luxembourgish */ + {"lbe", HB_TAG('L','A','K',' ')}, /* Lak */ + {"lbj", HB_TAG('L','D','K',' ')}, /* Ladakhi */ + {"lez", HB_TAG('L','E','Z',' ')}, /* Lezgi */ + {"lg", HB_TAG('L','U','G',' ')}, /* Ganda */ + {"li", HB_TAG('L','I','M',' ')}, /* Limburgan/Limburger/Limburgish */ + {"lif", HB_TAG('L','M','B',' ')}, /* Limbu */ + {"lij", HB_TAG('L','I','J',' ')}, /* Ligurian */ + {"lis", HB_TAG('L','I','S',' ')}, /* Lisu */ + {"ljp", HB_TAG('L','J','P',' ')}, /* Lampung Api */ + {"lki", HB_TAG('L','K','I',' ')}, /* Laki */ + {"lld", HB_TAG('L','A','D',' ')}, /* Ladin */ + {"lmn", HB_TAG('L','A','M',' ')}, /* Lambani */ + {"lmo", HB_TAG('L','M','O',' ')}, /* Lombard */ + {"ln", HB_TAG('L','I','N',' ')}, /* Lingala */ + {"lo", HB_TAG('L','A','O',' ')}, /* Lao */ + {"lom", HB_TAG('L','O','M',' ')}, /* Loma */ + {"lrc", HB_TAG('L','R','C',' ')}, /* Northern Luri */ + {"lt", HB_TAG('L','T','H',' ')}, /* Lithuanian */ + {"lu", HB_TAG('L','U','B',' ')}, /* Luba-Katanga */ + {"lua", HB_TAG('L','U','B',' ')}, /* Luba-Kasai */ + {"luo", HB_TAG('L','U','O',' ')}, /* Luo (Kenya and Tanzania) */ + {"lus", HB_TAG('M','I','Z',' ')}, /* Mizo */ + {"luy", HB_TAG('L','U','H',' ')}, /* Luyia/Oluluyia [macrolanguage] */ + {"luz", HB_TAG('L','R','C',' ')}, /* Southern Luri */ + {"lv", HB_TAG('L','V','I',' ')}, /* Latvian */ + {"lzz", HB_TAG('L','A','Z',' ')}, /* Laz */ + {"mad", HB_TAG('M','A','D',' ')}, /* Madurese */ + {"mag", HB_TAG('M','A','G',' ')}, /* Magahi */ + {"mai", HB_TAG('M','T','H',' ')}, /* Maithili */ + {"mak", HB_TAG('M','K','R',' ')}, /* Makasar */ + {"mal", HB_TAG('M','A','L',' ')}, /* Malayalam */ + {"mam", HB_TAG('M','A','M',' ')}, /* Mam */ + {"man", HB_TAG('M','N','K',' ')}, /* Manding/Mandingo [macrolanguage] */ + {"mdc", HB_TAG('M','L','E',' ')}, /* Male (Papua New Guinea) */ + {"mdf", HB_TAG('M','O','K',' ')}, /* Moksha */ + {"mdr", HB_TAG('M','D','R',' ')}, /* Mandar */ + {"mdy", HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ + {"men", HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ + {"mer", HB_TAG('M','E','R',' ')}, /* Meru */ + {"mfe", HB_TAG('M','F','E',' ')}, /* Morisyen */ + {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */ + {"mh", HB_TAG('M','A','H',' ')}, /* Marshallese */ + {"mhr", HB_TAG('L','M','A',' ')}, /* Low Mari */ + {"mi", HB_TAG('M','R','I',' ')}, /* Maori */ + {"min", HB_TAG('M','I','N',' ')}, /* Minangkabau */ + {"mk", HB_TAG('M','K','D',' ')}, /* Macedonian */ + {"mku", HB_TAG('M','N','K',' ')}, /* Konyanka Maninka */ + {"mkw", HB_TAG('M','K','W',' ')}, /* Kituba (Congo) */ + {"ml", HB_TAG('M','L','R',' ')}, /* Malayalam */ + {"mlq", HB_TAG('M','N','K',' ')}, /* Western Maninkakan */ + {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ + {"mnc", HB_TAG('M','C','H',' ')}, /* Manchu */ + {"mni", HB_TAG('M','N','I',' ')}, /* Manipuri */ + {"mnk", HB_TAG('M','N','D',' ')}, /* Mandinka */ + {"mns", HB_TAG('M','A','N',' ')}, /* Mansi */ + {"mnw", HB_TAG('M','O','N',' ')}, /* Mon */ + {"mo", HB_TAG('M','O','L',' ')}, /* Moldavian */ + {"moh", HB_TAG('M','O','H',' ')}, /* Mohawk */ + {"mos", HB_TAG('M','O','S',' ')}, /* Mossi */ + {"mpe", HB_TAG('M','A','J',' ')}, /* Majang */ + {"mr", HB_TAG('M','A','R',' ')}, /* Marathi */ + {"mrj", HB_TAG('H','M','A',' ')}, /* High Mari */ + {"ms", HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ + {"msc", HB_TAG('M','N','K',' ')}, /* Sankaran Maninka */ + {"mt", HB_TAG('M','T','S',' ')}, /* Maltese */ + {"mtr", HB_TAG('M','A','W',' ')}, /* Mewari */ + {"mus", HB_TAG('M','U','S',' ')}, /* Creek */ + {"mve", HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ + {"mwk", HB_TAG('M','N','K',' ')}, /* Kita Maninkakan */ + {"mwl", HB_TAG('M','W','L',' ')}, /* Mirandese */ + {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ + {"mww", HB_TAG('M','W','W',' ')}, /* Hmong Daw */ + {"my", HB_TAG('B','R','M',' ')}, /* Burmese */ + {"mym", HB_TAG('M','E','N',' ')}, /* Me'en */ + {"myn", HB_TAG('M','Y','N',' ')}, /* Mayan */ + {"myq", HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) */ + {"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */ + {"mzn", HB_TAG('M','Z','N',' ')}, /* Mazanderani */ + {"na", HB_TAG('N','A','U',' ')}, /* Nauru */ + {"nag", HB_TAG('N','A','G',' ')}, /* Naga-Assamese */ + {"nah", HB_TAG('N','A','H',' ')}, /* Nahuatl [family] */ + {"nap", HB_TAG('N','A','P',' ')}, /* Neapolitan */ + {"nb", HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål */ + {"nco", HB_TAG('S','I','B',' ')}, /* Sibe */ + {"nd", HB_TAG('N','D','B',' ')}, /* [North] Ndebele */ + {"ndc", HB_TAG('N','D','C',' ')}, /* Ndau */ + {"nds", HB_TAG('N','D','S',' ')}, /* Low German/Low Saxon */ + {"ne", HB_TAG('N','E','P',' ')}, /* Nepali */ + {"new", HB_TAG('N','E','W',' ')}, /* Newari */ + {"ng", HB_TAG('N','D','G',' ')}, /* Ndonga */ + {"nga", HB_TAG('N','G','A',' ')}, /* Ngabaka */ + {"ngl", HB_TAG('L','M','W',' ')}, /* Lomwe */ + {"ngo", HB_TAG('S','X','T',' ')}, /* Sutu */ + {"niu", HB_TAG('N','I','U',' ')}, /* Niuean */ + {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */ + {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */ + {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk */ + {"no", HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */ + {"nod", HB_TAG('N','T','A',' ')}, /* Northern Thai */ + {"noe", HB_TAG('N','O','E',' ')}, /* Nimadi */ + {"nog", HB_TAG('N','O','G',' ')}, /* Nogai */ + {"nov", HB_TAG('N','O','V',' ')}, /* Novial */ + {"nqo", HB_TAG('N','K','O',' ')}, /* N'Ko */ + {"nr", HB_TAG('N','D','B',' ')}, /* [South] Ndebele */ + {"nsk", HB_TAG('N','A','S',' ')}, /* Naskapi */ + {"nso", HB_TAG('S','O','T',' ')}, /* [Northern] Sotho */ + {"nv", HB_TAG('N','A','V',' ')}, /* Navajo */ + {"ny", HB_TAG('C','H','I',' ')}, /* Chewa/Chichwa/Nyanja */ + {"nym", HB_TAG('N','Y','M',' ')}, /* Nyamwezi */ + {"nyn", HB_TAG('N','K','L',' ')}, /* Nyankole */ + {"oc", HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ + {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] */ + {"ojs", HB_TAG('O','C','R',' ')}, /* Oji-Cree */ + {"okm", HB_TAG('K','O','H',' ')}, /* Korean Old Hangul */ + {"om", HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ + {"or", HB_TAG('O','R','I',' ')}, /* Oriya */ + {"os", HB_TAG('O','S','S',' ')}, /* Ossetian */ + {"pa", HB_TAG('P','A','N',' ')}, /* Panjabi */ + {"pag", HB_TAG('P','A','G',' ')}, /* Pangasinan */ + {"pam", HB_TAG('P','A','M',' ')}, /* Kapampangan/Pampanga */ + {"pap", HB_TAG('P','A','P','0')}, /* Papiamento */ + {"pau", HB_TAG('P','A','U',' ')}, /* Palauan */ + {"pcc", HB_TAG('P','C','C',' ')}, /* Bouyei */ + {"pcd", HB_TAG('P','C','D',' ')}, /* Picard */ + {"pce", HB_TAG('P','L','G',' ')}, /* [Ruching] Palaung */ + {"pdc", HB_TAG('P','D','C',' ')}, /* Pennsylvania German */ + {"pes", HB_TAG('F','A','R',' ')}, /* Iranian Persian */ + {"phk", HB_TAG('P','H','K',' ')}, /* Phake */ + {"pi", HB_TAG('P','A','L',' ')}, /* Pali */ + {"pih", HB_TAG('P','I','H',' ')}, /* Pitcairn-Norfolk */ + {"pl", HB_TAG('P','L','K',' ')}, /* Polish */ + {"pll", HB_TAG('P','L','G',' ')}, /* [Shwe] Palaung */ + {"plp", HB_TAG('P','A','P',' ')}, /* Palpa */ + {"pms", HB_TAG('P','M','S',' ')}, /* Piemontese */ + {"pnb", HB_TAG('P','N','B',' ')}, /* Western Panjabi */ + {"poh", HB_TAG('P','O','H',' ')}, /* Pocomchi */ + {"pon", HB_TAG('P','O','N',' ')}, /* Pohnpeian */ + {"prs", HB_TAG('D','R','I',' ')}, /* Afghan Persian/Dari */ + {"ps", HB_TAG('P','A','S',' ')}, /* Pashto/Pushto [macrolanguage] */ + {"pt", HB_TAG('P','T','G',' ')}, /* Portuguese */ + {"pwo", HB_TAG('P','W','O',' ')}, /* Pwo Western Karen */ + {"qu", HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */ + {"quc", HB_TAG('Q','U','C',' ')}, /* K'iche'/Quiché */ + {"quh", HB_TAG('Q','U','H',' ')}, /* Quechua (Bolivia) */ + {"quz", HB_TAG('Q','U','Z',' ')}, /* Cusco Quechua */ + {"qvi", HB_TAG('Q','V','I',' ')}, /* Quechua (Ecuador) */ + {"qwh", HB_TAG('Q','W','H',' ')}, /* Quechua (Peru) */ + {"raj", HB_TAG('R','A','J',' ')}, /* Rajasthani [macrolanguage] */ + {"rar", HB_TAG('R','A','R',' ')}, /* Rarotongan */ + {"rbb", HB_TAG('P','L','G',' ')}, /* Rumai Palaung */ + {"rej", HB_TAG('R','E','J',' ')}, /* Rejang */ + {"ria", HB_TAG('R','I','A',' ')}, /* Riang (India) */ + {"rif", HB_TAG('R','I','F',' ')}, /* Tarifit */ + {"ril", HB_TAG('R','I','A',' ')}, /* Riang (Myanmar) */ + {"rit", HB_TAG('R','I','T',' ')}, /* Ritarungo */ + {"rki", HB_TAG('A','R','K',' ')}, /* Rakhine */ + {"rkw", HB_TAG('R','K','W',' ')}, /* Arakwal */ + {"rm", HB_TAG('R','M','S',' ')}, /* Romansh */ + {"rmy", HB_TAG('R','M','Y',' ')}, /* Vlax Romani */ + {"rn", HB_TAG('R','U','N',' ')}, /* Rundi */ + {"ro", HB_TAG('R','O','M',' ')}, /* Romanian */ + {"rom", HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ + {"rtm", HB_TAG('R','T','M',' ')}, /* Rotuman */ + {"ru", HB_TAG('R','U','S',' ')}, /* Russian */ + {"rue", HB_TAG('R','S','Y',' ')}, /* Rusyn */ + {"rup", HB_TAG('R','U','P',' ')}, /* Aromanian/Arumanian/Macedo-Romanian */ + {"rw", HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ + {"rwr", HB_TAG('M','A','W',' ')}, /* Marwari (India) */ + {"sa", HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {"sah", HB_TAG('Y','A','K',' ')}, /* Yakut */ + {"sam", HB_TAG('P','A','A',' ')}, /* Palestinian Aramaic */ + {"sas", HB_TAG('S','A','S',' ')}, /* Sasak */ + {"sat", HB_TAG('S','A','T',' ')}, /* Santali */ + {"sc", HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */ + {"scn", HB_TAG('S','C','N',' ')}, /* Sicilian */ + {"sco", HB_TAG('S','C','O',' ')}, /* Scots */ + {"scs", HB_TAG('S','L','A',' ')}, /* [North] Slavey */ + {"sd", HB_TAG('S','N','D',' ')}, /* Sindhi */ + {"se", HB_TAG('N','S','M',' ')}, /* Northern Sami */ + {"seh", HB_TAG('S','N','A',' ')}, /* Sena */ + {"sel", HB_TAG('S','E','L',' ')}, /* Selkup */ + {"sg", HB_TAG('S','G','O',' ')}, /* Sango */ + {"sga", HB_TAG('S','G','A',' ')}, /* Old Irish (to 900) */ + {"sgs", HB_TAG('S','G','S',' ')}, /* Samogitian */ + {"sgw", HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage */ +/*{"sgw", HB_TAG('S','G','W',' ')},*/ /* Sebat Bet Gurage (in SIL fonts) */ + {"shi", HB_TAG('S','H','I',' ')}, /* Tachelhit */ + {"shn", HB_TAG('S','H','N',' ')}, /* Shan */ + {"si", HB_TAG('S','N','H',' ')}, /* Sinhala */ + {"sid", HB_TAG('S','I','D',' ')}, /* Sidamo */ + {"sjd", HB_TAG('K','S','M',' ')}, /* Kildin Sami */ + {"sk", HB_TAG('S','K','Y',' ')}, /* Slovak */ + {"skr", HB_TAG('S','R','K',' ')}, /* Seraiki */ + {"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */ + {"sm", HB_TAG('S','M','O',' ')}, /* Samoan */ + {"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */ + {"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */ + {"smn", HB_TAG('I','S','M',' ')}, /* Inari Sami */ + {"sms", HB_TAG('S','K','S',' ')}, /* Skolt Sami */ + {"sn", HB_TAG('S','N','A','0')}, /* Shona */ + {"snk", HB_TAG('S','N','K',' ')}, /* Soninke */ + {"so", HB_TAG('S','M','L',' ')}, /* Somali */ + {"sop", HB_TAG('S','O','P',' ')}, /* Songe */ + {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */ + {"sr", HB_TAG('S','R','B',' ')}, /* Serbian */ + {"srr", HB_TAG('S','R','R',' ')}, /* Serer */ + {"ss", HB_TAG('S','W','Z',' ')}, /* Swati */ + {"st", HB_TAG('S','O','T',' ')}, /* [Southern] Sotho */ + {"stq", HB_TAG('S','T','Q',' ')}, /* Saterfriesisch */ + {"stv", HB_TAG('S','I','G',' ')}, /* Silt'e */ + {"su", HB_TAG('S','U','N',' ')}, /* Sundanese */ + {"suk", HB_TAG('S','U','K',' ')}, /* Sukama */ + {"suq", HB_TAG('S','U','R',' ')}, /* Suri */ + {"sv", HB_TAG('S','V','E',' ')}, /* Swedish */ + {"sva", HB_TAG('S','V','A',' ')}, /* Svan */ + {"sw", HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ + {"swb", HB_TAG('C','M','R',' ')}, /* Comorian */ + {"swh", HB_TAG('S','W','K',' ')}, /* Kiswahili/Swahili */ + {"swv", HB_TAG('M','A','W',' ')}, /* Shekhawati */ + {"sxu", HB_TAG('S','X','U',' ')}, /* Upper Saxon */ + {"syl", HB_TAG('S','Y','L',' ')}, /* Sylheti */ + {"syr", HB_TAG('S','Y','R',' ')}, /* Syriac [macrolanguage] */ + {"szl", HB_TAG('S','Z','L',' ')}, /* Silesian */ + {"ta", HB_TAG('T','A','M',' ')}, /* Tamil */ + {"tab", HB_TAG('T','A','B',' ')}, /* Tabasaran */ + {"tcy", HB_TAG('T','U','L',' ')}, /* Tulu */ + {"tdd", HB_TAG('T','D','D',' ')}, /* Tai Nüa */ + {"te", HB_TAG('T','E','L',' ')}, /* Telugu */ + {"tem", HB_TAG('T','M','N',' ')}, /* Temne */ + {"tet", HB_TAG('T','E','T',' ')}, /* Tetum */ + {"tg", HB_TAG('T','A','J',' ')}, /* Tajik */ + {"th", HB_TAG('T','H','A',' ')}, /* Thai */ + {"ti", HB_TAG('T','G','Y',' ')}, /* Tigrinya */ + {"tig", HB_TAG('T','G','R',' ')}, /* Tigre */ + {"tiv", HB_TAG('T','I','V',' ')}, /* Tiv */ + {"tk", HB_TAG('T','K','M',' ')}, /* Turkmen */ + {"tl", HB_TAG('T','G','L',' ')}, /* Tagalog */ + {"tmh", HB_TAG('T','M','H',' ')}, /* Tamashek */ + {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */ + {"to", HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) */ + {"tod", HB_TAG('T','O','D','0')}, /* Toma */ + {"toi", HB_TAG('T','N','G',' ')}, /* Tonga */ + {"tpi", HB_TAG('T','P','I',' ')}, /* Tok Pisin */ + {"tr", HB_TAG('T','R','K',' ')}, /* Turkish */ + {"tru", HB_TAG('T','U','A',' ')}, /* Turoyo Aramaic */ + {"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */ + {"tt", HB_TAG('T','A','T',' ')}, /* Tatar */ + {"tum", HB_TAG('T','U','M',' ')}, /* Tumbuka */ + {"tvl", HB_TAG('T','V','L',' ')}, /* Tuvalu */ + {"tw", HB_TAG('T','W','I',' ')}, /* Twi */ + {"ty", HB_TAG('T','H','T',' ')}, /* Tahitian */ + {"tyv", HB_TAG('T','U','V',' ')}, /* Tuvin */ + {"tyz", HB_TAG('T','Y','Z',' ')}, /* Tày */ + {"tzm", HB_TAG('T','Z','M',' ')}, /* Central Atlas Tamazight */ + {"tzo", HB_TAG('T','Z','O',' ')}, /* Tzotzil */ + {"udm", HB_TAG('U','D','M',' ')}, /* Udmurt */ + {"ug", HB_TAG('U','Y','G',' ')}, /* Uighur */ + {"uk", HB_TAG('U','K','R',' ')}, /* Ukrainian */ + {"umb", HB_TAG('U','M','B',' ')}, /* Umbundu */ + {"unr", HB_TAG('M','U','N',' ')}, /* Mundari */ + {"ur", HB_TAG('U','R','D',' ')}, /* Urdu */ + {"uz", HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */ + {"uzn", HB_TAG('U','Z','B',' ')}, /* Northern Uzbek */ + {"uzs", HB_TAG('U','Z','B',' ')}, /* Southern Uzbek */ + {"ve", HB_TAG('V','E','N',' ')}, /* Venda */ + {"vec", HB_TAG('V','E','C',' ')}, /* Venetian */ + {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {"vls", HB_TAG('F','L','E',' ')}, /* Vlaams */ + {"vmw", HB_TAG('M','A','K',' ')}, /* Makhuwa */ + {"vo", HB_TAG('V','O','L',' ')}, /* Volapük */ + {"vro", HB_TAG('V','R','O',' ')}, /* Võro */ + {"wa", HB_TAG('W','L','N',' ')}, /* Walloon */ + {"war", HB_TAG('W','A','R',' ')}, /* Waray (Philippines) */ + {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */ + {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */ + {"wle", HB_TAG('S','I','G',' ')}, /* Wolane */ + {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */ + {"wry", HB_TAG('M','A','W',' ')}, /* Merwari */ + {"wtm", HB_TAG('W','T','M',' ')}, /* Mewati */ + {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */ + {"xan", HB_TAG('S','E','K',' ')}, /* Sekota */ + {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */ + {"xjb", HB_TAG('X','J','B',' ')}, /* Minjangbal */ + {"xog", HB_TAG('X','O','G',' ')}, /* Soga */ + {"xom", HB_TAG('K','M','O',' ')}, /* Komo (Sudan) */ + {"xpe", HB_TAG('X','P','E',' ')}, /* Kpelle (Liberia) */ + {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */ + {"xst", HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) */ + {"xwo", HB_TAG('T','O','D',' ')}, /* Written Oirat (Todo) */ + {"yao", HB_TAG('Y','A','O',' ')}, /* Yao */ + {"yap", HB_TAG('Y','A','P',' ')}, /* Yapese */ + {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ + {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */ + {"yso", HB_TAG('N','I','S',' ')}, /* Nisi (China) */ + {"za", HB_TAG('Z','H','A',' ')}, /* Chuang/Zhuang [macrolanguage] */ + {"zea", HB_TAG('Z','E','A',' ')}, /* Zeeuws */ + {"zgh", HB_TAG('Z','G','H',' ')}, /* Standard Morrocan Tamazigh */ + {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */ + {"zu", HB_TAG('Z','U','L',' ')}, /* Zulu */ + {"zum", HB_TAG('L','R','C',' ')}, /* Kumzari */ + {"zza", HB_TAG('Z','Z','A',' ')}, /* Zazaki */ + + /* The corresponding languages IDs for the following IDs are unclear, + * overlap, or are architecturally weird. Needs more research. */ + +/*{"chp", HB_TAG('S','A','Y',' ')},*/ /* Sayisi */ +/*{"cwd", HB_TAG('T','C','R',' ')},*/ /* TH-Cree */ +/*{"emk", HB_TAG('E','M','K',' ')},*/ /* Eastern Maninkakan */ +/*{"krc", HB_TAG('B','A','L',' ')},*/ /* Balkar */ +/*{"??", HB_TAG('B','C','R',' ')},*/ /* Bible Cree */ +/*{"zh?", HB_TAG('C','H','N',' ')},*/ /* Chinese (seen in Microsoft fonts) */ +/*{"ar-Syrc?", HB_TAG('G','A','R',' ')},*/ /* Garshuni */ +/*{"hy?", HB_TAG('H','Y','E','0')},*/ /* Armenian East (ISO 639-3 hye according to Microsoft, but that’s equivalent to ISO 639-1 hy) */ +/*{"ga-Latg?/" HB_TAG('I','R','T',' ')},*/ /* Irish Traditional */ +/*{"krc", HB_TAG('K','A','R',' ')},*/ /* Karachay */ +/*{"ka-Geok?", HB_TAG('K','G','E',' ')},*/ /* Khutsuri Georgian */ +/*{"kca", HB_TAG('K','H','K',' ')},*/ /* Khanty-Kazim */ +/*{"kca", HB_TAG('K','H','S',' ')},*/ /* Khanty-Shurishkar */ +/*{"kca", HB_TAG('K','H','V',' ')},*/ /* Khanty-Vakhi */ +/*{"kqs, kss", HB_TAG('K','I','S',' ')},*/ /* Kisii */ +/*{"lua", HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ +/*{"mlq", HB_TAG('M','L','N',' ')},*/ /* Malinke */ +/*{"nso", HB_TAG('N','S','O',' ')},*/ /* Sotho, Northern */ +/*{"??", HB_TAG('M','A','L',' ')},*/ /* Malayalam Traditional */ +/*{"csw", HB_TAG('N','C','R',' ')},*/ /* N-Cree */ +/*{"csw", HB_TAG('N','H','C',' ')},*/ /* Norway House Cree */ +/*{"el-polyton", HB_TAG('P','G','R',' ')},*/ /* Polytonic Greek */ +/*{"bgr, cnh, cnw, czt, sez, tcp, csy, ctd, flm, pck, tcz, zom, cmr, dao, hlt, cka, cnk, mrh, mwg, cbl, cnb, csh", HB_TAG('Q','I','N',' ')},*/ /* Chin */ +/*{"??", HB_TAG('Y','I','C',' ')},*/ /* Yi Classic */ +/*{"zh-Latn-pinyin", HB_TAG('Z','H','P',' ')},*/ /* Chinese Phonetic */ +}; + +typedef struct { + char language[11]; + hb_tag_t tag; +} LangTagLong; +static const LangTagLong ot_languages_zh[] = { + /* Store longest-first, if one is a prefix of another. */ + {"zh-cn", HB_TAG('Z','H','S',' ')}, /* Chinese (China) */ + {"zh-hk", HB_TAG('Z','H','H',' ')}, /* Chinese (Hong Kong) */ + {"zh-mo", HB_TAG('Z','H','H',' ')}, /* Chinese (Macao) */ + {"zh-sg", HB_TAG('Z','H','S',' ')}, /* Chinese (Singapore) */ + {"zh-tw", HB_TAG('Z','H','T',' ')}, /* Chinese (Taiwan) */ + {"zh-hans", HB_TAG('Z','H','S',' ')}, /* Chinese (Simplified) */ + {"zh-hant-hk",HB_TAG('Z','H','H',' ')}, /* Chinese (Hong Kong) */ + {"zh-hant-mo",HB_TAG('Z','H','H',' ')}, /* Chinese (Macao) */ + {"zh-hant", HB_TAG('Z','H','T',' ')}, /* Chinese (Traditional) */ +}; + +static int +lang_compare_first_component (const char *a, + const char *b) +{ + unsigned int da, db; + const char *p; + + p = strchr (a, '-'); + da = p ? (unsigned int) (p - a) : strlen (a); + + p = strchr (b, '-'); + db = p ? (unsigned int) (p - b) : strlen (b); + + return strncmp (a, b, MAX (da, db)); +} + +static hb_bool_t +lang_matches (const char *lang_str, const char *spec) +{ + unsigned int len = strlen (spec); + + return strncmp (lang_str, spec, len) == 0 && + (lang_str[len] == '\0' || lang_str[len] == '-'); +} + +hb_tag_t +hb_ot_tag_from_language (hb_language_t language) +{ + const char *lang_str, *s; + + if (language == HB_LANGUAGE_INVALID) + return HB_OT_TAG_DEFAULT_LANGUAGE; + + lang_str = hb_language_to_string (language); + + s = strstr (lang_str, "x-hbot"); + if (s) { + char tag[4]; + int i; + s += 6; + for (i = 0; i < 4 && ISALPHA (s[i]); i++) + tag[i] = TOUPPER (s[i]); + if (i) { + for (; i < 4; i++) + tag[i] = ' '; + return HB_TAG_CHAR4 (tag); + } + } + + /* + * "fonipa" is a variant tag in BCP-47, meaning the International Phonetic Alphabet. + * It can be applied to any language. + */ + if (strstr (lang_str, "-fonipa")) { + return HB_TAG('I','P','P','H'); /* Phonetic transcription—IPA conventions */ + } + + /* + * "fonnapa" is a variant tag in BCP-47, meaning the North American Phonetic Alphabet + * also known as Americanist Phonetic Notation. It can be applied to any language. + */ + if (strstr (lang_str, "-fonnapa")) { + return HB_TAG('A','P','P','H'); /* Phonetic transcription—Americanist conventions */ + } + + /* Find a language matching in the first component */ + { + const LangTag *lang_tag; + lang_tag = (LangTag *) bsearch (lang_str, ot_languages, + ARRAY_LENGTH (ot_languages), sizeof (LangTag), + (hb_compare_func_t) lang_compare_first_component); + if (lang_tag) + return lang_tag->tag; + } + + /* Otherwise, check the Chinese ones */ + if (0 == lang_compare_first_component (lang_str, "zh")) + { + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (ot_languages_zh); i++) + { + const LangTagLong *lang_tag; + lang_tag = &ot_languages_zh[i]; + if (lang_matches (lang_str, lang_tag->language)) + return lang_tag->tag; + } + + /* Otherwise just return 'ZHS ' */ + 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) & ~0x20202000u; + } + + return HB_OT_TAG_DEFAULT_LANGUAGE; +} + +/** + * hb_ot_tag_to_language: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_ot_tag_to_language (hb_tag_t tag) +{ + unsigned int i; + + if (tag == HB_OT_TAG_DEFAULT_LANGUAGE) + return NULL; + + for (i = 0; i < ARRAY_LENGTH (ot_languages); i++) + if (ot_languages[i].tag == tag) + return hb_language_from_string (ot_languages[i].language, -1); + + /* If tag starts with ZH, it's Chinese */ + if ((tag & 0xFFFF0000u) == 0x5A480000u) { + switch (tag) { + case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */ + case HB_TAG('Z','H','S',' '): return hb_language_from_string ("zh-Hans", -1); /* Simplified */ + case HB_TAG('Z','H','T',' '): return hb_language_from_string ("zh-Hant", -1); /* Traditional */ + default: break; /* Fall through */ + } + } + + /* struct LangTag has only room for 3-letter language tags. */ + switch (tag) { + case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ + return hb_language_from_string ("und-fonnapa", -1); + case HB_TAG('I','P','P','H'): /* Phonetic transcription—IPA conventions */ + return hb_language_from_string ("und-fonipa", -1); + } + + /* Else return a custom language in the form of "x-hbotABCD" */ + { + unsigned char buf[11] = "x-hbot"; + buf[6] = tag >> 24; + buf[7] = (tag >> 16) & 0xFF; + buf[8] = (tag >> 8) & 0xFF; + buf[9] = tag & 0xFF; + if (buf[9] == 0x20) + buf[9] = '\0'; + buf[10] = '\0'; + return hb_language_from_string ((char *) buf, -1); + } +} + +#ifdef MAIN +static inline void +test_langs_sorted (void) +{ + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++) + { + int c = lang_compare_first_component (ot_languages[i-1].language, ot_languages[i].language); + if (c >= 0) + { + fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n", + i, ot_languages[i-1].language, c, ot_languages[i].language); + abort(); + } + } +} + +int +main (void) +{ + test_langs_sorted (); + return 0; +} + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-tag.h b/gfx/harfbuzz/src/hb-ot-tag.h new file mode 100644 index 000000000..54fb747f5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-tag.h @@ -0,0 +1,59 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H_IN +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_TAG_H +#define HB_OT_TAG_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T') +#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't') + +HB_EXTERN void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2); + +HB_EXTERN hb_script_t +hb_ot_tag_to_script (hb_tag_t tag); + +HB_EXTERN hb_tag_t +hb_ot_tag_from_language (hb_language_t language); + +HB_EXTERN hb_language_t +hb_ot_tag_to_language (hb_tag_t tag); + + +HB_END_DECLS + +#endif /* HB_OT_TAG_H */ diff --git a/gfx/harfbuzz/src/hb-ot.h b/gfx/harfbuzz/src/hb-ot.h new file mode 100644 index 000000000..113e37b08 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot.h @@ -0,0 +1,44 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H +#define HB_OT_H +#define HB_OT_H_IN + +#include "hb.h" + +#include "hb-ot-font.h" +#include "hb-ot-layout.h" +#include "hb-ot-math.h" +#include "hb-ot-tag.h" +#include "hb-ot-shape.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_OT_H_IN +#endif /* HB_OT_H */ diff --git a/gfx/harfbuzz/src/hb-private.hh b/gfx/harfbuzz/src/hb-private.hh new file mode 100644 index 000000000..666af6260 --- /dev/null +++ b/gfx/harfbuzz/src/hb-private.hh @@ -0,0 +1,1024 @@ +/* + * Copyright © 2007,2008,2009 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_PRIVATE_HH +#define HB_PRIVATE_HH + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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> +#include <string.h> +#include <assert.h> + +/* We only use these two for debug output. However, the debug code is + * always seen by the compiler (and optimized out in non-debug builds. + * If including these becomes a problem, we can start thinking about + * someway around that. */ +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> + + +/* Compile-time custom allocator support. */ + +#if defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +extern "C" void* hb_malloc_impl(size_t size); +extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); +extern "C" void* hb_realloc_impl(void *ptr, size_t size); +extern "C" void hb_free_impl(void *ptr); +#define malloc hb_malloc_impl +#define calloc hb_calloc_impl +#define realloc hb_realloc_impl +#define free hb_free_impl +#endif + + +/* Compiler attributes */ + + +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0) +#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1)) +#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +#if !defined(__GNUC__) && !defined(__clang__) +#undef __attribute__ +#define __attribute__(x) +#endif + +#if __GNUC__ >= 3 +#define HB_PURE_FUNC __attribute__((pure)) +#define HB_CONST_FUNC __attribute__((const)) +#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#else +#define HB_PURE_FUNC +#define HB_CONST_FUNC +#define HB_PRINTF_FUNC(format_idx, arg_idx) +#endif +#if __GNUC__ >= 4 +#define HB_UNUSED __attribute__((unused)) +#else +#define HB_UNUSED +#endif + +#ifndef HB_INTERNAL +# if !defined(__MINGW32__) && !defined(__CYGWIN__) +# define HB_INTERNAL __attribute__((__visibility__("hidden"))) +# else +# define HB_INTERNAL +# endif +#endif + +#if __GNUC__ >= 3 +#define HB_FUNC __PRETTY_FUNCTION__ +#elif defined(_MSC_VER) +#define HB_FUNC __FUNCSIG__ +#else +#define HB_FUNC __func__ +#endif + +/* + * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411 + * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch + * cases that fall through without a break or return statement. HB_FALLTHROUGH + * is only needed on cases that have code: + * + * switch (foo) { + * case 1: // These cases have no code. No fallthrough annotations are needed. + * case 2: + * case 3: + * foo = 4; // This case has code, so a fallthrough annotation is needed: + * HB_FALLTHROUGH; + * default: + * return foo; + * } + */ +#if defined(__clang__) && __cplusplus >= 201103L + /* clang's fallthrough annotations are only available starting in C++11. */ +# define HB_FALLTHROUGH [[clang::fallthrough]] +#elif defined(_MSC_VER) + /* + * MSVC's __fallthrough annotations are checked by /analyze (Code Analysis): + * https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx + */ +# include <sal.h> +# define HB_FALLTHROUGH __fallthrough +#else +# define HB_FALLTHROUGH /* FALLTHROUGH */ +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) + /* We need Windows Vista for both Uniscribe backend and for + * MemoryBarrier. We don't support compiling on Windows XP, + * though we run on it fine. */ +# if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600 +# undef _WIN32_WINNT +# endif +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# ifndef STRICT +# define STRICT 1 +# endif + +# if defined(_WIN32_WCE) + /* Some things not defined on Windows CE. */ +# define strdup _strdup +# define vsnprintf _vsnprintf +# define getenv(Name) NULL +# if _WIN32_WCE < 0x800 +# define setlocale(Category, Locale) "C" +static int errno = 0; /* Use something better? */ +# endif +# elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +# define getenv(Name) NULL +# endif +# if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +# elif defined(_MSC_VER) && _MSC_VER >= 1900 +# /* Covers VC++ Error for strdup being a deprecated POSIX name and to instead use _strdup instead */ +# define strdup _strdup +# endif +#endif + +#if HAVE_ATEXIT +/* atexit() is only safe to be called from shared libraries on certain + * platforms. Whitelist. + * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */ +# if defined(__linux) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2,3) +/* From atexit() manpage, it's safe with glibc 2.2.3 on Linux. */ +# define HB_USE_ATEXIT 1 +# endif +# elif defined(_MSC_VER) || defined(__MINGW32__) +/* For MSVC: + * http://msdn.microsoft.com/en-ca/library/tze57ck3.aspx + * http://msdn.microsoft.com/en-ca/library/zk17ww08.aspx + * mingw32 headers say atexit is safe to use in shared libraries. + */ +# define HB_USE_ATEXIT 1 +# elif defined(__ANDROID__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +/* This was fixed in Android NKD r8 or r8b: + * https://code.google.com/p/android/issues/detail?id=6455 + * which introduced GCC 4.6: + * https://developer.android.com/tools/sdk/ndk/index.html + */ +# define HB_USE_ATEXIT 1 +# endif +#endif + +/* Basics */ + + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +#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; } + +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) +{ return (a + (b - 1)) / b; } + + +#undef ARRAY_LENGTH +template <typename Type, unsigned int n> +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + +#define HB_STMT_START do +#define HB_STMT_END while (0) + +#define _ASSERT_STATIC1(_line, _cond) HB_UNUSED 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)) + +template <unsigned int cond> class hb_assert_constant_t {}; + +#define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * (unsigned int) sizeof (hb_assert_constant_t<_cond>)) + +#define _PASTE1(a,b) a##b +#define PASTE(a,b) _PASTE1(a,b) + +/* Lets assert int types. Saves trouble down the road. */ + +ASSERT_STATIC (sizeof (int8_t) == 1); +ASSERT_STATIC (sizeof (uint8_t) == 1); +ASSERT_STATIC (sizeof (int16_t) == 2); +ASSERT_STATIC (sizeof (uint16_t) == 2); +ASSERT_STATIC (sizeof (int32_t) == 4); +ASSERT_STATIC (sizeof (uint32_t) == 4); +ASSERT_STATIC (sizeof (int64_t) == 8); +ASSERT_STATIC (sizeof (uint64_t) == 8); + +ASSERT_STATIC (sizeof (hb_codepoint_t) == 4); +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) \ + HB_UNUSED 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 */ + +/* Void! */ +struct _hb_void_t {}; +typedef const _hb_void_t *hb_void_t; +#define HB_VOID ((const _hb_void_t *) NULL) + +/* Return the number of 1 bits in mask. */ +static inline HB_CONST_FUNC unsigned int +_hb_popcount32 (uint32_t mask) +{ +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (mask); +#else + /* "HACKMEM 169" */ + uint32_t y; + y = (mask >> 1) &033333333333; + y = mask - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); +#endif +} + +/* Returns the number of bits needed to store number */ +static inline HB_CONST_FUNC unsigned int +_hb_bit_storage (unsigned int number) +{ +#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) + return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0; +#else + unsigned int n_bits = 0; + while (number) { + n_bits++; + number >>= 1; + } + return n_bits; +#endif +} + +/* Returns the number of zero bits in the least significant side of number */ +static inline HB_CONST_FUNC unsigned int +_hb_ctz (unsigned int number) +{ +#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) + return likely (number) ? __builtin_ctz (number) : 0; +#else + unsigned int n_bits = 0; + if (unlikely (!number)) return 0; + while (!(number & 1)) { + n_bits++; + number >>= 1; + } + return n_bits; +#endif +} + +static inline bool +_hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size) +{ + return (size > 0) && (count >= ((unsigned int) -1) / size); +} + + +/* Type of bsearch() / qsort() compare function */ +typedef int (*hb_compare_func_t) (const void *, const void *); + + + + +/* arrays and maps */ + + +#define HB_PREALLOCED_ARRAY_INIT {0, 0, NULL} +template <typename Type, unsigned int StaticSize=16> +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]; } + + inline Type *push (void) + { + if (!array) { + array = static_array; + allocated = ARRAY_LENGTH (static_array); + } + if (likely (len < allocated)) + return &array[len++]; + + /* Need to reallocate */ + unsigned int new_allocated = allocated + (allocated >> 1) + 8; + Type *new_array = NULL; + + if (array == static_array) { + new_array = (Type *) calloc (new_allocated, sizeof (Type)); + if (new_array) + memcpy (new_array, array, len * sizeof (Type)); + } else { + bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type)); + if (likely (!overflows)) { + new_array = (Type *) realloc (array, new_allocated * sizeof (Type)); + } + } + + if (unlikely (!new_array)) + return NULL; + + array = new_array; + allocated = new_allocated; + return &array[len++]; + } + + inline void pop (void) + { + len--; + } + + inline void remove (unsigned int i) + { + if (unlikely (i >= len)) + return; + memmove (static_cast<void *> (&array[i]), + static_cast<void *> (&array[i + 1]), + (len - i - 1) * sizeof (Type)); + len--; + } + + inline void shrink (unsigned int l) + { + if (l < len) + len = l; + } + + template <typename T> + inline Type *find (T v) { + for (unsigned int i = 0; i < len; i++) + if (array[i] == v) + return &array[i]; + return NULL; + } + template <typename T> + inline const Type *find (T v) const { + for (unsigned int i = 0; i < len; i++) + if (array[i] == v) + return &array[i]; + return NULL; + } + + inline void qsort (void) + { + ::qsort (array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); + } + + inline void qsort (unsigned int start, unsigned int end) + { + ::qsort (array + start, end - start, sizeof (Type), (hb_compare_func_t) Type::cmp); + } + + template <typename T> + inline Type *bsearch (T *key) + { + return (Type *) ::bsearch (key, array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); + } + template <typename T> + inline const Type *bsearch (T *key) const + { + return (const Type *) ::bsearch (key, array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); + } + + inline void finish (void) + { + if (array != static_array) + free (array); + array = NULL; + allocated = len = 0; + } +}; + +template <typename Type> +struct hb_auto_array_t : hb_prealloced_array_t <Type> +{ + hb_auto_array_t (void) { hb_prealloced_array_t<Type>::init (); } + ~hb_auto_array_t (void) { hb_prealloced_array_t<Type>::finish (); } +}; + + +#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT} +template <typename item_t, typename lock_t> +struct hb_lockable_set_t +{ + 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, bool replace) + { + l.lock (); + item_t *item = items.find (v); + if (item) { + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.finish (); + } + else { + item = NULL; + l.unlock (); + } + } else { + item = items.push (); + if (likely (item)) + *item = v; + l.unlock (); + } + return item; + } + + template <typename T> + inline void remove (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (item) { + item_t old = *item; + *item = items[items.len - 1]; + items.pop (); + l.unlock (); + old.finish (); + } else { + l.unlock (); + } + } + + template <typename T> + inline bool find (T v, item_t *i, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (item) + *i = *item; + l.unlock (); + return !!item; + } + + template <typename T> + inline item_t *find_or_insert (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (!item) { + item = items.push (); + if (likely (item)) + *item = v; + } + l.unlock (); + return item; + } + + inline void finish (lock_t &l) + { + if (!items.len) { + /* No need for locking. */ + items.finish (); + return; + } + l.lock (); + while (items.len) { + item_t old = items[items.len - 1]; + items.pop (); + l.unlock (); + old.finish (); + l.lock (); + } + items.finish (); + l.unlock (); + } + +}; + + +/* ASCII tag/character handling */ + +static inline bool ISALPHA (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } +static inline bool ISALNUM (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } +static inline bool ISSPACE (unsigned char c) +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } +static inline unsigned char TOUPPER (unsigned char c) +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } +static inline unsigned char TOLOWER (unsigned char c) +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } + +#define HB_TAG_CHAR4(s) (HB_TAG(((const char *) s)[0], \ + ((const char *) s)[1], \ + ((const char *) s)[2], \ + ((const char *) s)[3])) + + +/* C++ helpers */ + +/* Makes class uncopyable. Use in private: section. */ +#define NO_COPY(T) \ + T (const T &o); \ + T &operator = (const T &o) + + +/* Debug */ + + +/* HB_NDEBUG disables some sanity checks that are very safe to disable and + * should be disabled in production systems. If NDEBUG is defined, enable + * HB_NDEBUG; but if it's desirable that normal assert()s (which are very + * light-weight) to be enabled, then HB_DEBUG can be defined to disable + * the costlier checks. */ +#ifdef NDEBUG +#define HB_NDEBUG +#endif + +#ifndef HB_DEBUG +#define HB_DEBUG 0 +#endif + +static inline bool +_hb_debug (unsigned int level, + unsigned int max_level) +{ + return level < max_level; +} + +#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) +#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0)) + +static inline void +_hb_print_func (const char *func) +{ + if (func) + { + unsigned int func_len = strlen (func); + /* Skip "static" */ + if (0 == strncmp (func, "static ", 7)) + func += 7; + /* Skip "typename" */ + if (0 == strncmp (func, "typename ", 9)) + func += 9; + /* Skip return type */ + const char *space = strchr (func, ' '); + if (space) + func = space + 1; + /* Skip parameter list */ + const char *paren = strchr (func, '('); + if (paren) + func_len = paren - func; + fprintf (stderr, "%.*s", func_len, func); + } +} + +template <int max_level> static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) HB_PRINTF_FUNC(7, 0); +template <int max_level> static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) +{ + if (!_hb_debug (level, max_level)) + 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) { +#define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ +#define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +#define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ +#define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */ +#define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */ + static const char bars[] = + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; + fprintf (stderr, "%2u %s" VRBAR "%s", + level, + bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); + } else + fprintf (stderr, " " VRBAR LBAR); + + _hb_print_func (func); + + if (message) + { + fprintf (stderr, ": "); + 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> static inline void +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) HB_PRINTF_FUNC(7, 8); +template <int max_level> static inline void +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) +{ + va_list ap; + va_start (ap, message); + _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap); + va_end (ap); +} +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__) + + +/* + * Printer + */ + +template <typename T> +struct hb_printer_t { + const char *print (const T&) { return "something"; } +}; + +template <> +struct hb_printer_t<bool> { + const char *print (bool v) { return v ? "true" : "false"; } +}; + +template <> +struct hb_printer_t<hb_void_t> { + const char *print (hb_void_t) { return ""; } +}; + + +/* + * Trace + */ + +template <typename T> +static inline void _hb_warn_no_return (bool returned) +{ + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n"); + } +} +template <> +/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED) +{} + +template <int max_level, typename ret_t> +struct hb_auto_trace_t { + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) : plevel (plevel_), what (what_), obj (obj_), returned (false) + { + 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) + { + _hb_warn_no_return<ret_t> (returned); + if (!returned) { + _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " "); + } + if (plevel) --*plevel; + } + + inline ret_t ret (ret_t v, unsigned int line = 0) + { + if (unlikely (returned)) { + fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); + return v; + } + + _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, + "return %s (line %d)", + hb_printer_t<ret_t>().print (v), line); + if (plevel) --*plevel; + plevel = NULL; + returned = true; + return v; + } + + private: + unsigned int *plevel; + const char *what; + const void *obj; + bool returned; +}; +template <typename ret_t> /* Optimize when tracing is disabled */ +struct hb_auto_trace_t<0, ret_t> { + 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, + ...) {} + + inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } +}; + +#define return_trace(RET) return trace.ret (RET, __LINE__) + +/* Misc */ + +template <typename T> class hb_assert_unsigned_t; +template <> class hb_assert_unsigned_t<unsigned char> {}; +template <> class hb_assert_unsigned_t<unsigned short> {}; +template <> class hb_assert_unsigned_t<unsigned int> {}; +template <> class hb_assert_unsigned_t<unsigned long> {}; + +template <typename T> static inline bool +hb_in_range (T u, T lo, T hi) +{ + /* The sizeof() is here to force template instantiation. + * I'm sure there are better ways to do this but can't think of + * one right now. Declaring a variable won't work as HB_UNUSED + * is unusable on some platforms and unused types are less likely + * to generate a warning than unused variables. */ + ASSERT_STATIC (sizeof (hb_assert_unsigned_t<T>) >= 0); + + /* The casts below are important as if T is smaller than int, + * the subtract results will become a signed int! */ + return (T)(u - lo) <= (T)(hi - lo); +} + +template <typename T> static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); +} + +template <typename T> static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); +} + + +/* Enable bitwise ops on enums marked as flags_t */ +/* To my surprise, looks like the function resolver is happy to silently cast + * one enum to another... So this doesn't provide the type-checking that I + * originally had in mind... :(. + * + * For MSVC warnings, see: https://github.com/behdad/harfbuzz/pull/163 + */ +#ifdef _MSC_VER +# pragma warning(disable:4200) +# pragma warning(disable:4800) +#endif +#define HB_MARK_AS_FLAG_T(T) \ + extern "C++" { \ + static inline T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \ + static inline T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \ + static inline T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \ + static inline T operator ~ (T r) { return T (~(unsigned int) r); } \ + static inline T& operator |= (T &l, T r) { l = l | r; return l; } \ + static inline T& operator &= (T& l, T r) { l = l & r; return l; } \ + static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \ + } + + +/* Useful for set-operations on small enums. + * For example, for testing "x ∈ {x1, x2, x3}" use: + * (FLAG_SAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) + */ +#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((x) < 32) + (1U << (x))) +#define FLAG_SAFE(x) (1U << (x)) +#define FLAG_UNSAFE(x) ((x) < 32 ? FLAG_SAFE(x) : 0) +#define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x)) + + +template <typename T, typename T2> static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) +{ + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T2 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2)); + array2[j] = t; + } + } +} + +template <typename T> static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +{ + hb_stable_sort (array, len, compar, (int *) NULL); +} + +static inline hb_bool_t +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) +{ + /* Pain because we don't know whether s is nul-terminated. */ + char buf[64]; + len = MIN (ARRAY_LENGTH (buf) - 1, len); + strncpy (buf, s, len); + buf[len] = '\0'; + + char *end; + errno = 0; + unsigned long v = strtoul (buf, &end, base); + if (errno) return false; + if (*end) return false; + *out = v; + return true; +} + + +/* Global runtime options. */ + +struct hb_options_t +{ + unsigned int initialized : 1; + unsigned int uniscribe_bug_compatible : 1; +}; + +union hb_options_union_t { + unsigned int i; + hb_options_t opts; +}; +ASSERT_STATIC (sizeof (int) == sizeof (hb_options_union_t)); + +HB_INTERNAL void +_hb_options_init (void); + +extern HB_INTERNAL hb_options_union_t _hb_options; + +static inline hb_options_t +hb_options (void) +{ + if (unlikely (!_hb_options.i)) + _hb_options_init (); + + return _hb_options.opts; +} + +/* Size signifying variable-sized array */ +#define VAR 1 + +#endif /* HB_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-set-private.hh b/gfx/harfbuzz/src/hb-set-private.hh new file mode 100644 index 000000000..e2010d762 --- /dev/null +++ b/gfx/harfbuzz/src/hb-set-private.hh @@ -0,0 +1,402 @@ +/* + * 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-object-private.hh" + + +/* + * The set digests here implement various "filters" that support + * "approximate member query". Conceptually these are like Bloom + * Filter and Quotient Filter, however, much smaller, faster, and + * designed to fit the requirements of our uses for glyph coverage + * queries. + * + * Our filters are highly accurate if the lookup covers fairly local + * set of glyphs, but fully flooded and ineffective if coverage is + * all over the place. + * + * The frozen-set can be used instead of a digest, to trade more + * memory for 100% accuracy, but in practice, that doesn't look like + * an attractive trade-off. + */ + +template <typename mask_t, unsigned int shift> +struct hb_set_digest_lowest_bits_t +{ + ASSERT_POD (); + + static const unsigned int mask_bytes = sizeof (mask_t); + static const unsigned int mask_bits = sizeof (mask_t) * 8; + static const unsigned int num_bits = 0 + + (mask_bytes >= 1 ? 3 : 0) + + (mask_bytes >= 2 ? 1 : 0) + + (mask_bytes >= 4 ? 1 : 0) + + (mask_bytes >= 8 ? 1 : 0) + + (mask_bytes >= 16? 1 : 0) + + 0; + + ASSERT_STATIC (shift < sizeof (hb_codepoint_t) * 8); + ASSERT_STATIC (shift + num_bits <= sizeof (hb_codepoint_t) * 8); + + inline void init (void) { + mask = 0; + } + + inline void add (hb_codepoint_t g) { + mask |= mask_for (g); + } + + inline void add_range (hb_codepoint_t a, hb_codepoint_t b) { + if ((b >> shift) - (a >> shift) >= mask_bits - 1) + mask = (mask_t) -1; + else { + mask_t ma = mask_for (a); + mask_t mb = mask_for (b); + mask |= mb + (mb - ma) - (mb < ma); + } + } + + inline bool may_have (hb_codepoint_t g) const { + return !!(mask & mask_for (g)); + } + + private: + + static inline mask_t mask_for (hb_codepoint_t g) { + return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); + } + mask_t mask; +}; + +template <typename head_t, typename tail_t> +struct hb_set_digest_combiner_t +{ + ASSERT_POD (); + + inline void init (void) { + head.init (); + tail.init (); + } + + inline void add (hb_codepoint_t g) { + head.add (g); + tail.add (g); + } + + inline void add_range (hb_codepoint_t a, hb_codepoint_t b) { + head.add_range (a, b); + tail.add_range (a, b); + } + + inline bool may_have (hb_codepoint_t g) const { + return head.may_have (g) && tail.may_have (g); + } + + private: + head_t head; + tail_t tail; +}; + + +/* + * hb_set_digest_t + * + * This is a combination of digests that performs "best". + * There is not much science to this: it's a result of intuition + * and testing. + */ +typedef hb_set_digest_combiner_t +< + hb_set_digest_lowest_bits_t<unsigned long, 4>, + hb_set_digest_combiner_t + < + hb_set_digest_lowest_bits_t<unsigned long, 0>, + hb_set_digest_lowest_bits_t<unsigned long, 9> + > +> hb_set_digest_t; + + + +/* + * hb_set_t + */ + + +/* TODO Make this faster and memmory efficient. */ + +struct hb_set_t +{ + friend struct hb_frozen_set_t; + + hb_object_header_t header; + ASSERT_POD (); + bool in_error; + + inline void init (void) { + hb_object_init (this); + clear (); + } + inline void fini (void) { + } + inline void clear (void) { + if (unlikely (hb_object_is_inert (this))) + return; + in_error = false; + memset (elts, 0, sizeof elts); + } + inline bool is_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 (in_error)) return; + if (unlikely (g == INVALID)) return; + if (unlikely (g > MAX_G)) return; + elt (g) |= mask (g); + } + inline void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (in_error)) return; + /* TODO Speedup */ + for (unsigned int i = a; i < b + 1; i++) + add (i); + } + inline void del (hb_codepoint_t g) + { + if (unlikely (in_error)) return; + if (unlikely (g > MAX_G)) return; + elt (g) &= ~mask (g); + } + inline void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (in_error)) return; + /* TODO Speedup */ + for (unsigned int i = a; i < b + 1; i++) + del (i); + } + 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 is_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) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] = other->elts[i]; + } + inline void union_ (const hb_set_t *other) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] |= other->elts[i]; + } + inline void intersect (const hb_set_t *other) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] &= other->elts[i]; + } + inline void subtract (const hb_set_t *other) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] &= ~other->elts[i]; + } + inline void symmetric_difference (const hb_set_t *other) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] ^= other->elts[i]; + } + inline void invert (void) + { + if (unlikely (in_error)) return; + for (unsigned int i = 0; i < ELTS; i++) + elts[i] = ~elts[i]; + } + inline bool next (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + hb_codepoint_t i = get_min (); + if (i != INVALID) { + *codepoint = i; + return true; + } else { + *codepoint = INVALID; + return false; + } + } + for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++) + if (has (i)) { + *codepoint = i; + return true; + } + *codepoint = INVALID; + return false; + } + inline bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *last; + if (!next (&i)) + { + *last = *first = INVALID; + return false; + } + + *last = *first = i; + while (next (&i) && i == *last + 1) + (*last)++; + + return true; + } + + inline unsigned int get_population (void) const + { + unsigned int count = 0; + for (unsigned int i = 0; i < ELTS; i++) + count += _hb_popcount32 (elts[i]); + return count; + } + inline hb_codepoint_t get_min (void) const + { + for (unsigned int i = 0; i < ELTS; i++) + if (elts[i]) + for (unsigned int j = 0; j < BITS; j++) + if (elts[i] & (1u << j)) + return i * BITS + j; + return INVALID; + } + 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] & (1u << (j - 1))) + return (i - 1) * BITS + (j - 1); + return INVALID; + } + + 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 INVALID = HB_SET_VALUE_INVALID; + + elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; } + elt_t const &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); +}; + +struct hb_frozen_set_t +{ + static const unsigned int SHIFT = hb_set_t::SHIFT; + static const unsigned int BITS = hb_set_t::BITS; + static const unsigned int MASK = hb_set_t::MASK; + typedef hb_set_t::elt_t elt_t; + + inline void init (const hb_set_t &set) + { + start = count = 0; + elts = NULL; + + unsigned int max = set.get_max (); + if (max == set.INVALID) + return; + unsigned int min = set.get_min (); + const elt_t &min_elt = set.elt (min); + + start = min & ~MASK; + count = max - start + 1; + unsigned int num_elts = (count + BITS - 1) / BITS; + unsigned int elts_size = num_elts * sizeof (elt_t); + elts = (elt_t *) malloc (elts_size); + if (unlikely (!elts)) + { + start = count = 0; + return; + } + memcpy (elts, &min_elt, elts_size); + } + + inline void fini (void) + { + if (elts) + free (elts); + } + + inline bool has (hb_codepoint_t g) const + { + /* hb_codepoint_t is unsigned. */ + g -= start; + if (unlikely (g > count)) return false; + return !!(elt (g) & mask (g)); + } + + elt_t const &elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; } + elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); } + + private: + hb_codepoint_t start, count; + elt_t *elts; +}; + + +#endif /* HB_SET_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-set.cc b/gfx/harfbuzz/src/hb-set.cc new file mode 100644 index 000000000..cb7fcdbf6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-set.cc @@ -0,0 +1,471 @@ +/* + * 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_create: (Xconstructor) + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_create (void) +{ + hb_set_t *set; + + if (!(set = hb_object_create<hb_set_t> ())) + return hb_set_get_empty (); + + set->clear (); + + return set; +} + +/** + * hb_set_get_empty: + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_get_empty (void) +{ + static const hb_set_t _hb_set_nil = { + HB_OBJECT_HEADER_STATIC, + true, /* in_error */ + + {0} /* elts */ + }; + + return const_cast<hb_set_t *> (&_hb_set_nil); +} + +/** + * hb_set_reference: (skip) + * @set: a set. + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_reference (hb_set_t *set) +{ + return hb_object_reference (set); +} + +/** + * hb_set_destroy: (skip) + * @set: a set. + * + * Since: 0.9.2 + **/ +void +hb_set_destroy (hb_set_t *set) +{ + if (!hb_object_destroy (set)) return; + + set->fini (); + + free (set); +} + +/** + * hb_set_set_user_data: (skip) + * @set: a set. + * @key: + * @data: + * @destroy (closure data): + * @replace: + * + * Return value: + * + * Since: 0.9.2 + **/ +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); +} + +/** + * hb_set_get_user_data: (skip) + * @set: a set. + * @key: + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +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_set_allocation_successful: + * @set: a set. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_allocation_successful (const hb_set_t *set HB_UNUSED) +{ + return !set->in_error; +} + +/** + * hb_set_clear: + * @set: a set. + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_clear (hb_set_t *set) +{ + set->clear (); +} + +/** + * hb_set_is_empty: + * @set: a set. + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_empty (const hb_set_t *set) +{ + return set->is_empty (); +} + +/** + * hb_set_has: + * @set: a set. + * @codepoint: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_has (const hb_set_t *set, + hb_codepoint_t codepoint) +{ + return set->has (codepoint); +} + +/** + * hb_set_add: + * @set: a set. + * @codepoint: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint) +{ + set->add (codepoint); +} + +/** + * hb_set_add_range: + * @set: a set. + * @first: + * @last: + * + * + * + * Since: 0.9.7 + **/ +void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + set->add_range (first, last); +} + +/** + * hb_set_del: + * @set: a set. + * @codepoint: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint) +{ + set->del (codepoint); +} + +/** + * hb_set_del_range: + * @set: a set. + * @first: + * @last: + * + * + * + * Since: 0.9.7 + **/ +void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + set->del_range (first, last); +} + +/** + * hb_set_is_equal: + * @set: a set. + * @other: + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other) +{ + return set->is_equal (other); +} + +/** + * hb_set_set: + * @set: a set. + * @other: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_set (hb_set_t *set, + const hb_set_t *other) +{ + set->set (other); +} + +/** + * hb_set_union: + * @set: a set. + * @other: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_union (hb_set_t *set, + const hb_set_t *other) +{ + set->union_ (other); +} + +/** + * hb_set_intersect: + * @set: a set. + * @other: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other) +{ + set->intersect (other); +} + +/** + * hb_set_subtract: + * @set: a set. + * @other: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other) +{ + set->subtract (other); +} + +/** + * hb_set_symmetric_difference: + * @set: a set. + * @other: + * + * + * + * Since: 0.9.2 + **/ +void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other) +{ + set->symmetric_difference (other); +} + +/** + * hb_set_invert: + * @set: a set. + * + * + * + * Since: 0.9.10 + **/ +void +hb_set_invert (hb_set_t *set) +{ + set->invert (); +} + +/** + * hb_set_get_population: + * @set: a set. + * + * Returns the number of numbers in the set. + * + * Return value: set population. + * + * Since: 0.9.7 + **/ +unsigned int +hb_set_get_population (const hb_set_t *set) +{ + return set->get_population (); +} + +/** + * hb_set_get_min: + * @set: a set. + * + * Finds the minimum number in the set. + * + * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_min (const hb_set_t *set) +{ + return set->get_min (); +} + +/** + * hb_set_get_max: + * @set: a set. + * + * Finds the maximum number in the set. + * + * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_max (const hb_set_t *set) +{ + return set->get_max (); +} + +/** + * hb_set_next: + * @set: a set. + * @codepoint: (inout): + * + * + * + * Return value: whether there was a next value. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->next (codepoint); +} + +/** + * hb_set_next_range: + * @set: a set. + * @first: (out): output first codepoint in the range. + * @last: (inout): input current last and output last codepoint in the range. + * + * Gets the next consecutive range of numbers in @set that + * are greater than current value of @last. + * + * Return value: whether there was a next range. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last) +{ + return set->next_range (first, last); +} diff --git a/gfx/harfbuzz/src/hb-set.h b/gfx/harfbuzz/src/hb-set.h new file mode 100644 index 000000000..2164c1a65 --- /dev/null +++ b/gfx/harfbuzz/src/hb-set.h @@ -0,0 +1,157 @@ +/* + * 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 + + +/* + * Since: 0.9.21 + */ +#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1) + +typedef struct hb_set_t hb_set_t; + + +HB_EXTERN hb_set_t * +hb_set_create (void); + +HB_EXTERN hb_set_t * +hb_set_get_empty (void); + +HB_EXTERN hb_set_t * +hb_set_reference (hb_set_t *set); + +HB_EXTERN void +hb_set_destroy (hb_set_t *set); + +HB_EXTERN 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); + +HB_EXTERN void * +hb_set_get_user_data (hb_set_t *set, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_set_allocation_successful (const hb_set_t *set); + +HB_EXTERN void +hb_set_clear (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_empty (const hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_has (const 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. */ +HB_EXTERN void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_set (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_union (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_invert (hb_set_t *set); + +HB_EXTERN unsigned int +hb_set_get_population (const hb_set_t *set); + +/* Returns -1 if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_min (const hb_set_t *set); + +/* Returns -1 if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_max (const hb_set_t *set); + +/* Pass -1 in to get started. */ +HB_EXTERN hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint); + +/* Pass -1 for first and last to get started. */ +HB_EXTERN hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); + + +HB_END_DECLS + +#endif /* HB_SET_H */ diff --git a/gfx/harfbuzz/src/hb-shape-plan-private.hh b/gfx/harfbuzz/src/hb-shape-plan-private.hh new file mode 100644 index 000000000..aa0413a27 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan-private.hh @@ -0,0 +1,67 @@ +/* + * 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_SHAPE_PLAN_PRIVATE_HH +#define HB_SHAPE_PLAN_PRIVATE_HH + +#include "hb-private.hh" +#include "hb-object-private.hh" +#include "hb-shaper-private.hh" + + +struct hb_shape_plan_t +{ + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t default_shaper_list; + hb_face_t *face_unsafe; /* We don't carry a reference to face. */ + hb_segment_properties_t props; + + hb_shape_func_t *shaper_func; + const char *shaper_name; + + hb_feature_t *user_features; + unsigned int num_user_features; + + int *coords; + unsigned int num_coords; + + struct hb_shaper_data_t shaper_data; +}; + +#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \ + , const hb_feature_t *user_features \ + , unsigned int num_user_features \ + , const int *coords \ + , unsigned int num_coords +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS + + +#endif /* HB_SHAPE_PLAN_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-shape-plan.cc b/gfx/harfbuzz/src/hb-shape-plan.cc new file mode 100644 index 000000000..600faaeb1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan.cc @@ -0,0 +1,585 @@ +/* + * 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-shape-plan-private.hh" +#include "hb-shaper-private.hh" +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" + + +#ifndef HB_DEBUG_SHAPE_PLAN +#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0) +#endif + + +#define HB_SHAPER_IMPLEMENT(shaper) \ + HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \ + HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font) +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + +static void +hb_shape_plan_plan (hb_shape_plan_t *shape_plan, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, + "num_features=%d num_coords=%d shaper_list=%p", + num_user_features, + num_coords, + shaper_list); + + const hb_shaper_pair_t *shapers = _hb_shapers_get (); + +#define HB_SHAPER_PLAN(shaper) \ + HB_STMT_START { \ + if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \ + HB_SHAPER_DATA (shaper, shape_plan) = \ + HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \ + user_features, num_user_features, \ + coords, num_coords); \ + shape_plan->shaper_func = _hb_##shaper##_shape; \ + shape_plan->shaper_name = #shaper; \ + return; \ + } \ + } HB_STMT_END + + if (likely (!shaper_list)) { + for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++) + if (0) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shapers[i].func == _hb_##shaper##_shape) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } else { + for (; *shaper_list; shaper_list++) + if (0) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (0 == strcmp (*shaper_list, #shaper)) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + +#undef HB_SHAPER_PLAN +} + + +/* + * hb_shape_plan_t + */ + +/** + * hb_shape_plan_create: (Xconstructor) + * @face: + * @props: + * @user_features: (array length=num_user_features): + * @num_user_features: + * @shaper_list: (array zero-terminated=1): + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create2 (face, props, + user_features, num_user_features, + NULL, 0, + shaper_list); +} + +hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *orig_coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, NULL, + "face=%p num_features=%d num_coords=%d shaper_list=%p", + face, + num_user_features, + num_coords, + shaper_list); + + hb_shape_plan_t *shape_plan; + hb_feature_t *features = NULL; + int *coords = NULL; + + if (unlikely (!face)) + face = hb_face_get_empty (); + if (unlikely (!props)) + return hb_shape_plan_get_empty (); + if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t)))) + return hb_shape_plan_get_empty (); + if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int)))) + { + free (features); + return hb_shape_plan_get_empty (); + } + if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) + { + free (coords); + free (features); + return hb_shape_plan_get_empty (); + } + + assert (props->direction != HB_DIRECTION_INVALID); + + hb_face_make_immutable (face); + shape_plan->default_shaper_list = shaper_list == NULL; + shape_plan->face_unsafe = face; + shape_plan->props = *props; + shape_plan->num_user_features = num_user_features; + shape_plan->user_features = features; + if (num_user_features) + memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); + shape_plan->num_coords = num_coords; + shape_plan->coords = coords; + if (num_coords) + memcpy (coords, orig_coords, num_coords * sizeof (int)); + + hb_shape_plan_plan (shape_plan, + user_features, num_user_features, + coords, num_coords, + shaper_list); + + return shape_plan; +} + +/** + * hb_shape_plan_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_get_empty (void) +{ + static const hb_shape_plan_t _hb_shape_plan_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* default_shaper_list */ + NULL, /* face */ + HB_SEGMENT_PROPERTIES_DEFAULT, /* props */ + + NULL, /* shaper_func */ + NULL, /* shaper_name */ + + NULL, /* user_features */ + 0, /* num_user_featurs */ + + NULL, /* coords */ + 0, /* num_coords */ + + { +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + }; + + return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil); +} + +/** + * hb_shape_plan_reference: (skip) + * @shape_plan: a shape plan. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan) +{ + return hb_object_reference (shape_plan); +} + +/** + * hb_shape_plan_destroy: (skip) + * @shape_plan: a shape plan. + * + * + * + * Since: 0.9.7 + **/ +void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) +{ + if (!hb_object_destroy (shape_plan)) return; + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + free (shape_plan->user_features); + free (shape_plan->coords); + + free (shape_plan); +} + +/** + * hb_shape_plan_set_user_data: (skip) + * @shape_plan: a shape plan. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (shape_plan, key, data, destroy, replace); +} + +/** + * hb_shape_plan_get_user_data: (skip) + * @shape_plan: a shape plan. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.7 + **/ +void * +hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (shape_plan, key); +} + + +/** + * hb_shape_plan_execute: + * @shape_plan: a shape plan. + * @font: a font. + * @buffer: a buffer. + * @features: (array length=num_features): + * @num_features: + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, + "num_features=%d shaper_func=%p, shaper_name=%s", + num_features, + shape_plan->shaper_func, + shape_plan->shaper_name); + + if (unlikely (!buffer->len)) + return true; + + assert (!hb_object_is_inert (buffer)); + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); + + if (unlikely (hb_object_is_inert (shape_plan))) + return false; + + assert (shape_plan->face_unsafe == font->face); + assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props)); + +#define HB_SHAPER_EXECUTE(shaper) \ + HB_STMT_START { \ + return HB_SHAPER_DATA (shaper, shape_plan) && \ + hb_##shaper##_shaper_font_data_ensure (font) && \ + _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \ + } HB_STMT_END + + if (0) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shape_plan->shaper_func == _hb_##shaper##_shape) \ + HB_SHAPER_EXECUTE (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +#undef HB_SHAPER_EXECUTE + + return false; +} + + +/* + * caching + */ + +#if 0 +static unsigned int +hb_shape_plan_hash (const hb_shape_plan_t *shape_plan) +{ + return hb_segment_properties_hash (&shape_plan->props) + + shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func; +} +#endif + +/* User-feature caching is currently somewhat dumb: + * it only finds matches where the feature array is identical, + * not cases where the feature lists would be compatible for plan purposes + * but have different ranges, for example. + */ +struct hb_shape_plan_proposal_t +{ + const hb_segment_properties_t props; + const char * const *shaper_list; + const hb_feature_t *user_features; + unsigned int num_user_features; + const int *coords; + unsigned int num_coords; + hb_shape_func_t *shaper_func; +}; + +static inline hb_bool_t +hb_shape_plan_user_features_match (const hb_shape_plan_t *shape_plan, + const hb_shape_plan_proposal_t *proposal) +{ + if (proposal->num_user_features != shape_plan->num_user_features) + return false; + for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++) + if (proposal->user_features[i].tag != shape_plan->user_features[i].tag || + proposal->user_features[i].value != shape_plan->user_features[i].value || + proposal->user_features[i].start != shape_plan->user_features[i].start || + proposal->user_features[i].end != shape_plan->user_features[i].end) + return false; + return true; +} + +static inline hb_bool_t +hb_shape_plan_coords_match (const hb_shape_plan_t *shape_plan, + const hb_shape_plan_proposal_t *proposal) +{ + if (proposal->num_coords != shape_plan->num_coords) + return false; + for (unsigned int i = 0, n = proposal->num_coords; i < n; i++) + if (proposal->coords[i] != shape_plan->coords[i]) + return false; + return true; +} + +static hb_bool_t +hb_shape_plan_matches (const hb_shape_plan_t *shape_plan, + const hb_shape_plan_proposal_t *proposal) +{ + return hb_segment_properties_equal (&shape_plan->props, &proposal->props) && + hb_shape_plan_user_features_match (shape_plan, proposal) && + hb_shape_plan_coords_match (shape_plan, proposal) && + ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) || + (shape_plan->shaper_func == proposal->shaper_func)); +} + +static inline hb_bool_t +hb_non_global_user_features_present (const hb_feature_t *user_features, + unsigned int num_user_features) +{ + while (num_user_features) + if (user_features->start != 0 || user_features->end != (unsigned int) -1) + return true; + else + num_user_features--, user_features++; + return false; +} + +static inline hb_bool_t +hb_coords_present (const int *coords, + unsigned int num_coords) +{ + return num_coords != 0; +} + +/** + * hb_shape_plan_create_cached: + * @face: + * @props: + * @user_features: (array length=num_user_features): + * @num_user_features: + * @shaper_list: (array zero-terminated=1): + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create_cached2 (face, props, + user_features, num_user_features, + NULL, 0, + shaper_list); +} + +hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, NULL, + "face=%p num_features=%d shaper_list=%p", + face, + num_user_features, + shaper_list); + + hb_shape_plan_proposal_t proposal = { + *props, + shaper_list, + user_features, + num_user_features, + NULL + }; + + if (shaper_list) { + /* Choose shaper. Adapted from hb_shape_plan_plan(). + * Must choose shaper exactly the same way as that function. */ + for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++) + if (0) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (0 == strcmp (*shaper_item, #shaper) && \ + hb_##shaper##_shaper_face_data_ensure (face)) \ + { \ + proposal.shaper_func = _hb_##shaper##_shape; \ + break; \ + } +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + if (unlikely (!proposal.shaper_func)) + return hb_shape_plan_get_empty (); + } + + +retry: + hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans); + for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next) + if (hb_shape_plan_matches (node->shape_plan, &proposal)) + { + DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache"); + return hb_shape_plan_reference (node->shape_plan); + } + + /* Not found. */ + + hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props, + user_features, num_user_features, + coords, num_coords, + shaper_list); + + /* Don't add to the cache if face is inert. */ + if (unlikely (hb_object_is_inert (face))) + return shape_plan; + + /* Don't add the plan to the cache if there were user features with non-global ranges */ + if (hb_non_global_user_features_present (user_features, num_user_features)) + return shape_plan; + /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */ + if (hb_coords_present (coords, num_coords)) + return shape_plan; + + hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t)); + if (unlikely (!node)) + return shape_plan; + + node->shape_plan = shape_plan; + node->next = cached_plan_nodes; + + if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) { + hb_shape_plan_destroy (shape_plan); + free (node); + goto retry; + } + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache"); + + return hb_shape_plan_reference (shape_plan); +} + +/** + * hb_shape_plan_get_shaper: + * @shape_plan: a shape plan. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.7 + **/ +const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) +{ + return shape_plan->shaper_name; +} diff --git a/gfx/harfbuzz/src/hb-shape-plan.h b/gfx/harfbuzz/src/hb-shape-plan.h new file mode 100644 index 000000000..b62ae7ca3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan.h @@ -0,0 +1,108 @@ +/* + * 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_SHAPE_PLAN_H +#define HB_SHAPE_PLAN_H + +#include "hb-common.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +typedef struct hb_shape_plan_t hb_shape_plan_t; + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_get_empty (void); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan); + +HB_EXTERN void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan); + +HB_EXTERN hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key); + + +HB_EXTERN hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan); + + +HB_END_DECLS + +#endif /* HB_SHAPE_PLAN_H */ diff --git a/gfx/harfbuzz/src/hb-shape.cc b/gfx/harfbuzz/src/hb-shape.cc new file mode 100644 index 000000000..706f14420 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape.cc @@ -0,0 +1,409 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" +#include "hb-buffer-private.hh" +#include "hb-font-private.hh" + +/** + * SECTION:hb-shape + * @title: Shaping + * @short_description: Conversion of text strings into positioned glyphs + * @include: hb.h + * + * Shaping is the central operation of HarfBuzz. Shaping operates on buffers, + * which are sequences of Unicode characters that use the same font and have + * the same text direction, script and language. After shaping the buffer + * contains the output glyphs and their positions. + **/ + +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} + +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); + + if (*pp == end || **pp != c) + return false; + + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + unsigned int v; + + /* Intentionally use strtol instead of strtoul, such that + * -1 turns into "big number"... */ + errno = 0; + v = strtol (p, &pend, 0); + if (errno || p == pend) + return false; + + *pv = v; + *pp += pend - p; + return true; +} + +static bool +parse_bool (const char **pp, const char *end, unsigned int *pv) +{ + parse_space (pp, end); + + const char *p = *pp; + while (*pp < end && ISALPHA(**pp)) + (*pp)++; + + /* CSS allows on/off as aliases 1/0. */ + if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) + *pv = 1; + else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) + *pv = 0; + else + return false; + + return true; +} + +static bool +parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) +{ + if (parse_char (pp, end, '-')) + feature->value = 0; + else { + parse_char (pp, end, '+'); + feature->value = 1; + } + + return true; +} + +static bool +parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + char quote = 0; + + if (*pp < end && (**pp == '\'' || **pp == '"')) + { + quote = **pp; + (*pp)++; + } + + const char *p = *pp; + while (*pp < end && ISALNUM(**pp)) + (*pp)++; + + if (p == *pp || *pp - p > 4) + return false; + + feature->tag = hb_tag_from_string (p, *pp - p); + + if (quote) + { + /* CSS expects exactly four bytes. And we only allow quotations for + * CSS compatibility. So, enforce the length. */ + if (*pp - p != 4) + return false; + if (*pp == end || **pp != quote) + return false; + (*pp)++; + } + + return true; +} + +static bool +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + bool has_start; + + feature->start = 0; + feature->end = (unsigned int) -1; + + if (!parse_char (pp, end, '[')) + return true; + + has_start = parse_uint (pp, end, &feature->start); + + if (parse_char (pp, end, ':')) { + parse_uint (pp, end, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; + } + + return parse_char (pp, end, ']'); +} + +static bool +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + bool had_equal = parse_char (pp, end, '='); + bool had_value = parse_uint (pp, end, &feature->value) || + parse_bool (pp, end, &feature->value); + /* CSS doesn't use equal-sign between tag and value. + * If there was an equal-sign, then there *must* be a value. + * A value without an eqaul-sign is ok, but not required. */ + return !had_equal || had_value; +} + + +static bool +parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, end, feature) && + parse_feature_tag (pp, end, feature) && + parse_feature_indices (pp, end, feature) && + parse_feature_value_postfix (pp, end, feature) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_feature_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is %NULL terminated + * @feature: (out): the #hb_feature_t to initialize with the parsed values + * + * Parses a string into a #hb_feature_t. + * + * TODO: document the syntax here. + * + * Return value: + * %true if @str is successfully parsed, %false otherwise. + * + * Since: 0.9.5 + **/ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + hb_feature_t feat; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_feature (&str, str + len, &feat))) + { + if (feature) + *feature = feat; + return true; + } + + if (feature) + memset (feature, 0, sizeof (*feature)); + return false; +} + +/** + * hb_feature_to_string: + * @feature: an #hb_feature_t to convert + * @buf: (array length=size) (out): output string + * @size: the allocated size of @buf + * + * Converts a #hb_feature_t into a %NULL-terminated string in the format + * understood by hb_feature_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 0.9.5 + **/ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + if (feature->value == 0) + s[len++] = '-'; + hb_tag_to_string (feature->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + if (feature->start != 0 || feature->end != (unsigned int) -1) + { + s[len++] = '['; + if (feature->start) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != (unsigned int) -1) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = MIN (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} + + +static const char **static_shaper_list; + +#ifdef HB_USE_ATEXIT +static +void free_static_shaper_list (void) +{ + free (static_shaper_list); +} +#endif + +/** + * hb_shape_list_shapers: + * + * Retrieves the list of shapers supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): an array of + * constant strings + * + * Since: 0.9.2 + **/ +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 + HB_SHAPERS_COUNT, sizeof (const char *)); + if (unlikely (!shaper_list)) { + static const char *nil_shaper_list[] = {NULL}; + return nil_shaper_list; + } + + const hb_shaper_pair_t *shapers = _hb_shapers_get (); + unsigned int i; + for (i = 0; i < HB_SHAPERS_COUNT; 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 HB_USE_ATEXIT + atexit (free_static_shaper_list); /* First person registers atexit() callback. */ +#endif + } + + return shaper_list; +} + + +/** + * hb_shape_full: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (allow-none): an array of user + * specified #hb_feature_t or %NULL + * @num_features: the length of @features array + * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated + * array of shapers to use or %NULL + * + * See hb_shape() for details. If @shaper_list is not %NULL, the specified + * shapers will be used in the given order, otherwise the default shapers list + * will be used. + * + * Return value: %FALSE if all shapers failed, %TRUE otherwise + * + * Since: 0.9.2 + **/ +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_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props, + features, num_features, + font->coords, font->num_coords, + shaper_list); + hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); + hb_shape_plan_destroy (shape_plan); + + if (res) + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + return res; +} + +/** + * hb_shape: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (allow-none): an array of user + * specified #hb_feature_t or %NULL + * @num_features: the length of @features array + * + * Shapes @buffer using @font turning its Unicode characters content to + * positioned glyphs. If @features is not %NULL, it will be used to control the + * features applied during shaping. + * + * Since: 0.9.2 + **/ +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); +} diff --git a/gfx/harfbuzz/src/hb-shape.h b/gfx/harfbuzz/src/hb-shape.h new file mode 100644 index 000000000..53bb845bf --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_SHAPE_H +#define HB_SHAPE_H + +#include "hb-common.h" +#include "hb-buffer.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + + +typedef struct hb_feature_t { + hb_tag_t tag; + uint32_t value; + unsigned int start; + unsigned int end; +} hb_feature_t; + +HB_EXTERN hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +HB_EXTERN void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + + +HB_EXTERN void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN 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_EXTERN const char ** +hb_shape_list_shapers (void); + + +HB_END_DECLS + +#endif /* HB_SHAPE_H */ diff --git a/gfx/harfbuzz/src/hb-shaper-impl-private.hh b/gfx/harfbuzz/src/hb-shaper-impl-private.hh new file mode 100644 index 000000000..7844081e9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper-impl-private.hh @@ -0,0 +1,43 @@ +/* + * 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_SHAPER_IMPL_PRIVATE_HH +#define HB_SHAPER_IMPL_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" + + +#ifdef HB_SHAPER +#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object) +#endif + + +#endif /* HB_SHAPER_IMPL_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-shaper-list.hh b/gfx/harfbuzz/src/hb-shaper-list.hh new file mode 100644 index 000000000..b0835d31a --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper-list.hh @@ -0,0 +1,58 @@ +/* + * 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_SHAPER_LIST_HH +#define HB_SHAPER_LIST_HH +#endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */ + +/* v--- Add new shapers in the right place here. */ + +#ifdef HAVE_GRAPHITE2 +/* Only picks up fonts that have a "Silf" table. */ +HB_SHAPER_IMPLEMENT (graphite2) +#endif +#ifdef HAVE_CORETEXT +/* Only picks up fonts that have a "mort" or "morx" table. */ +HB_SHAPER_IMPLEMENT (coretext_aat) +#endif + +#ifdef HAVE_OT +HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */ +#endif + +#ifdef HAVE_UNISCRIBE +HB_SHAPER_IMPLEMENT (uniscribe) +#endif +#ifdef HAVE_DIRECTWRITE +HB_SHAPER_IMPLEMENT (directwrite) +#endif +#ifdef HAVE_CORETEXT +HB_SHAPER_IMPLEMENT (coretext) +#endif + +#ifdef HAVE_FALLBACK +HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ +#endif diff --git a/gfx/harfbuzz/src/hb-shaper-private.hh b/gfx/harfbuzz/src/hb-shaper-private.hh new file mode 100644 index 000000000..d1d1146da --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper-private.hh @@ -0,0 +1,108 @@ +/* + * 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_SHAPER_PRIVATE_HH +#define HB_SHAPER_PRIVATE_HH + +#include "hb-private.hh" + +typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +#define HB_SHAPER_IMPLEMENT(name) \ + extern "C" HB_INTERNAL hb_shape_func_t _hb_##name##_shape; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_shaper_pair_t { + char name[16]; + hb_shape_func_t *func; +}; + +HB_INTERNAL const hb_shaper_pair_t * +_hb_shapers_get (void); + + +/* For embedding in face / font / ... */ +struct hb_shaper_data_t { +#define HB_SHAPER_IMPLEMENT(shaper) void *shaper; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; + +#define HB_SHAPERS_COUNT (sizeof (hb_shaper_data_t) / sizeof (void *)) + +/* Means: succeeded, but don't need to keep any data. */ +#define HB_SHAPER_DATA_SUCCEEDED ((void *) +1) + +/* Means: tried but failed to create. */ +#define HB_SHAPER_DATA_INVALID ((void *) -1) +#define HB_SHAPER_DATA_IS_INVALID(data) ((void *) (data) == HB_SHAPER_DATA_INVALID) + +#define HB_SHAPER_DATA_TYPE(shaper, object) struct hb_##shaper##_shaper_##object##_data_t +#define HB_SHAPER_DATA_INSTANCE(shaper, object, instance) (* (HB_SHAPER_DATA_TYPE(shaper, object) **) &(instance)->shaper_data.shaper) +#define HB_SHAPER_DATA(shaper, object) HB_SHAPER_DATA_INSTANCE (shaper, object, object) +#define HB_SHAPER_DATA_CREATE_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_create +#define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_destroy + +#define HB_SHAPER_DATA_PROTOTYPE(shaper, object) \ + HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \ + extern "C" HB_INTERNAL HB_SHAPER_DATA_TYPE (shaper, object) * \ + HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS); \ + extern "C" HB_INTERNAL void \ + HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *data) + +#define HB_SHAPER_DATA_DESTROY(shaper, object) \ + if (HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object)) \ + if (data != HB_SHAPER_DATA_INVALID && data != HB_SHAPER_DATA_SUCCEEDED) \ + HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); + +#define HB_SHAPER_DATA_ENSURE_DECLARE(shaper, object) \ +static inline bool \ +hb_##shaper##_shaper_##object##_data_ensure (hb_##object##_t *object) \ +{\ + retry: \ + HB_SHAPER_DATA_TYPE (shaper, object) *data = (HB_SHAPER_DATA_TYPE (shaper, object) *) hb_atomic_ptr_get (&HB_SHAPER_DATA (shaper, object)); \ + if (unlikely (!data)) { \ + data = HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (object); \ + if (unlikely (!data)) \ + data = (HB_SHAPER_DATA_TYPE (shaper, object) *) HB_SHAPER_DATA_INVALID; \ + if (!hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), NULL, data)) { \ + if (data && \ + data != HB_SHAPER_DATA_INVALID && \ + data != HB_SHAPER_DATA_SUCCEEDED) \ + HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \ + goto retry; \ + } \ + } \ + return data != NULL && !HB_SHAPER_DATA_IS_INVALID (data); \ +} + + +#endif /* HB_SHAPER_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-shaper.cc b/gfx/harfbuzz/src/hb-shaper.cc new file mode 100644 index 000000000..b25566d8a --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper.cc @@ -0,0 +1,111 @@ +/* + * 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-private.hh" +#include "hb-shaper-private.hh" +#include "hb-atomic-private.hh" + + +static const hb_shaper_pair_t all_shapers[] = { +#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape}, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; + + +/* Thread-safe, lock-free, shapers */ + +static const hb_shaper_pair_t *static_shapers; + +#ifdef HB_USE_ATEXIT +static +void free_static_shapers (void) +{ + if (unlikely (static_shapers != all_shapers)) + free ((void *) static_shapers); +} +#endif + +const hb_shaper_pair_t * +_hb_shapers_get (void) +{ +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) { + (void) hb_atomic_ptr_cmpexch (&static_shapers, NULL, &all_shapers[0]); + return (const hb_shaper_pair_t *) all_shapers; + } + + /* Not found; allocate one. */ + shapers = (hb_shaper_pair_t *) calloc (1, sizeof (all_shapers)); + if (unlikely (!shapers)) { + (void) hb_atomic_ptr_cmpexch (&static_shapers, NULL, &all_shapers[0]); + 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 (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) { + free (shapers); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_static_shapers); /* First person registers atexit() callback. */ +#endif + } + + return shapers; +} diff --git a/gfx/harfbuzz/src/hb-ucdn.cc b/gfx/harfbuzz/src/hb-ucdn.cc new file mode 100644 index 000000000..a884e3ffd --- /dev/null +++ b/gfx/harfbuzz/src/hb-ucdn.cc @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hb-private.hh" + +#include "hb-unicode-private.hh" + +#include "ucdn.h" + +static const hb_script_t ucdn_script_translate[] = +{ + HB_SCRIPT_COMMON, + HB_SCRIPT_LATIN, + HB_SCRIPT_GREEK, + HB_SCRIPT_CYRILLIC, + HB_SCRIPT_ARMENIAN, + HB_SCRIPT_HEBREW, + HB_SCRIPT_ARABIC, + HB_SCRIPT_SYRIAC, + HB_SCRIPT_THAANA, + HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_BENGALI, + HB_SCRIPT_GURMUKHI, + HB_SCRIPT_GUJARATI, + HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, + HB_SCRIPT_TELUGU, + HB_SCRIPT_KANNADA, + HB_SCRIPT_MALAYALAM, + HB_SCRIPT_SINHALA, + HB_SCRIPT_THAI, + HB_SCRIPT_LAO, + HB_SCRIPT_TIBETAN, + HB_SCRIPT_MYANMAR, + HB_SCRIPT_GEORGIAN, + HB_SCRIPT_HANGUL, + HB_SCRIPT_ETHIOPIC, + HB_SCRIPT_CHEROKEE, + HB_SCRIPT_CANADIAN_SYLLABICS, + HB_SCRIPT_OGHAM, + HB_SCRIPT_RUNIC, + HB_SCRIPT_KHMER, + HB_SCRIPT_MONGOLIAN, + HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KATAKANA, + HB_SCRIPT_BOPOMOFO, + HB_SCRIPT_HAN, + HB_SCRIPT_YI, + HB_SCRIPT_OLD_ITALIC, + HB_SCRIPT_GOTHIC, + HB_SCRIPT_DESERET, + HB_SCRIPT_INHERITED, + HB_SCRIPT_TAGALOG, + HB_SCRIPT_HANUNOO, + HB_SCRIPT_BUHID, + HB_SCRIPT_TAGBANWA, + HB_SCRIPT_LIMBU, + HB_SCRIPT_TAI_LE, + HB_SCRIPT_LINEAR_B, + HB_SCRIPT_UGARITIC, + HB_SCRIPT_SHAVIAN, + HB_SCRIPT_OSMANYA, + HB_SCRIPT_CYPRIOT, + HB_SCRIPT_BRAILLE, + HB_SCRIPT_BUGINESE, + HB_SCRIPT_COPTIC, + HB_SCRIPT_NEW_TAI_LUE, + HB_SCRIPT_GLAGOLITIC, + HB_SCRIPT_TIFINAGH, + HB_SCRIPT_SYLOTI_NAGRI, + HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_KHAROSHTHI, + HB_SCRIPT_BALINESE, + HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_PHOENICIAN, + HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_NKO, + HB_SCRIPT_SUNDANESE, + HB_SCRIPT_LEPCHA, + HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_VAI, + HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_REJANG, + HB_SCRIPT_LYCIAN, + HB_SCRIPT_CARIAN, + HB_SCRIPT_LYDIAN, + HB_SCRIPT_CHAM, + HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, + HB_SCRIPT_AVESTAN, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, + HB_SCRIPT_SAMARITAN, + HB_SCRIPT_LISU, + HB_SCRIPT_BAMUM, + HB_SCRIPT_JAVANESE, + HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_OLD_SOUTH_ARABIAN, + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, + HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_KAITHI, + HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, + HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, + HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, + HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, + HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI, + HB_SCRIPT_UNKNOWN, + HB_SCRIPT_BASSA_VAH, + HB_SCRIPT_CAUCASIAN_ALBANIAN, + HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, + HB_SCRIPT_GRANTHA, + HB_SCRIPT_KHOJKI, + HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, + HB_SCRIPT_MAHAJANI, + HB_SCRIPT_MANICHAEAN, + HB_SCRIPT_MENDE_KIKAKUI, + HB_SCRIPT_MODI, + HB_SCRIPT_MRO, + HB_SCRIPT_NABATAEAN, + HB_SCRIPT_OLD_NORTH_ARABIAN, + HB_SCRIPT_OLD_PERMIC, + HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, + HB_SCRIPT_PAU_CIN_HAU, + HB_SCRIPT_PSALTER_PAHLAVI, + HB_SCRIPT_SIDDHAM, + HB_SCRIPT_TIRHUTA, + HB_SCRIPT_WARANG_CITI, + HB_SCRIPT_AHOM, + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, + HB_SCRIPT_HATRAN, + HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, + HB_SCRIPT_SIGNWRITING, + HB_SCRIPT_ADLAM, + HB_SCRIPT_BHAIKSUKI, + HB_SCRIPT_MARCHEN, + HB_SCRIPT_NEWA, + HB_SCRIPT_OSAGE, + HB_SCRIPT_TANGUT, +}; + +static hb_unicode_combining_class_t +hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode); +} + +static unsigned int +hb_ucdn_eastasian_width(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + int w = ucdn_get_east_asian_width(unicode); + return (w == UCDN_EAST_ASIAN_F || w == UCDN_EAST_ASIAN_W) ? 2 : 1; +} + +static hb_unicode_general_category_t +hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_general_category_t)ucdn_get_general_category(unicode); +} + +static hb_codepoint_t +hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return ucdn_mirror(unicode); +} + +static hb_script_t +hb_ucdn_script(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return ucdn_script_translate[ucdn_get_script(unicode)]; +} + +static hb_bool_t +hb_ucdn_compose(hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ + return ucdn_compose(ab, a, b); +} + +static hb_bool_t +hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ + return ucdn_decompose(ab, a, b); +} + +static unsigned int +hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, hb_codepoint_t *decomposed, + void *user_data HB_UNUSED) +{ + return ucdn_compat_decompose(u, decomposed); +} + +extern "C" HB_INTERNAL +hb_unicode_funcs_t * +hb_ucdn_get_unicode_funcs (void) +{ + static const hb_unicode_funcs_t _hb_ucdn_unicode_funcs = { + HB_OBJECT_HEADER_STATIC, + + NULL, /* parent */ + true, /* immutable */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_ucdn_##name, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } + }; + + return const_cast<hb_unicode_funcs_t *> (&_hb_ucdn_unicode_funcs); +} + diff --git a/gfx/harfbuzz/src/hb-unicode-private.hh b/gfx/harfbuzz/src/hb-unicode-private.hh new file mode 100644 index 000000000..a4d118b6d --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode-private.hh @@ -0,0 +1,370 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2010,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_UNICODE_PRIVATE_HH +#define HB_UNICODE_PRIVATE_HH + +#include "hb-private.hh" +#include "hb-object-private.hh" + + +extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; + +/* + * hb_unicode_funcs_t + */ + +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \ + HB_UNICODE_FUNC_IMPLEMENT (combining_class) \ + HB_UNICODE_FUNC_IMPLEMENT (eastasian_width) \ + HB_UNICODE_FUNC_IMPLEMENT (general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (script) \ + HB_UNICODE_FUNC_IMPLEMENT (compose) \ + HB_UNICODE_FUNC_IMPLEMENT (decompose) \ + HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility) \ + /* ^--- Add new callbacks here */ + +/* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */ +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \ + HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \ + /* ^--- Add new simple callbacks here */ + +struct hb_unicode_funcs_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_unicode_funcs_t *parent; + + bool immutable; + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + inline return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); } +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + + inline hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b, + hb_codepoint_t *ab) + { + *ab = 0; + if (unlikely (!a || !b)) return false; + return func.compose (this, a, b, ab, user_data.compose); + } + + inline hb_bool_t decompose (hb_codepoint_t ab, + hb_codepoint_t *a, hb_codepoint_t *b) + { + *a = ab; *b = 0; + return func.decompose (this, ab, a, b, user_data.decompose); + } + + inline unsigned int decompose_compatibility (hb_codepoint_t u, + hb_codepoint_t *decomposed) + { + unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility); + if (ret == 1 && u == decomposed[0]) { + decomposed[0] = 0; + return 0; + } + decomposed[ret] = 0; + return ret; + } + + + inline unsigned int + modified_combining_class (hb_codepoint_t unicode) + { + /* XXX This hack belongs to the Myanmar shaper. */ + if (unlikely (unicode == 0x1037u)) unicode = 0x103Au; + + /* XXX This hack belongs to the SEA shaper (for Tai Tham): + * Reorder SAKOT to ensure it comes after any tone marks. */ + if (unlikely (unicode == 0x1A60u)) return 254; + + /* XXX This hack belongs to the Tibetan shaper: + * Reorder PADMA to ensure it comes after any vowel marks. */ + if (unlikely (unicode == 0x0FC6u)) return 254; + /* Reorder TSA -PHRU to reorder before U+0F74 */ + if (unlikely (unicode == 0x0F39u)) return 127; + + return _hb_modified_combining_class[combining_class (unicode)]; + } + + static inline hb_bool_t + is_variation_selector (hb_codepoint_t unicode) + { + /* U+180B..180D MONGOLIAN FREE VARIATION SELECTORs are handled in the + * Arabic shaper. No need to match them here. */ + return unlikely (hb_in_ranges (unicode, + 0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */ + 0xE0100u, 0xE01EFu)); /* VARIATION SELECTOR-17..256 */ + } + + /* Default_Ignorable codepoints: + * + * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable, + * we do NOT want to hide them, as the way Uniscribe has implemented them + * is with regular spacing glyphs, and that's the way fonts are made to work. + * As such, we make exceptions for those four. + * + * Unicode 7.0: + * $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/' + * 00AD # Cf SOFT HYPHEN + * 034F # Mn COMBINING GRAPHEME JOINER + * 061C # Cf ARABIC LETTER MARK + * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER + * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA + * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE + * 180E # Cf MONGOLIAN VOWEL SEPARATOR + * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK + * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE + * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS + * 2065 # Cn <reserved-2065> + * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES + * 3164 # Lo HANGUL FILLER + * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 + * FEFF # Cf ZERO WIDTH NO-BREAK SPACE + * FFA0 # Lo HALFWIDTH HANGUL FILLER + * FFF0..FFF8 # Cn [9] <reserved-FFF0>..<reserved-FFF8> + * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP + * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE + * E0000 # Cn <reserved-E0000> + * E0001 # Cf LANGUAGE TAG + * E0002..E001F # Cn [30] <reserved-E0002>..<reserved-E001F> + * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG + * E0080..E00FF # Cn [128] <reserved-E0080>..<reserved-E00FF> + * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + * E01F0..E0FFF # Cn [3600] <reserved-E01F0>..<reserved-E0FFF> + */ + static inline hb_bool_t + is_default_ignorable (hb_codepoint_t ch) + { + hb_codepoint_t plane = ch >> 16; + if (likely (plane == 0)) + { + /* BMP */ + hb_codepoint_t page = ch >> 8; + switch (page) { + case 0x00: return unlikely (ch == 0x00ADu); + case 0x03: return unlikely (ch == 0x034Fu); + case 0x06: return unlikely (ch == 0x061Cu); + case 0x17: return hb_in_range (ch, 0x17B4u, 0x17B5u); + case 0x18: return hb_in_range (ch, 0x180Bu, 0x180Eu); + case 0x20: return hb_in_ranges (ch, 0x200Bu, 0x200Fu, + 0x202Au, 0x202Eu, + 0x2060u, 0x206Fu); + case 0xFE: return hb_in_range (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu; + case 0xFF: return hb_in_range (ch, 0xFFF0u, 0xFFF8u); + default: return false; + } + } + else + { + /* Other planes */ + switch (plane) { + case 0x01: return hb_in_ranges (ch, 0x1BCA0u, 0x1BCA3u, + 0x1D173u, 0x1D17Au); + case 0x0E: return hb_in_range (ch, 0xE0000u, 0xE0FFFu); + default: return false; + } + } + } + + /* Space estimates based on: + * http://www.unicode.org/charts/PDF/U2000.pdf + * https://www.microsoft.com/typography/developers/fdsspec/spaces.aspx + */ + enum space_t { + NOT_SPACE = 0, + SPACE_EM = 1, + SPACE_EM_2 = 2, + SPACE_EM_3 = 3, + SPACE_EM_4 = 4, + SPACE_EM_5 = 5, + SPACE_EM_6 = 6, + SPACE_EM_16 = 16, + SPACE_4_EM_18, /* 4/18th of an EM! */ + SPACE, + SPACE_FIGURE, + SPACE_PUNCTUATION, + SPACE_NARROW, + }; + static inline space_t + space_fallback_type (hb_codepoint_t u) + { + switch (u) + { + /* All GC=Zs chars that can use a fallback. */ + default: return NOT_SPACE; /* U+1680 OGHAM SPACE MARK */ + case 0x0020u: return SPACE; /* U+0020 SPACE */ + case 0x00A0u: return SPACE; /* U+00A0 NO-BREAK SPACE */ + case 0x2000u: return SPACE_EM_2; /* U+2000 EN QUAD */ + case 0x2001u: return SPACE_EM; /* U+2001 EM QUAD */ + case 0x2002u: return SPACE_EM_2; /* U+2002 EN SPACE */ + case 0x2003u: return SPACE_EM; /* U+2003 EM SPACE */ + case 0x2004u: return SPACE_EM_3; /* U+2004 THREE-PER-EM SPACE */ + case 0x2005u: return SPACE_EM_4; /* U+2005 FOUR-PER-EM SPACE */ + case 0x2006u: return SPACE_EM_6; /* U+2006 SIX-PER-EM SPACE */ + case 0x2007u: return SPACE_FIGURE; /* U+2007 FIGURE SPACE */ + case 0x2008u: return SPACE_PUNCTUATION; /* U+2008 PUNCTUATION SPACE */ + case 0x2009u: return SPACE_EM_5; /* U+2009 THIN SPACE */ + case 0x200Au: return SPACE_EM_16; /* U+200A HAIR SPACE */ + case 0x202Fu: return SPACE_NARROW; /* U+202F NARROW NO-BREAK SPACE */ + case 0x205Fu: return SPACE_4_EM_18; /* U+205F MEDIUM MATHEMATICAL SPACE */ + case 0x3000u: return SPACE_EM; /* U+3000 IDEOGRAPHIC SPACE */ + } + } + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } func; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) void *name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } destroy; +}; + + +extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil; + + +/* Modified combining marks */ + +/* Hebrew + * + * We permute the "fixed-position" classes 10-26 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 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */ +#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */ +#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */ +#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */ +#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats */ +#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam */ +#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */ +#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */ +#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */ +#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */ +#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */ + +/* + * Arabic + * + * Modify to move Shadda (ccc=33) before other marks. See: + * http://unicode.org/faq/normalization.html#8 + * http://unicode.org/faq/normalization.html#9 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */ +#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */ +#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */ +#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */ +#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */ +#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */ + +/* Syriac */ +#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */ + +/* Telugu + * + * Modify Telugu length marks (ccc=84, ccc=91). + * These are the only matras in the main Indic scripts range that have + * a non-zero ccc. That makes them reorder with the Halant that is + * ccc=9. Just zero them, we don't need them in our Indic shaper. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC84 0 /* length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC91 0 /* ai length mark */ + +/* Thai + * + * Modify U+0E38 and U+0E39 (ccc=103) to be reordered before U+0E3A (ccc=9). + * Assign 3, which is unassigned otherwise. + * Uniscribe does this reordering too. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */ + +/* Lao */ +#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */ + +/* Tibetan + * Modify U+0F74 (ccc=132) to reorder before ccc=130 marks. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */ +#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */ +#define HB_MODIFIED_COMBINING_CLASS_CCC132 128 /* sign u */ + + +/* Misc */ + +#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \ + (FLAG_SAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + +#define HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL(gen_cat) \ + (FLAG_SAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL))) + +#endif /* HB_UNICODE_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-unicode.cc b/gfx/harfbuzz/src/hb-unicode.cc new file mode 100644 index 000000000..d553a7172 --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode.cc @@ -0,0 +1,563 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2010,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-unicode-private.hh" + + + +/* + * hb_unicode_funcs_t + */ + +static hb_unicode_combining_class_t +hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; +} + +static unsigned int +hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 1; +} + +static hb_unicode_general_category_t +hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; +} + +static hb_codepoint_t +hb_unicode_mirroring_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return unicode; +} + +static hb_script_t +hb_unicode_script_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_SCRIPT_UNKNOWN; +} + +static hb_bool_t +hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a HB_UNUSED, + hb_codepoint_t b HB_UNUSED, + hb_codepoint_t *ab HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + +static hb_bool_t +hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab HB_UNUSED, + hb_codepoint_t *a HB_UNUSED, + hb_codepoint_t *b HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + + +static unsigned int +hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t u HB_UNUSED, + hb_codepoint_t *decomposed HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} + + +#define HB_UNICODE_FUNCS_IMPLEMENT_SET \ + HB_UNICODE_FUNCS_IMPLEMENT (glib) \ + HB_UNICODE_FUNCS_IMPLEMENT (icu) \ + HB_UNICODE_FUNCS_IMPLEMENT (ucdn) \ + HB_UNICODE_FUNCS_IMPLEMENT (nil) \ + /* ^--- Add new callbacks before nil */ + +#define hb_nil_get_unicode_funcs hb_unicode_funcs_get_empty + +/* Prototype them all */ +#define HB_UNICODE_FUNCS_IMPLEMENT(set) \ +extern "C" hb_unicode_funcs_t *hb_##set##_get_unicode_funcs (void); +HB_UNICODE_FUNCS_IMPLEMENT_SET +#undef HB_UNICODE_FUNCS_IMPLEMENT + + +hb_unicode_funcs_t * +hb_unicode_funcs_get_default (void) +{ +#define HB_UNICODE_FUNCS_IMPLEMENT(set) \ + return hb_##set##_get_unicode_funcs (); + +#if defined(HAVE_UCDN) + HB_UNICODE_FUNCS_IMPLEMENT(ucdn) +#elif defined(HAVE_GLIB) + HB_UNICODE_FUNCS_IMPLEMENT(glib) +#elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) + HB_UNICODE_FUNCS_IMPLEMENT(icu) +#else +#define HB_UNICODE_FUNCS_NIL 1 + HB_UNICODE_FUNCS_IMPLEMENT(nil) +#endif + +#undef HB_UNICODE_FUNCS_IMPLEMENT +} + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) +#error "Could not find any Unicode functions implementation, you have to provide your own" +#error "Consider building hb-ucdn.c. If you absolutely want to build without any, check the code." +#endif + +/** + * hb_unicode_funcs_create: (Xconstructor) + * @parent: (nullable): + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +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_get_empty (); + + if (!parent) + parent = hb_unicode_funcs_get_empty (); + + hb_unicode_funcs_make_immutable (parent); + ufuncs->parent = hb_unicode_funcs_reference (parent); + + ufuncs->func = parent->func; + + /* We can safely copy user_data from parent since we hold a reference + * onto it and it's immutable. We should not copy the destroy notifiers + * though. */ + ufuncs->user_data = parent->user_data; + + return ufuncs; +} + + +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_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_empty (void) +{ + return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil); +} + +/** + * hb_unicode_funcs_reference: (skip) + * @ufuncs: Unicode functions. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs) +{ + return hb_object_reference (ufuncs); +} + +/** + * hb_unicode_funcs_destroy: (skip) + * @ufuncs: Unicode functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) +{ + if (!hb_object_destroy (ufuncs)) return; + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + if (ufuncs->destroy.name) ufuncs->destroy.name (ufuncs->user_data.name); + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + hb_unicode_funcs_destroy (ufuncs->parent); + + free (ufuncs); +} + +/** + * hb_unicode_funcs_set_user_data: (skip) + * @ufuncs: Unicode functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +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_bool_t replace) +{ + return hb_object_set_user_data (ufuncs, key, data, destroy, replace); +} + +/** + * hb_unicode_funcs_get_user_data: (skip) + * @ufuncs: Unicode functions. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ufuncs, key); +} + + +/** + * hb_unicode_funcs_make_immutable: + * @ufuncs: Unicode functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs) +{ + if (unlikely (hb_object_is_inert (ufuncs))) + return; + + ufuncs->immutable = true; +} + +/** + * hb_unicode_funcs_is_immutable: + * @ufuncs: Unicode functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs) +{ + return ufuncs->immutable; +} + +/** + * hb_unicode_funcs_get_parent: + * @ufuncs: Unicode functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs) +{ + return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty (); +} + + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t *ufuncs, \ + hb_unicode_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (ufuncs->immutable) \ + return; \ + \ + if (ufuncs->destroy.name) \ + ufuncs->destroy.name (ufuncs->user_data.name); \ + \ + if (func) { \ + ufuncs->func.name = func; \ + ufuncs->user_data.name = user_data; \ + ufuncs->destroy.name = destroy; \ + } else { \ + ufuncs->func.name = ufuncs->parent->func.name; \ + ufuncs->user_data.name = ufuncs->parent->user_data.name; \ + ufuncs->destroy.name = NULL; \ + } \ +} + +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + \ +return_type \ +hb_unicode_##name (hb_unicode_funcs_t *ufuncs, \ + hb_codepoint_t unicode) \ +{ \ + return ufuncs->name (unicode); \ +} +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + +/** + * hb_unicode_compose: + * @ufuncs: Unicode functions. + * @a: + * @b: + * @ab: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return ufuncs->compose (a, b, ab); +} + +/** + * hb_unicode_decompose: + * @ufuncs: Unicode functions. + * @ab: + * @a: (out): + * @b: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return ufuncs->decompose (ab, a, b); +} + +/** + * hb_unicode_decompose_compatibility: + * @ufuncs: Unicode functions. + * @u: + * @decomposed: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed) +{ + return ufuncs->decompose_compatibility (u, decomposed); +} + + +/* See hb-unicode-private.hh for details. */ +const uint8_t +_hb_modified_combining_class[256] = +{ + 0, /* HB_UNICODE_COMBINING_CLASS_NOT_REORDERED */ + 1, /* HB_UNICODE_COMBINING_CLASS_OVERLAY */ + 2, 3, 4, 5, 6, + 7, /* HB_UNICODE_COMBINING_CLASS_NUKTA */ + 8, /* HB_UNICODE_COMBINING_CLASS_KANA_VOICING */ + 9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */ + + /* Hebrew */ + HB_MODIFIED_COMBINING_CLASS_CCC10, + HB_MODIFIED_COMBINING_CLASS_CCC11, + HB_MODIFIED_COMBINING_CLASS_CCC12, + HB_MODIFIED_COMBINING_CLASS_CCC13, + HB_MODIFIED_COMBINING_CLASS_CCC14, + HB_MODIFIED_COMBINING_CLASS_CCC15, + HB_MODIFIED_COMBINING_CLASS_CCC16, + HB_MODIFIED_COMBINING_CLASS_CCC17, + HB_MODIFIED_COMBINING_CLASS_CCC18, + HB_MODIFIED_COMBINING_CLASS_CCC19, + HB_MODIFIED_COMBINING_CLASS_CCC20, + HB_MODIFIED_COMBINING_CLASS_CCC21, + HB_MODIFIED_COMBINING_CLASS_CCC22, + HB_MODIFIED_COMBINING_CLASS_CCC23, + HB_MODIFIED_COMBINING_CLASS_CCC24, + HB_MODIFIED_COMBINING_CLASS_CCC25, + HB_MODIFIED_COMBINING_CLASS_CCC26, + + /* Arabic */ + HB_MODIFIED_COMBINING_CLASS_CCC27, + HB_MODIFIED_COMBINING_CLASS_CCC28, + HB_MODIFIED_COMBINING_CLASS_CCC29, + HB_MODIFIED_COMBINING_CLASS_CCC30, + HB_MODIFIED_COMBINING_CLASS_CCC31, + HB_MODIFIED_COMBINING_CLASS_CCC32, + HB_MODIFIED_COMBINING_CLASS_CCC33, + HB_MODIFIED_COMBINING_CLASS_CCC34, + HB_MODIFIED_COMBINING_CLASS_CCC35, + + /* Syriac */ + HB_MODIFIED_COMBINING_CLASS_CCC36, + + 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, + + /* Telugu */ + HB_MODIFIED_COMBINING_CLASS_CCC84, + 85, 86, 87, 88, 89, 90, + HB_MODIFIED_COMBINING_CLASS_CCC91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + + /* Thai */ + HB_MODIFIED_COMBINING_CLASS_CCC103, + 104, 105, 106, + HB_MODIFIED_COMBINING_CLASS_CCC107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + + /* Lao */ + HB_MODIFIED_COMBINING_CLASS_CCC118, + 119, 120, 121, + HB_MODIFIED_COMBINING_CLASS_CCC122, + 123, 124, 125, 126, 127, 128, + + /* Tibetan */ + HB_MODIFIED_COMBINING_CLASS_CCC129, + HB_MODIFIED_COMBINING_CLASS_CCC130, + 131, + HB_MODIFIED_COMBINING_CLASS_CCC132, + 133, 134, 135, 136, 137, 138, 139, + + + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + + 200, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT */ + 201, + 202, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW */ + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE */ + 215, + 216, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT */ + 217, + 218, /* HB_UNICODE_COMBINING_CLASS_BELOW_LEFT */ + 219, + 220, /* HB_UNICODE_COMBINING_CLASS_BELOW */ + 221, + 222, /* HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT */ + 223, + 224, /* HB_UNICODE_COMBINING_CLASS_LEFT */ + 225, + 226, /* HB_UNICODE_COMBINING_CLASS_RIGHT */ + 227, + 228, /* HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT */ + 229, + 230, /* HB_UNICODE_COMBINING_CLASS_ABOVE */ + 231, + 232, /* HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT */ + 233, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW */ + 234, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE */ + 235, 236, 237, 238, 239, + 240, /* HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT */ + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, /* HB_UNICODE_COMBINING_CLASS_INVALID */ +}; diff --git a/gfx/harfbuzz/src/hb-unicode.h b/gfx/harfbuzz/src/hb-unicode.h new file mode 100644 index 000000000..2657f4813 --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode.h @@ -0,0 +1,471 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include <hb.h> instead." +#endif + +#ifndef HB_UNICODE_H +#define HB_UNICODE_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/* hb_unicode_general_category_t */ + +/* Unicode Character Database property: General_Category (gc) */ +typedef enum +{ + HB_UNICODE_GENERAL_CATEGORY_CONTROL, /* Cc */ + HB_UNICODE_GENERAL_CATEGORY_FORMAT, /* Cf */ + HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, /* Cn */ + HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE, /* Co */ + HB_UNICODE_GENERAL_CATEGORY_SURROGATE, /* Cs */ + HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, /* Ll */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER, /* Lm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER, /* Lo */ + HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, /* Lt */ + HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, /* Lu */ + HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK, /* Mc */ + HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK, /* Me */ + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK, /* Mn */ + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER, /* Nd */ + HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER, /* Nl */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER, /* No */ + HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, /* Pc */ + HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION, /* Pd */ + HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION, /* Pe */ + HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, /* Pf */ + HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, /* Pi */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION, /* Po */ + HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION, /* Ps */ + HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, /* Sc */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, /* Sk */ + HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL, /* Sm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, /* So */ + HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR, /* Zl */ + HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, /* Zp */ + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR /* Zs */ +} hb_unicode_general_category_t; + +/* hb_unicode_combining_class_t */ + +/* Note: newer versions of Unicode may add new values. Clients should be ready to handle + * any value in the 0..254 range being returned from hb_unicode_combining_class(). + */ + +/* Unicode Character Database property: Canonical_Combining_Class (ccc) */ +typedef enum +{ + HB_UNICODE_COMBINING_CLASS_NOT_REORDERED = 0, + HB_UNICODE_COMBINING_CLASS_OVERLAY = 1, + HB_UNICODE_COMBINING_CLASS_NUKTA = 7, + HB_UNICODE_COMBINING_CLASS_KANA_VOICING = 8, + HB_UNICODE_COMBINING_CLASS_VIRAMA = 9, + + /* Hebrew */ + HB_UNICODE_COMBINING_CLASS_CCC10 = 10, + HB_UNICODE_COMBINING_CLASS_CCC11 = 11, + HB_UNICODE_COMBINING_CLASS_CCC12 = 12, + HB_UNICODE_COMBINING_CLASS_CCC13 = 13, + HB_UNICODE_COMBINING_CLASS_CCC14 = 14, + HB_UNICODE_COMBINING_CLASS_CCC15 = 15, + HB_UNICODE_COMBINING_CLASS_CCC16 = 16, + HB_UNICODE_COMBINING_CLASS_CCC17 = 17, + HB_UNICODE_COMBINING_CLASS_CCC18 = 18, + HB_UNICODE_COMBINING_CLASS_CCC19 = 19, + HB_UNICODE_COMBINING_CLASS_CCC20 = 20, + HB_UNICODE_COMBINING_CLASS_CCC21 = 21, + HB_UNICODE_COMBINING_CLASS_CCC22 = 22, + HB_UNICODE_COMBINING_CLASS_CCC23 = 23, + HB_UNICODE_COMBINING_CLASS_CCC24 = 24, + HB_UNICODE_COMBINING_CLASS_CCC25 = 25, + HB_UNICODE_COMBINING_CLASS_CCC26 = 26, + + /* Arabic */ + HB_UNICODE_COMBINING_CLASS_CCC27 = 27, + HB_UNICODE_COMBINING_CLASS_CCC28 = 28, + HB_UNICODE_COMBINING_CLASS_CCC29 = 29, + HB_UNICODE_COMBINING_CLASS_CCC30 = 30, + HB_UNICODE_COMBINING_CLASS_CCC31 = 31, + HB_UNICODE_COMBINING_CLASS_CCC32 = 32, + HB_UNICODE_COMBINING_CLASS_CCC33 = 33, + HB_UNICODE_COMBINING_CLASS_CCC34 = 34, + HB_UNICODE_COMBINING_CLASS_CCC35 = 35, + + /* Syriac */ + HB_UNICODE_COMBINING_CLASS_CCC36 = 36, + + /* Telugu */ + HB_UNICODE_COMBINING_CLASS_CCC84 = 84, + HB_UNICODE_COMBINING_CLASS_CCC91 = 91, + + /* Thai */ + HB_UNICODE_COMBINING_CLASS_CCC103 = 103, + HB_UNICODE_COMBINING_CLASS_CCC107 = 107, + + /* Lao */ + HB_UNICODE_COMBINING_CLASS_CCC118 = 118, + HB_UNICODE_COMBINING_CLASS_CCC122 = 122, + + /* Tibetan */ + HB_UNICODE_COMBINING_CLASS_CCC129 = 129, + HB_UNICODE_COMBINING_CLASS_CCC130 = 130, + HB_UNICODE_COMBINING_CLASS_CCC133 = 132, + + + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT = 200, + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW = 202, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE = 214, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT = 216, + HB_UNICODE_COMBINING_CLASS_BELOW_LEFT = 218, + HB_UNICODE_COMBINING_CLASS_BELOW = 220, + HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT = 222, + HB_UNICODE_COMBINING_CLASS_LEFT = 224, + HB_UNICODE_COMBINING_CLASS_RIGHT = 226, + HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT = 228, + HB_UNICODE_COMBINING_CLASS_ABOVE = 230, + HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT = 232, + HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW = 233, + HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE = 234, + + HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT = 240, + + HB_UNICODE_COMBINING_CLASS_INVALID = 255 +} hb_unicode_combining_class_t; + + +/* + * hb_unicode_funcs_t + */ + +typedef struct hb_unicode_funcs_t hb_unicode_funcs_t; + + +/* + * just give me the best implementation you've got there. + */ +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_default (void); + + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_create (hb_unicode_funcs_t *parent); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_empty (void); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs); + + +/* + * funcs + */ + +/* typedefs */ + +typedef hb_unicode_combining_class_t (*hb_unicode_combining_class_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); +typedef unsigned int (*hb_unicode_eastasian_width_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); +typedef hb_unicode_general_category_t (*hb_unicode_general_category_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); +typedef hb_codepoint_t (*hb_unicode_mirroring_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); +typedef hb_script_t (*hb_unicode_script_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +typedef hb_bool_t (*hb_unicode_compose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data); +typedef hb_bool_t (*hb_unicode_decompose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data); + +/** + * hb_unicode_decompose_compatibility_func_t: + * @ufuncs: a Unicode function structure + * @u: codepoint to decompose + * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into + * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func() + * + * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed. + * The complete length of the decomposition will be returned. + * + * If @u has no compatibility decomposition, zero should be returned. + * + * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any + * compatibility decomposition plus an terminating value of 0. Consequently, @decompose must be allocated by the caller to be at least this length. Implementations + * of this function type must ensure that they do not write past the provided array. + * + * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available. + */ +typedef unsigned int (*hb_unicode_decompose_compatibility_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data); + +/* See Unicode 6.1 for details on the maximum decomposition length. */ +#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */ + +/* setters */ + +/** + * hb_unicode_funcs_set_combining_class_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_combining_class_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_eastasian_width_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_eastasian_width_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_general_category_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_general_category_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_mirroring_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_mirroring_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_script_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_script_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_compose_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_compose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_decompose_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_decompose_compatibility_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_compatibility_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* accessors */ + +/** + * hb_unicode_combining_class: + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_combining_class_t +hb_unicode_combining_class (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_eastasian_width: + * + * Since: 0.9.2 + **/ +HB_EXTERN unsigned int +hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_general_category: + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_general_category_t +hb_unicode_general_category (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_mirroring: + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_codepoint_t +hb_unicode_mirroring (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_script: + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_script_t +hb_unicode_script (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +HB_EXTERN hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + +HB_EXTERN hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + +HB_EXTERN unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed); + +HB_END_DECLS + +#endif /* HB_UNICODE_H */ diff --git a/gfx/harfbuzz/src/hb-uniscribe.cc b/gfx/harfbuzz/src/hb-uniscribe.cc new file mode 100644 index 000000000..6e4db0149 --- /dev/null +++ b/gfx/harfbuzz/src/hb-uniscribe.cc @@ -0,0 +1,1036 @@ +/* + * Copyright © 2011,2012,2013 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 HB_SHAPER uniscribe +#include "hb-shaper-impl-private.hh" + +#include <windows.h> +#include <usp10.h> +#include <rpc.h> + +#include "hb-uniscribe.h" + +#include "hb-open-file-private.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-tag.h" + + +#ifndef HB_DEBUG_UNISCRIBE +#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) +#endif + + +static inline uint16_t hb_uint16_swap (const uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline uint32_t hb_uint32_swap (const uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } + + +typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +); + +typedef HRESULT (WINAPI *SSOT) /*ScriptShapeOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +); + +typedef HRESULT (WINAPI *SPOT) /*ScriptPlaceOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +); + + +/* Fallback implementations. */ + +static HRESULT WINAPI +hb_ScriptItemizeOpenType( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +) +{ +{ + return ScriptItemize (pwcInChars, + cInChars, + cMaxItems, + psControl, + psState, + pItems, + pcItems); +} +} + +static HRESULT WINAPI +hb_ScriptShapeOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pOutGlyphProps; + return ScriptShape (hdc, + psc, + pwcChars, + cChars, + cMaxGlyphs, + psa, + pwOutGlyphs, + pwLogClust, + psva, + pcGlyphs); +} + +static HRESULT WINAPI +hb_ScriptPlaceOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pGlyphProps; + return ScriptPlace (hdc, + psc, + pwGlyphs, + cGlyphs, + psva, + psa, + piAdvance, + pGoffset, + pABC); +} + + +struct hb_uniscribe_shaper_funcs_t { + SIOT ScriptItemizeOpenType; + SSOT ScriptShapeOpenType; + SPOT ScriptPlaceOpenType; + + inline void init (void) + { + HMODULE hinstLib; + this->ScriptItemizeOpenType = NULL; + this->ScriptShapeOpenType = NULL; + this->ScriptPlaceOpenType = NULL; + + hinstLib = GetModuleHandle (TEXT ("usp10.dll")); + if (hinstLib) + { + this->ScriptItemizeOpenType = (SIOT) GetProcAddress (hinstLib, "ScriptItemizeOpenType"); + this->ScriptShapeOpenType = (SSOT) GetProcAddress (hinstLib, "ScriptShapeOpenType"); + this->ScriptPlaceOpenType = (SPOT) GetProcAddress (hinstLib, "ScriptPlaceOpenType"); + } + if (!this->ScriptItemizeOpenType || + !this->ScriptShapeOpenType || + !this->ScriptPlaceOpenType) + { + DEBUG_MSG (UNISCRIBE, NULL, "OpenType versions of functions not found; falling back."); + this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType; + this->ScriptShapeOpenType = hb_ScriptShapeOpenType; + this->ScriptPlaceOpenType = hb_ScriptPlaceOpenType; + } + } +}; +static hb_uniscribe_shaper_funcs_t *uniscribe_funcs; + +static inline void +free_uniscribe_funcs (void) +{ + free (uniscribe_funcs); +} + +static hb_uniscribe_shaper_funcs_t * +hb_uniscribe_shaper_get_funcs (void) +{ +retry: + hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) hb_atomic_ptr_get (&uniscribe_funcs); + + if (unlikely (!funcs)) + { + funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t)); + if (unlikely (!funcs)) + return NULL; + + funcs->init (); + + if (!hb_atomic_ptr_cmpexch (&uniscribe_funcs, NULL, funcs)) { + free (funcs); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_uniscribe_funcs); /* First person registers atexit() callback. */ +#endif + } + + return funcs; +} + + +struct active_feature_t { + OPENTYPE_FEATURE_RECORD rec; + unsigned int order; + + static int cmp (const active_feature_t *a, const active_feature_t *b) { + return a->rec.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 : + 0; + } + bool operator== (const active_feature_t *f) { + return cmp (this, f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + static int cmp (const feature_event_t *a, const feature_event_t *b) { + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + TEXTRANGE_PROPERTIES props; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + +HB_SHAPER_DATA_ENSURE_DECLARE(uniscribe, face) +HB_SHAPER_DATA_ENSURE_DECLARE(uniscribe, font) + + +/* + * shaper face data + */ + +struct hb_uniscribe_shaper_face_data_t { + HANDLE fh; + hb_uniscribe_shaper_funcs_t *funcs; + wchar_t face_name[LF_FACESIZE]; +}; + +/* face_name should point to a wchar_t[LF_FACESIZE] object. */ +static void +_hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen) +{ + /* We'll create a private name for the font from a UUID using a simple, + * somewhat base64-like encoding scheme */ + const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + UUID id; + UuidCreate ((UUID*) &id); + ASSERT_STATIC (2 + 3 * (16/2) < LF_FACESIZE); + unsigned int name_str_len = 0; + face_name[name_str_len++] = 'F'; + face_name[name_str_len++] = '_'; + unsigned char *p = (unsigned char *) &id; + for (unsigned int i = 0; i < 16; i += 2) + { + /* Spread the 16 bits from two bytes of the UUID across three chars of face_name, + * using the bits in groups of 5,5,6 to select chars from enc. + * This will generate 24 characters; with the 'F_' prefix we already provided, + * the name will be 26 chars (plus the NUL terminator), so will always fit within + * face_name (LF_FACESIZE = 32). */ + face_name[name_str_len++] = enc[p[i] >> 3]; + face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f]; + face_name[name_str_len++] = enc[p[i + 1] & 0x3f]; + } + face_name[name_str_len] = 0; + if (plen) + *plen = name_str_len; +} + +/* Destroys blob. */ +static hb_blob_t * +_hb_rename_font (hb_blob_t *blob, wchar_t *new_name) +{ + /* Create a copy of the font data, with the 'name' table replaced by a + * table that names the font with our private F_* name created above. + * For simplicity, we just append a new 'name' table and update the + * sfnt directory; the original table is left in place, but unused. + * + * The new table will contain just 5 name IDs: family, style, unique, + * full, PS. All of them point to the same name data with our unique name. + */ + + blob = OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (blob); + + unsigned int length, new_length, name_str_len; + const char *orig_sfnt_data = hb_blob_get_data (blob, &length); + + _hb_generate_unique_face_name (new_name, &name_str_len); + + static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 }; + + unsigned int name_table_length = OT::name::min_size + + ARRAY_LENGTH (name_IDs) * OT::NameRecord::static_size + + name_str_len * 2; /* for name data in UTF16BE form */ + unsigned int name_table_offset = (length + 3) & ~3; + + new_length = name_table_offset + ((name_table_length + 3) & ~3); + void *new_sfnt_data = calloc (1, new_length); + if (!new_sfnt_data) + { + hb_blob_destroy (blob); + return NULL; + } + + memcpy(new_sfnt_data, orig_sfnt_data, length); + + OT::name &name = OT::StructAtOffset<OT::name> (new_sfnt_data, name_table_offset); + name.format.set (0); + name.count.set (ARRAY_LENGTH (name_IDs)); + name.stringOffset.set (name.get_size ()); + for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++) + { + OT::NameRecord &record = name.nameRecord[i]; + record.platformID.set (3); + record.encodingID.set (1); + record.languageID.set (0x0409u); /* English */ + record.nameID.set (name_IDs[i]); + record.length.set (name_str_len * 2); + record.offset.set (0); + } + + /* Copy string data from new_name, converting wchar_t to UTF16BE. */ + unsigned char *p = &OT::StructAfter<unsigned char> (name); + for (unsigned int i = 0; i < name_str_len; i++) + { + *p++ = new_name[i] >> 8; + *p++ = new_name[i] & 0xff; + } + + /* Adjust name table entry to point to new name table */ + const OT::OpenTypeFontFile &file = * (OT::OpenTypeFontFile *) (new_sfnt_data); + unsigned int face_count = file.get_face_count (); + for (unsigned int face_index = 0; face_index < face_count; face_index++) + { + /* Note: doing multiple edits (ie. TTC) can be unsafe. There may be + * toe-stepping. But we don't really care. */ + const OT::OpenTypeFontFace &face = file.get_face (face_index); + unsigned int index; + if (face.find_table_index (HB_OT_TAG_name, &index)) + { + OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index)); + record.checkSum.set_for_data (&name, name_table_length); + record.offset.set (name_table_offset); + record.length.set (name_table_length); + } + else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ + { + free (new_sfnt_data); + hb_blob_destroy (blob); + return NULL; + } + } + + /* The checkSumAdjustment field in the 'head' table is now wrong, + * but that doesn't actually seem to cause any problems so we don't + * bother. */ + + hb_blob_destroy (blob); + return hb_blob_create ((const char *) new_sfnt_data, new_length, + HB_MEMORY_MODE_WRITABLE, NULL, free); +} + +hb_uniscribe_shaper_face_data_t * +_hb_uniscribe_shaper_face_data_create (hb_face_t *face) +{ + hb_uniscribe_shaper_face_data_t *data = (hb_uniscribe_shaper_face_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_face_data_t)); + if (unlikely (!data)) + return NULL; + + data->funcs = hb_uniscribe_shaper_get_funcs (); + if (unlikely (!data->funcs)) + { + free (data); + return NULL; + } + + hb_blob_t *blob = hb_face_reference_blob (face); + if (unlikely (!hb_blob_get_length (blob))) + DEBUG_MSG (UNISCRIBE, face, "Face has empty blob"); + + blob = _hb_rename_font (blob, data->face_name); + if (unlikely (!blob)) + { + free (data); + return NULL; + } + + DWORD num_fonts_installed; + data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, NULL), + hb_blob_get_length (blob), + 0, &num_fonts_installed); + if (unlikely (!data->fh)) + { + DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed"); + free (data); + return NULL; + } + + return data; +} + +void +_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_shaper_face_data_t *data) +{ + RemoveFontMemResourceEx (data->fh); + free (data); +} + + +/* + * shaper font data + */ + +struct hb_uniscribe_shaper_font_data_t { + HDC hdc; + LOGFONTW log_font; + HFONT hfont; + SCRIPT_CACHE script_cache; + double x_mult, y_mult; /* From LOGFONT space to HB space. */ +}; + +static bool +populate_log_font (LOGFONTW *lf, + hb_font_t *font, + unsigned int font_size) +{ + memset (lf, 0, sizeof (*lf)); + lf->lfHeight = -font_size; + lf->lfCharSet = DEFAULT_CHARSET; + + hb_face_t *face = font->face; + hb_uniscribe_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + + memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName)); + + return true; +} + +hb_uniscribe_shaper_font_data_t * +_hb_uniscribe_shaper_font_data_create (hb_font_t *font) +{ + if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return NULL; + + hb_uniscribe_shaper_font_data_t *data = (hb_uniscribe_shaper_font_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_font_data_t)); + if (unlikely (!data)) + return NULL; + + int font_size = font->face->get_upem (); /* Default... */ + /* No idea if the following is even a good idea. */ + if (font->y_ppem) + font_size = font->y_ppem; + + if (font_size < 0) + font_size = -font_size; + data->x_mult = (double) font->x_scale / font_size; + data->y_mult = (double) font->y_scale / font_size; + + data->hdc = GetDC (NULL); + + if (unlikely (!populate_log_font (&data->log_font, font, font_size))) { + DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return NULL; + } + + data->hfont = CreateFontIndirectW (&data->log_font); + if (unlikely (!data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return NULL; + } + + if (!SelectObject (data->hdc, data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return NULL; + } + + return data; +} + +void +_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_shaper_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); +} + +LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font) +{ + if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL; + hb_uniscribe_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + return &font_data->log_font; +} + +HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font) +{ + if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL; + hb_uniscribe_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + return font_data->hfont; +} + + +/* + * shaper shape_plan data + */ + +struct hb_uniscribe_shaper_shape_plan_data_t {}; + +hb_uniscribe_shaper_shape_plan_data_t * +_hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) +{ + return (hb_uniscribe_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + + +hb_bool_t +_hb_uniscribe_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + hb_uniscribe_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + hb_uniscribe_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs; + + /* + * Set up features. + */ + hb_auto_array_t<OPENTYPE_FEATURE_RECORD> feature_records; + hb_auto_array_t<range_record_t> range_records; + if (num_features) + { + /* Sort features by start/end events. */ + hb_auto_array_t<feature_event_t> feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + active_feature_t feature; + feature.rec.tagFeature = hb_uint32_swap (features[i].tag); + feature.rec.lParameter = features[i].value; + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.rec.tagFeature = 0; + feature.rec.lParameter = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_auto_array_t<active_feature_t> active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.len; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + if (unlikely (!range)) + goto fail_features; + + unsigned int offset = feature_records.len; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.len; j++) + { + if (!j || active_features[j].rec.tagFeature != feature_records[feature_records.len - 1].tagFeature) + { + OPENTYPE_FEATURE_RECORD *feature = feature_records.push (); + if (unlikely (!feature)) + goto fail_features; + *feature = active_features[j].rec; + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.len - 1].lParameter = active_features[j].rec.lParameter; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->props.potfRecords = reinterpret_cast<OPENTYPE_FEATURE_RECORD *> (offset); + range->props.cotfRecords = feature_records.len - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) { + active_feature_t *feature = active_features.push (); + if (unlikely (!feature)) + goto fail_features; + *feature = event->feature; + } else { + active_feature_t *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.array); + } + } + + if (!range_records.len) /* No active feature found. */ + goto fail_features; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.len; i++) + { + range_record_t *range = &range_records[i]; + range->props.potfRecords = feature_records.array + reinterpret_cast<uintptr_t> (range->props.potfRecords); + } + } + else + { + fail_features: + num_features = 0; + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \ + return false; \ + } HB_STMT_END; + + HRESULT hr; + +retry: + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index() = chars_len; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); + + if (num_features) + { + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + } + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof(WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (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); + + /* Note: + * We can't touch the contents of glyph_props. Our fallback + * implementations of Shape and Place functions use that buffer + * by casting it to a different type. It works because they + * both agree about it, but if we want to access it here we + * need address that issue first. + */ + +#undef ALLOCATE_ARRAY + +#define MAX_ITEMS 256 + + SCRIPT_ITEM items[MAX_ITEMS + 1]; + SCRIPT_CONTROL bidi_control = {0}; + SCRIPT_STATE bidi_state = {0}; + ULONG script_tags[MAX_ITEMS]; + int item_count; + + /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ + //bidi_control.fMergeNeutralItems = true; + *(uint32_t*)&bidi_control |= 1u<<24; + + bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + bidi_state.fOverrideDirection = 1; + + hr = funcs->ScriptItemizeOpenType (pchars, + 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 + + OPENTYPE_TAG language_tag = hb_uint32_swap (hb_ot_tag_from_language (buffer->props.language)); + hb_auto_array_t<TEXTRANGE_PROPERTIES*> range_properties; + hb_auto_array_t<int> range_char_counts; + + unsigned int glyphs_offset = 0; + unsigned int glyphs_len; + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + 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; + + if (num_features) + { + range_properties.shrink (0); + range_char_counts.shrink (0); + + range_record_t *last_range = &range_records[0]; + + for (unsigned int k = chars_offset; k < chars_offset + item_chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (!range_properties.len || + &range->props != range_properties[range_properties.len - 1]) + { + TEXTRANGE_PROPERTIES **props = range_properties.push (); + int *c = range_char_counts.push (); + if (unlikely (!props || !c)) + { + range_properties.shrink (0); + range_char_counts.shrink (0); + break; + } + *props = &range->props; + *c = 1; + } + else + { + range_char_counts[range_char_counts.len - 1]++; + } + + last_range = range; + } + } + + /* Asking for glyphs in logical order circumvents at least + * one bug in Uniscribe. */ + items[i].a.fLogicalOrder = true; + + retry_shape: + hr = funcs->ScriptShapeOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + range_char_counts.array, + range_properties.array, + range_properties.len, + pchars + 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); + + if (unlikely (items[i].a.fNoGlyphIndex)) + FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); + if (unlikely (hr == E_OUTOFMEMORY || hr == E_NOT_SUFFICIENT_BUFFER)) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + goto retry; + } + if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) + { + if (items[i].a.eScript == SCRIPT_UNDEFINED) + FAIL ("ScriptShapeOpenType() failed: Font doesn't support script"); + items[i].a.eScript = SCRIPT_UNDEFINED; + goto retry_shape; + } + if (unlikely (FAILED (hr))) + { + FAIL ("ScriptShapeOpenType() failed: 0x%08xL", hr); + } + + for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) + log_clusters[j] += glyphs_offset; + + hr = funcs->ScriptPlaceOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + range_char_counts.array, + range_properties.array, + range_properties.len, + pchars + 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); + + if (DEBUG_ENABLED (UNISCRIBE)) + fprintf (stderr, "Item %d RTL %d LayoutRTL %d LogicalOrder %d ScriptTag %c%c%c%c\n", + i, + items[i].a.fRTL, + items[i].a.fLayoutRTL, + items[i].a.fLogicalOrder, + HB_UNTAG (hb_uint32_swap (script_tags[i]))); + + 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); + } + for (unsigned int i = 1; i < glyphs_len; i++) + if (vis_clusters[i] == -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphs_len))) + 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.i32 = offsets[i].du; + info->var2.i32 = offsets[i].dv; + } + + /* Set glyph positions */ + buffer->clear_positions (); + double x_mult = font_data->x_mult, y_mult = font_data->y_mult; + 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 = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (backward) + hb_buffer_reverse (buffer); + + /* Wow, done! */ + return true; +} + + diff --git a/gfx/harfbuzz/src/hb-uniscribe.h b/gfx/harfbuzz/src/hb-uniscribe.h new file mode 100644 index 000000000..4e4ef9986 --- /dev/null +++ b/gfx/harfbuzz/src/hb-uniscribe.h @@ -0,0 +1,46 @@ +/* + * 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_UNISCRIBE_H +#define HB_UNISCRIBE_H + +#include "hb.h" + +#include <windows.h> + +HB_BEGIN_DECLS + + +HB_EXTERN LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font); + +HB_EXTERN HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_UNISCRIBE_H */ diff --git a/gfx/harfbuzz/src/hb-utf-private.hh b/gfx/harfbuzz/src/hb-utf-private.hh new file mode 100644 index 000000000..74cf5d66a --- /dev/null +++ b/gfx/harfbuzz/src/hb-utf-private.hh @@ -0,0 +1,282 @@ +/* + * Copyright © 2011,2012,2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_UTF_PRIVATE_HH +#define HB_UTF_PRIVATE_HH + +#include "hb-private.hh" + + +struct hb_utf8_t +{ + typedef uint8_t codepoint_t; + + static inline const uint8_t * + next (const uint8_t *text, + const uint8_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + /* Written to only accept well-formed sequences. + * Based on ideas from ICU's U8_NEXT. + * Generates one "replacement" for each ill-formed byte. */ + + hb_codepoint_t c = *text++; + + if (c > 0x7Fu) + { + if (hb_in_range (c, 0xC2u, 0xDFu)) /* Two-byte */ + { + unsigned int t1; + if (likely (text < end && + (t1 = text[0] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x1Fu)<<6) | t1; + text++; + } + else + goto error; + } + else if (hb_in_range (c, 0xE0u, 0xEFu)) /* Three-byte */ + { + unsigned int t1, t2; + if (likely (1 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu)) + { + c = ((c&0xFu)<<12) | (t1<<6) | t2; + if (unlikely (c < 0x0800u || hb_in_range (c, 0xD800u, 0xDFFFu))) + goto error; + text += 2; + } + else + goto error; + } + else if (hb_in_range (c, 0xF0u, 0xF4u)) /* Four-byte */ + { + unsigned int t1, t2, t3; + if (likely (2 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu && + (t3 = text[2] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x7u)<<18) | (t1<<12) | (t2<<6) | t3; + if (unlikely (!hb_in_range (c, 0x10000u, 0x10FFFFu))) + goto error; + text += 3; + } + else + goto error; + } + else + goto error; + } + + *unicode = c; + return text; + + error: + *unicode = replacement; + return text; + } + + static inline const uint8_t * + prev (const uint8_t *text, + const uint8_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + const uint8_t *end = text--; + while (start < text && (*text & 0xc0) == 0x80 && end - text < 4) + text--; + + if (likely (next (text, end, unicode, replacement) == end)) + return text; + + *unicode = replacement; + return end - 1; + } + + static inline unsigned int + strlen (const uint8_t *text) + { + return ::strlen ((const char *) text); + } +}; + + +struct hb_utf16_t +{ + typedef uint16_t codepoint_t; + + static inline const uint16_t * + next (const uint16_t *text, + const uint16_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *text++; + + if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c <= 0xDBFFu && text < end)) + { + /* High-surrogate in c */ + hb_codepoint_t l = *text; + if (likely (hb_in_range (l, 0xDC00u, 0xDFFFu))) + { + /* Low-surrogate in l */ + *unicode = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text++; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + static inline const uint16_t * + prev (const uint16_t *text, + const uint16_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *--text; + + if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c >= 0xDC00u && start < text)) + { + /* Low-surrogate in c */ + hb_codepoint_t h = text[-1]; + if (likely (hb_in_range (h, 0xD800u, 0xDBFFu))) + { + /* High-surrogate in h */ + *unicode = (h << 10) + c - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text--; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + + static inline unsigned int + strlen (const uint16_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } +}; + + +template <bool validate=true> +struct hb_utf32_t +{ + typedef uint32_t codepoint_t; + + static inline const uint32_t * + next (const uint32_t *text, + const uint32_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *text++; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static inline const uint32_t * + prev (const uint32_t *text, + const uint32_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *--text; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static inline unsigned int + strlen (const uint32_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } +}; + + +struct hb_latin1_t +{ + typedef uint8_t codepoint_t; + + static inline const uint8_t * + next (const uint8_t *text, + const uint8_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *text++; + return text; + } + + static inline const uint8_t * + prev (const uint8_t *text, + const uint8_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + *unicode = *--text; + return text; + } + + static inline unsigned int + strlen (const uint8_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } +}; + +#endif /* HB_UTF_PRIVATE_HH */ diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h new file mode 100644 index 000000000..0cbe94706 --- /dev/null +++ b/gfx/harfbuzz/src/hb-version.h @@ -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 1 +#define HB_VERSION_MINOR 4 +#define HB_VERSION_MICRO 1 + +#define HB_VERSION_STRING "1.4.1" + +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/gfx/harfbuzz/src/hb-version.h.in b/gfx/harfbuzz/src/hb-version.h.in new file mode 100644 index 000000000..0ffd889b2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-version.h.in @@ -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 @HB_VERSION_MAJOR@ +#define HB_VERSION_MINOR @HB_VERSION_MINOR@ +#define HB_VERSION_MICRO @HB_VERSION_MICRO@ + +#define HB_VERSION_STRING "@HB_VERSION@" + +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/gfx/harfbuzz/src/hb-warning.cc b/gfx/harfbuzz/src/hb-warning.cc new file mode 100644 index 000000000..8f322bcb1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-warning.cc @@ -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 "hb-atomic-private.hh" +#include "hb-mutex-private.hh" + + +#if defined(HB_ATOMIC_INT_NIL) +#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe" +#error "Check hb-atomic-private.hh for possible resolutions." +#endif + +#if defined(HB_MUTEX_IMPL_NIL) +#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe" +#error "Check hb-mutex-private.hh for possible resolutions." +#endif diff --git a/gfx/harfbuzz/src/hb.h b/gfx/harfbuzz/src/hb.h new file mode 100644 index 000000000..7402034f4 --- /dev/null +++ b/gfx/harfbuzz/src/hb.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H +#define HB_H +#define HB_H_IN + +#ifndef HB_EXTERN +#define HB_EXTERN extern +#endif + +#include "hb-blob.h" +#include "hb-buffer.h" +#include "hb-common.h" +#include "hb-deprecated.h" +#include "hb-face.h" +#include "hb-font.h" +#include "hb-set.h" +#include "hb-shape.h" +#include "hb-shape-plan.h" +#include "hb-unicode.h" +#include "hb-version.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_H_IN +#endif /* HB_H */ diff --git a/gfx/harfbuzz/src/main.cc b/gfx/harfbuzz/src/main.cc new file mode 100644 index 000000000..f9708cc94 --- /dev/null +++ b/gfx/harfbuzz/src/main.cc @@ -0,0 +1,199 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#include "hb-mutex-private.hh" +#include "hb-open-file-private.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsubgpos-private.hh" + +#ifdef HAVE_GLIB +#include <glib.h> +#endif +#include <stdlib.h> +#include <stdio.h> + + +using namespace OT; + + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + const char *font_data = NULL; + int len = 0; + +#ifdef HAVE_GLIB + GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL); + font_data = g_mapped_file_get_contents (mf); + len = g_mapped_file_get_length (mf); +#else + FILE *f = fopen (argv[1], "rb"); + fseek (f, 0, SEEK_END); + len = ftell (f); + fseek (f, 0, SEEK_SET); + font_data = (const char *) malloc (len); + len = fread ((char *) font_data, 1, len, f); +#endif + + printf ("Opened font file %s: %d bytes long\n", argv[1], len); + + const OpenTypeFontFile &ot = *CastP<OpenTypeFontFile> (font_data); + + switch (ot.get_tag ()) { + case OpenTypeFontFile::TrueTypeTag: + printf ("OpenType font with TrueType outlines\n"); + break; + case OpenTypeFontFile::CFFTag: + printf ("OpenType font with CFF (Type1) outlines\n"); + break; + case OpenTypeFontFile::TTCTag: + printf ("TrueType Collection of OpenType fonts\n"); + break; + case OpenTypeFontFile::TrueTag: + printf ("Obsolete Apple TrueType font\n"); + break; + case OpenTypeFontFile::Typ1Tag: + printf ("Obsolete Apple Type1 font in SFNT container\n"); + break; + default: + printf ("Unknown font format\n"); + break; + } + + int num_fonts = ot.get_face_count (); + printf ("%d font(s) found in file\n", num_fonts); + for (int n_font = 0; n_font < num_fonts; n_font++) { + const OpenTypeFontFace &font = ot.get_face (n_font); + printf ("Font %d of %d:\n", n_font, num_fonts); + + int num_tables = font.get_table_count (); + printf (" %d table(s) found in font\n", num_tables); + for (int n_table = 0; n_table < num_tables; n_table++) { + const OpenTypeTable &table = font.get_table (n_table); + printf (" Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables, + (const char *)table.tag, + (unsigned int) table.offset, + (unsigned int) table.length); + + switch (table.tag) { + + case GSUBGPOS::GSUBTag: + case GSUBGPOS::GPOSTag: + { + + const GSUBGPOS &g = *CastP<GSUBGPOS> (font_data + table.offset); + + int num_scripts = g.get_script_count (); + printf (" %d script(s) found in table\n", num_scripts); + for (int n_script = 0; n_script < num_scripts; n_script++) { + const Script &script = g.get_script (n_script); + printf (" Script %2d of %2d: %.4s\n", n_script, num_scripts, + (const char *)g.get_script_tag(n_script)); + + if (!script.has_default_lang_sys()) + printf (" No default language system\n"); + int num_langsys = script.get_lang_sys_count (); + printf (" %d language system(s) found in script\n", num_langsys); + for (int n_langsys = script.has_default_lang_sys() ? -1 : 0; n_langsys < num_langsys; n_langsys++) { + const LangSys &langsys = n_langsys == -1 + ? script.get_default_lang_sys () + : script.get_lang_sys (n_langsys); + 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.has_required_feature ()) + printf (" No required feature\n"); + else + printf (" Required feature index: %d\n", + langsys.get_required_feature_index ()); + + int num_features = langsys.get_feature_count (); + printf (" %d feature(s) found in language system\n", num_features); + for (int n_feature = 0; n_feature < num_features; n_feature++) { + printf (" Feature index %2d of %2d: %d\n", n_feature, num_features, + langsys.get_feature_index (n_feature)); + } + } + } + + int num_features = g.get_feature_count (); + printf (" %d feature(s) found in table\n", num_features); + for (int n_feature = 0; n_feature < num_features; n_feature++) { + const Feature &feature = g.get_feature (n_feature); + int num_lookups = feature.get_lookup_count (); + printf (" Feature %2d of %2d: %c%c%c%c\n", n_feature, num_features, + HB_UNTAG(g.get_feature_tag(n_feature))); + + printf (" %d lookup(s) found in feature\n", num_lookups); + for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) { + printf (" Lookup index %2d of %2d: %d\n", n_lookup, num_lookups, + feature.get_lookup_index (n_lookup)); + } + } + + int num_lookups = g.get_lookup_count (); + printf (" %d lookup(s) found in table\n", num_lookups); + for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) { + const Lookup &lookup = g.get_lookup (n_lookup); + printf (" Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups, + lookup.get_type(), lookup.get_props()); + } + + } + break; + + case GDEF::tableTag: + { + + const GDEF &gdef = *CastP<GDEF> (font_data + table.offset); + + printf (" Has %sglyph classes\n", + gdef.has_glyph_classes () ? "" : "no "); + printf (" Has %smark attachment types\n", + gdef.has_mark_attachment_types () ? "" : "no "); + printf (" Has %sattach points\n", + gdef.has_attach_points () ? "" : "no "); + printf (" Has %slig carets\n", + gdef.has_lig_carets () ? "" : "no "); + printf (" Has %smark sets\n", + gdef.has_mark_sets () ? "" : "no "); + break; + } + } + } + } + + return 0; +} + + diff --git a/gfx/harfbuzz/src/moz.build b/gfx/harfbuzz/src/moz.build new file mode 100644 index 000000000..7ed39aa10 --- /dev/null +++ b/gfx/harfbuzz/src/moz.build @@ -0,0 +1,85 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.harfbuzz += [ + 'hb-blob.h', + 'hb-buffer.h', + 'hb-common.h', + 'hb-deprecated.h', + 'hb-face.h', + 'hb-font.h', + 'hb-ot-font.h', + 'hb-ot-layout.h', + 'hb-ot-math.h', + 'hb-ot-shape.h', + 'hb-ot-tag.h', + 'hb-ot.h', + 'hb-set.h', + 'hb-shape-plan.h', + 'hb-shape.h', + 'hb-unicode.h', + 'hb-version.h', + 'hb.h', +] + +SOURCES += [ + 'hb-blob.cc', # error: use of undeclared identifier 'snprintf' (FreeBSD) + 'hb-common.cc', # error: use of undeclared identifier 'strdup' + 'hb-ot-math.cc', # conflict with hb-ot-layout.cc + 'hb-ot-shape-complex-hangul.cc', # error: redefinition of enumerator 'NONE' + 'hb-ot-shape-complex-indic.cc', # error: redefinition of enumerator 'INIT' + 'hb-ot-shape-complex-use.cc', # error: redefinition of 'basic_features' + 'hb-ot-shape.cc', # error: functions that differ only in their return type cannot be overloaded + 'hb-shape-plan.cc', # error: redefinition of 'hb_ot_shaper_face_data_ensure' +] + +UNIFIED_SOURCES += [ + 'hb-buffer.cc', + 'hb-face.cc', + 'hb-fallback-shape.cc', + 'hb-font.cc', + 'hb-ot-layout.cc', + 'hb-ot-map.cc', + 'hb-ot-shape-complex-arabic.cc', + 'hb-ot-shape-complex-default.cc', + 'hb-ot-shape-complex-hebrew.cc', + 'hb-ot-shape-complex-indic-table.cc', + 'hb-ot-shape-complex-myanmar.cc', + 'hb-ot-shape-complex-thai.cc', + 'hb-ot-shape-complex-tibetan.cc', + 'hb-ot-shape-complex-use-table.cc', + 'hb-ot-shape-fallback.cc', + 'hb-ot-shape-normalize.cc', + 'hb-ot-tag.cc', + 'hb-set.cc', + 'hb-shape.cc', + 'hb-shaper.cc', + 'hb-unicode.cc', + 'hb-warning.cc', +] + +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + EXPORTS.harfbuzz += [ + 'hb-glib.h', + ] + UNIFIED_SOURCES += [ + 'hb-glib.cc', + ] + CXXFLAGS += CONFIG['GLIB_CFLAGS'] + +# We allow warnings for third-party code that can be updated from upstream. +ALLOW_COMPILER_WARNINGS = True + +FINAL_LIBRARY = 'gkmedias' + +DEFINES['PACKAGE_VERSION'] = '"moz"' +DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' +DEFINES['HAVE_OT'] = 1 +DEFINES['HB_NO_MT'] = True +DEFINES['HB_NO_UNICODE_FUNCS'] = True +# Cancel the effect of the -DDEBUG macro if present, +# because harfbuzz uses that name for its own purposes +DEFINES['DEBUG'] = False diff --git a/gfx/harfbuzz/src/sample.py b/gfx/harfbuzz/src/sample.py new file mode 100755 index 000000000..c2cb94d53 --- /dev/null +++ b/gfx/harfbuzz/src/sample.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import print_function +import sys +import array +from gi.repository import HarfBuzz as hb +from gi.repository import GLib + +# Python 2/3 compatibility +try: + unicode +except NameError: + unicode = str + +def tounicode(s, encoding='utf-8'): + if not isinstance(s, unicode): + return s.decode(encoding) + else: + return s + +fontdata = open (sys.argv[1], 'rb').read () +text = tounicode(sys.argv[2]) +# Need to create GLib.Bytes explicitly until this bug is fixed: +# https://bugzilla.gnome.org/show_bug.cgi?id=729541 +blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) +face = hb.face_create (blob, 0) +del blob +font = hb.font_create (face) +upem = hb.face_get_upem (face) +del face +hb.font_set_scale (font, upem, upem) +#hb.ft_font_set_funcs (font) +hb.ot_font_set_funcs (font) + +buf = hb.buffer_create () +class Debugger(object): + def message (self, buf, font, msg, data, _x_what_is_this): + print(msg) + return True +debugger = Debugger() +hb.buffer_set_message_func (buf, debugger.message, 1, 0) + +## +## Add text to buffer +## +# +# See https://github.com/behdad/harfbuzz/pull/271 +# +if False: + # If you do not care about cluster values reflecting Python + # string indices, then this is quickest way to add text to + # buffer: + hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1) + # Otherwise, then following handles both narrow and wide + # Python builds: +elif sys.maxunicode == 0x10FFFF: + hb.buffer_add_utf32 (buf, array.array('I', text.encode('utf-32')), 0, -1) +else: + hb.buffer_add_utf16 (buf, array.array('H', text.encode('utf-16')), 0, -1) + + +hb.buffer_guess_segment_properties (buf) + +hb.shape (font, buf, []) +del font + +infos = hb.buffer_get_glyph_infos (buf) +positions = hb.buffer_get_glyph_positions (buf) + +for info,pos in zip(infos, positions): + gid = info.codepoint + cluster = info.cluster + x_advance = pos.x_advance + x_offset = pos.x_offset + y_offset = pos.y_offset + + print("gid%d=%d@%d,%d+%d" % (gid, cluster, x_advance, x_offset, y_offset)) diff --git a/gfx/harfbuzz/src/test-buffer-serialize.cc b/gfx/harfbuzz/src/test-buffer-serialize.cc new file mode 100644 index 000000000..18c46e952 --- /dev/null +++ b/gfx/harfbuzz/src/test-buffer-serialize.cc @@ -0,0 +1,129 @@ +/* + * Copyright © 2010,2011,2013 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 "hb.h" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +#ifdef HAVE_GLIB +# include <glib.h> +# if !GLIB_CHECK_VERSION (2, 22, 0) +# define g_mapped_file_unref g_mapped_file_free +# endif +#endif +#include <stdlib.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + hb_blob_t *blob = NULL; + + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\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); + } + + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = NULL; + + unsigned int upem = hb_face_get_upem (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + hb_font_set_scale (font, upem, upem); +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + hb_buffer_t *buf; + buf = hb_buffer_create (); + + bool ret = true; + char line[BUFSIZ], out[BUFSIZ]; + while (fgets (line, sizeof(line), stdin) != 0) + { + hb_buffer_clear_contents (buf); + + const char *p = line; + while (hb_buffer_deserialize_glyphs (buf, + p, -1, &p, + font, + HB_BUFFER_SERIALIZE_FORMAT_JSON)) + ; + if (*p && *p != '\n') + ret = false; + + hb_buffer_serialize_glyphs (buf, 0, hb_buffer_get_length (buf), + out, sizeof (out), NULL, + font, HB_BUFFER_SERIALIZE_FORMAT_JSON, + HB_BUFFER_SERIALIZE_FLAG_DEFAULT); + puts (out); + } + + hb_buffer_destroy (buf); + + hb_font_destroy (font); + + return !ret; +} diff --git a/gfx/harfbuzz/src/test-size-params.cc b/gfx/harfbuzz/src/test-size-params.cc new file mode 100644 index 000000000..35d9e3c8e --- /dev/null +++ b/gfx/harfbuzz/src/test-size-params.cc @@ -0,0 +1,96 @@ +/* + * Copyright © 2010,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 "hb.h" +#include "hb-ot.h" + +#ifdef HAVE_GLIB +# include <glib.h> +# if !GLIB_CHECK_VERSION (2, 22, 0) +# define g_mapped_file_unref g_mapped_file_free +# endif +#endif +#include <stdlib.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + hb_blob_t *blob = NULL; + + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\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); + } + + /* Create the face */ + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = NULL; + + unsigned int p[5]; + bool ret = hb_ot_layout_get_size_params (face, p, p+1, p+2, p+3, p+4); + + printf ("%g %u %u %g %g\n", p[0]/10., p[1], p[2], p[3]/10., p[4]/10.); + + return !ret; +} diff --git a/gfx/harfbuzz/src/test-would-substitute.cc b/gfx/harfbuzz/src/test-would-substitute.cc new file mode 100644 index 000000000..8ea87cdf3 --- /dev/null +++ b/gfx/harfbuzz/src/test-would-substitute.cc @@ -0,0 +1,106 @@ +/* + * Copyright © 2010,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 "hb.h" +#include "hb-ot.h" + +#ifdef HAVE_GLIB +# include <glib.h> +# if !GLIB_CHECK_VERSION (2, 22, 0) +# define g_mapped_file_unref g_mapped_file_free +# endif +#endif +#include <stdlib.h> +#include <stdio.h> + +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +int +main (int argc, char **argv) +{ + hb_blob_t *blob = NULL; + + if (argc != 4 && argc != 5) { + fprintf (stderr, "usage: %s font-file lookup-index first-glyph [second-glyph]\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); + } + + /* Create the face */ + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = NULL; + + hb_font_t *font = hb_font_create (face); +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + unsigned int len = argc - 3; + hb_codepoint_t glyphs[2]; + if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) || + (argc > 4 && + !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1]))) + return 2; + return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], NULL, 0), glyphs, len, false); +} diff --git a/gfx/harfbuzz/src/test.cc b/gfx/harfbuzz/src/test.cc new file mode 100644 index 000000000..0c90f8ff4 --- /dev/null +++ b/gfx/harfbuzz/src/test.cc @@ -0,0 +1,136 @@ +/* + * Copyright © 2010,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 "hb.h" + +#ifdef HAVE_GLIB +# include <glib.h> +# if !GLIB_CHECK_VERSION (2, 22, 0) +# define g_mapped_file_unref g_mapped_file_free +# endif +#endif +#include <stdlib.h> +#include <stdio.h> + +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +int +main (int argc, char **argv) +{ + hb_blob_t *blob = 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 */ + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = NULL; + unsigned int upem = hb_face_get_upem (face); + + hb_font_t *font = hb_font_create (face); + hb_font_set_scale (font, upem, upem); + +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + hb_buffer_t *buffer = hb_buffer_create (); + + hb_buffer_add_utf8 (buffer, "\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\x95", -1, 0, -1); + hb_buffer_guess_segment_properties (buffer); + + hb_shape (font, buffer, NULL, 0); + + unsigned int count = hb_buffer_get_length (buffer); + hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, NULL); + + for (unsigned int i = 0; i < count; i++) + { + hb_glyph_info_t *info = &infos[i]; + hb_glyph_position_t *pos = &positions[i]; + + printf ("cluster %d glyph 0x%x at (%d,%d)+(%d,%d)\n", + info->cluster, + info->codepoint, + pos->x_offset, + pos->y_offset, + pos->x_advance, + pos->y_advance); + + } + + hb_buffer_destroy (buffer); + hb_font_destroy (font); + hb_face_destroy (face); + + return 0; +} + + |