diff options
Diffstat (limited to 'src')
138 files changed, 31699 insertions, 8102 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 344cc57..c99967f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,15 +1,21 @@ # Process this file with automake to produce Makefile.in NULL = +SUBDIRS = +DIST_SUBDIRS = BUILT_SOURCES = EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = +DISTCHECK_CONFIGURE_FLAGS = --enable-introspection -# The following warning options are useful for debugging: -Wpadded -Wcast-align +# The following warning options are useful for debugging: -Wpadded #AM_CXXFLAGS = +# Convenience targets: +lib: libharfbuzz.la + lib_LTLIBRARIES = libharfbuzz.la HBCFLAGS = @@ -17,18 +23,22 @@ HBLIBS = HBSOURCES = \ hb-atomic-private.hh \ hb-blob.cc \ + hb-buffer-deserialize-json.hh \ + hb-buffer-deserialize-text.hh \ hb-buffer-private.hh \ + hb-buffer-serialize.cc \ hb-buffer.cc \ hb-cache-private.hh \ hb-common.cc \ - hb-fallback-shape-private.hh \ - hb-fallback-shape.cc \ + hb-face-private.hh \ + hb-face.cc \ hb-font-private.hh \ hb-font.cc \ hb-mutex-private.hh \ hb-object-private.hh \ hb-open-file-private.hh \ hb-open-type-private.hh \ + hb-ot-cmap-table.hh \ hb-ot-head-table.hh \ hb-ot-hhea-table.hh \ hb-ot-hmtx-table.hh \ @@ -39,9 +49,15 @@ HBSOURCES = \ hb-set-private.hh \ hb-set.cc \ hb-shape.cc \ - hb-tt-font.cc \ + hb-shape-plan-private.hh \ + hb-shape-plan.cc \ + hb-shaper-list.hh \ + hb-shaper-impl-private.hh \ + hb-shaper-private.hh \ + hb-shaper.cc \ hb-unicode-private.hh \ hb-unicode.cc \ + hb-utf-private.hh \ hb-warning.cc \ $(NULL) HBHEADERS = \ @@ -49,44 +65,74 @@ HBHEADERS = \ hb-blob.h \ hb-buffer.h \ hb-common.h \ + hb-deprecated.h \ + hb-face.h \ hb-font.h \ hb-set.h \ hb-shape.h \ + hb-shape-plan.h \ hb-unicode.h \ + $(NULL) +HBNODISTHEADERS = \ hb-version.h \ $(NULL) if HAVE_OT HBSOURCES += \ + hb-ot-font.cc \ hb-ot-layout.cc \ hb-ot-layout-common-private.hh \ hb-ot-layout-gdef-table.hh \ hb-ot-layout-gpos-table.hh \ hb-ot-layout-gsubgpos-private.hh \ hb-ot-layout-gsub-table.hh \ + hb-ot-layout-jstf-table.hh \ hb-ot-layout-private.hh \ hb-ot-map.cc \ hb-ot-map-private.hh \ hb-ot-shape.cc \ hb-ot-shape-complex-arabic.cc \ + hb-ot-shape-complex-arabic-fallback.hh \ hb-ot-shape-complex-arabic-table.hh \ + hb-ot-shape-complex-arabic-win1256.hh \ + hb-ot-shape-complex-default.cc \ + hb-ot-shape-complex-hangul.cc \ + hb-ot-shape-complex-hebrew.cc \ hb-ot-shape-complex-indic.cc \ hb-ot-shape-complex-indic-machine.hh \ hb-ot-shape-complex-indic-private.hh \ - hb-ot-shape-complex-indic-table.hh \ - hb-ot-shape-complex-misc.cc \ + hb-ot-shape-complex-indic-table.cc \ + hb-ot-shape-complex-myanmar.cc \ + hb-ot-shape-complex-myanmar-machine.hh \ + hb-ot-shape-complex-sea.cc \ + hb-ot-shape-complex-sea-machine.hh \ + hb-ot-shape-complex-thai.cc \ + hb-ot-shape-complex-tibetan.cc \ hb-ot-shape-complex-private.hh \ hb-ot-shape-normalize-private.hh \ hb-ot-shape-normalize.cc \ + hb-ot-shape-fallback-private.hh \ + hb-ot-shape-fallback.cc \ hb-ot-shape-private.hh \ $(NULL) HBHEADERS += \ hb-ot.h \ + hb-ot-font.h \ hb-ot-layout.h \ + hb-ot-shape.h \ hb-ot-tag.h \ $(NULL) endif +if HAVE_FALLBACK +HBSOURCES += hb-fallback-shape.cc +endif + +if HAVE_PTHREAD +HBCFLAGS += $(PTHREAD_CFLAGS) +HBLIBS += $(PTHREAD_LIBS) +endif + if HAVE_GLIB HBCFLAGS += $(GLIB_CFLAGS) HBLIBS += $(GLIB_LIBS) @@ -94,28 +140,6 @@ HBSOURCES += hb-glib.cc HBHEADERS += hb-glib.h endif -if HAVE_GOBJECT -HBCFLAGS += $(GOBJECT_CFLAGS) -HBLIBS += $(GOBJECT_LIBS) -HBSOURCES += hb-gobject-structs.cc -nodist_HBSOURCES = hb-gobject-enums.cc -HBHEADERS += hb-gobject.h -BUILT_SOURCES += hb-gobject-enums.cc -EXTRA_DIST += hb-gobject-enums.cc.tmpl -DISTCLEANFILES += hb-gobject-enums.cc - -hb-gobject-enums.cc: hb-gobject-enums.cc.tmpl $(HBHEADERS) - $(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > "$@.tmp" && \ - mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false ) -endif - -if HAVE_ICU -HBCFLAGS += $(ICU_CFLAGS) -HBLIBS += $(ICU_LIBS) -HBSOURCES += hb-icu.cc -HBHEADERS += hb-icu.h -endif - if HAVE_FREETYPE HBCFLAGS += $(FREETYPE_CFLAGS) HBLIBS += $(FREETYPE_LIBS) @@ -126,89 +150,258 @@ endif if HAVE_GRAPHITE2 HBCFLAGS += $(GRAPHITE2_CFLAGS) HBLIBS += $(GRAPHITE2_LIBS) -HBSOURCES += hb-graphite2.cc hb-graphite2-private.hh +HBSOURCES += hb-graphite2.cc HBHEADERS += hb-graphite2.h endif if HAVE_UNISCRIBE HBCFLAGS += $(UNISCRIBE_CFLAGS) HBLIBS += $(UNISCRIBE_LIBS) -HBSOURCES += hb-uniscribe.cc hb-uniscribe-private.hh +HBSOURCES += hb-uniscribe.cc HBHEADERS += hb-uniscribe.h endif -# Use a C linker, not C++; Don't link to libstdc++ +if HAVE_CORETEXT +HBCFLAGS += $(CORETEXT_CFLAGS) +HBLIBS += $(CORETEXT_LIBS) +HBSOURCES += hb-coretext.cc +HBHEADERS += hb-coretext.h +endif + +if HAVE_UCDN +SUBDIRS += hb-ucdn +HBCFLAGS += -I$(srcdir)/hb-ucdn +HBLIBS += hb-ucdn/libhb-ucdn.la +HBSOURCES += hb-ucdn.cc +endif +DIST_SUBDIRS += hb-ucdn + + +# Put the library together + +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) -libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) -nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES) +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) -no-undefined +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 = hb-version.h +nodist_pkginclude_HEADERS = $(HBNODISTHEADERS) +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = harfbuzz.pc +EXTRA_DIST += harfbuzz.pc.in + +if HAVE_ICU +lib_LTLIBRARIES += libharfbuzz-icu.la +libharfbuzz_icu_la_SOURCES = hb-icu.cc +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.h +pkgconfig_DATA += harfbuzz-icu.pc +endif +EXTRA_DIST += harfbuzz-icu.pc.in + +if HAVE_GOBJECT +lib_LTLIBRARIES += libharfbuzz-gobject.la +libharfbuzz_gobject_la_SOURCES = hb-gobject-structs.cc +nodist_libharfbuzz_gobject_la_SOURCES = hb-gobject-enums.cc +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.h hb-gobject-structs.h +nodist_pkginclude_HEADERS += hb-gobject-enums.h +pkgconfig_DATA += harfbuzz-gobject.pc + +BUILT_SOURCES += \ + hb-gobject-enums.cc \ + hb-gobject-enums.h \ + $(NULL) +DISTCLEANFILES += \ + hb-gobject-enums.cc \ + hb-gobject-enums.h \ + $(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@%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-$(HB_VERSION_MAJOR).dll; \ + ) >"$@" + @ ! grep -q hb_ERROR "$@" \ + || ($(RM) "$@"; false) GENERATORS = \ gen-arabic-table.py \ gen-indic-table.py \ $(NULL) - EXTRA_DIST += $(GENERATORS) unicode-tables: arabic-table indic-table indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt - $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.hh.tmp && \ - mv hb-ot-shape-complex-indic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-indic-table.hh || \ - ($(RM) hb-ot-shape-complex-indic-table.hh.tmp; false) + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \ + || ($(RM) hb-ot-shape-complex-indic-table.cc; false) -arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt - $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \ - mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \ - ($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false) +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) +built-sources: $(BUILT_SOURCES) -.PHONY: unicode-tables arabic-table indic-table +.PHONY: unicode-tables arabic-table indic-table built-sources -BUILT_SOURCES += hb-ot-shape-complex-indic-machine.hh -EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl -hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl - $(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \ - mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false ) +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-sea-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-sea-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 indic +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) -indic_SOURCES = indic.cc -indic_CPPFLAGS = $(HBCFLAGS) -indic_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-internal-symbols.sh \ check-includes.sh \ + check-libstdc++.sh \ + check-static-inits.sh \ + check-symbols.sh \ $(NULL) -if HAVE_ICU -else -dist_check_SCRIPTS += check-libstdc++.sh -endif - TESTS = $(dist_check_SCRIPTS) TESTS_ENVIRONMENT = \ srcdir="$(srcdir)" \ MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ HBSOURCES="$(HBSOURCES)" \ - HBHEADERS="$(HBHEADERS)" \ + HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \ + $(NULL) + +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = HarfBuzz-$(HB_VERSION_MAJOR).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 \ + $(NULL) +HarfBuzz_0_0_gir_LIBS = \ + libharfbuzz.la \ + libharfbuzz-gobject.la \ + $(NULL) +HarfBuzz_0_0_gir_FILES = \ + $(HBHEADERS) \ + $(HBNODISTHEADERS) \ + $(HBSOURCES) \ + hb-gobject-enums.cc \ + hb-gobject-enums.h \ + hb-gobject-structs.cc \ + hb-gobject-structs.h \ $(NULL) -scan: - g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library libharfbuzz.la +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/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh index e7c95ab..b10310f 100755 --- a/src/check-c-linkage-decls.sh +++ b/src/check-c-linkage-decls.sh @@ -6,13 +6,21 @@ export LC_ALL test -z "$srcdir" && srcdir=. stat=0 -test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'` +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 HB_BEGIN_DECLS / HB_END_DECLS" + 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 diff --git a/src/check-defs.sh b/src/check-defs.sh new file mode 100755 index 0000000..65a2467 --- /dev/null +++ b/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/src/check-header-guards.sh b/src/check-header-guards.sh index af9fa7f..9a3302c 100755 --- a/src/check-header-guards.sh +++ b/src/check-header-guards.sh @@ -6,8 +6,8 @@ export LC_ALL test -z "$srcdir" && srcdir=. stat=0 -test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'` -test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` +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 diff --git a/src/check-includes.sh b/src/check-includes.sh index 79323a7..902f235 100755 --- a/src/check-includes.sh +++ b/src/check-includes.sh @@ -6,16 +6,14 @@ export LC_ALL test -z "$srcdir" && srcdir=. stat=0 -test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'` -test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` - - -cd "$srcdir" +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"' | @@ -28,7 +26,8 @@ grep . >&2 && stat=1 echo 'Checking that source files #include "hb-*private.hh" first (or none)' for x in $HBSOURCES; do - grep '#.*\<include\>' "$x" /dev/null | head -n 1 + 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:' | @@ -36,7 +35,10 @@ grep . >&2 && stat=1 echo 'Checking that there is no #include <hb.*.h>' -grep '#.*\<include\>.*<.*hb' $HBHEADERS $HBSOURCES >&2 && stat=1 +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/src/check-internal-symbols.sh b/src/check-internal-symbols.sh deleted file mode 100755 index a24a693..0000000 --- a/src/check-internal-symbols.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/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-internal-symbols.sh: 'nm' not found; skipping test" - exit 77 -fi - -tested=false -for suffix in so; do - so=.libs/libharfbuzz.$suffix - if test -f "$so"; then - echo "Checking that we are exposing internal symbols" - if nm $so | grep ' T ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then - echo "Ouch, internal symbols exposed" - stat=1 - fi - tested=true - fi -done -if ! $tested; then - echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test" - exit 77 -fi - -exit $stat diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh index 0521532..27deb42 100755 --- a/src/check-libstdc++.sh +++ b/src/check-libstdc++.sh @@ -17,17 +17,17 @@ fi tested=false for suffix in so dylib; do so=.libs/libharfbuzz.$suffix - if test -f "$so"; then - echo "Checking that we are not linking to libstdc++" - if ldd $so | grep 'libstdc[+][+]'; then - echo "Ouch, linked to libstdc++" - stat=1 - fi - tested=true + if ! test -f "$so"; then continue; fi + + echo "Checking that we are not linking to libstdc++" + if ldd $so | grep 'libstdc[+][+]'; then + echo "Ouch, linked to libstdc++" + stat=1 fi + tested=true done if ! $tested; then - echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test" + echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test" exit 77 fi diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh new file mode 100755 index 0000000..1446fa7 --- /dev/null +++ b/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/src/check-symbols.sh b/src/check-symbols.sh new file mode 100755 index 0000000..b2bf43f --- /dev/null +++ b/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\>\| 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/src/gen-arabic-table.py b/src/gen-arabic-table.py index 2d3c881..308435f 100755 --- a/src/gen-arabic-table.py +++ b/src/gen-arabic-table.py @@ -3,34 +3,48 @@ import sys import os.path -if len (sys.argv) != 3: - print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt" +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 ()]] +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: -def print_joining_table(f): + j = line.find ('#') + if j >= 0: + line = line[:j] - print - print "static const uint8_t joining_table[] =" - print "{" + 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): - min_u = 0x110000 - max_u = 0 - num = 0 - last = -1 - block = '' + values = {} for line in f: if line[0] == '#': - if line.find (" characters"): - block = line[2:].strip () continue fields = [x.strip () for x in line.split (';')] @@ -38,43 +52,100 @@ def print_joining_table(f): continue u = int (fields[0], 16) - if u == 0x200C or u == 0x200D: - continue - if u < last: - raise Exception ("Input data character not sorted", u) - min_u = min (min_u, u) - max_u = max (max_u, u) - num += 1 - - if block: - print "\n /* %s */\n" % block - block = '' - - if last != -1: - last += 1 - while last < u: - print " JOINING_TYPE_X, /* %04X */" % last - last += 1 - else: - last = u if fields[3] in ["ALAPH", "DALATH RISH"]: value = "JOINING_GROUP_" + fields[3].replace(' ', '_') else: value = "JOINING_TYPE_" + fields[2] - print " %s, /* %s */" % (value, '; '.join(fields)) + 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 - 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 "#define JOINING_TABLE_FIRST 0x%04X" % min_u - print "#define JOINING_TABLE_LAST 0x%04X" % max_u + 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 - occupancy = num * 100 / (max_u - min_u + 1) - # Maintain at least 40% occupancy in the table */ - if occupancy < 40: - raise Exception ("Table too sparse, please investigate: ", occupancy) + 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): @@ -122,15 +193,15 @@ def print_shaping_table(f): keys = shapes.keys () min_u, max_u = min (keys), max (keys) for u in range (min_u, max_u + 1): - s = [shapes[u][shape] if u in shapes and shape in shapes[u] else u + 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%04X" % c for c in s) + 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%04X" % min_u - print "#define SHAPING_TABLE_LAST 0x%04X" % max_u + print "#define SHAPING_TABLE_FIRST 0x%04Xu" % min_u + print "#define SHAPING_TABLE_LAST 0x%04Xu" % max_u print ligas = {} @@ -148,9 +219,9 @@ def print_shaping_table(f): ligas[liga[0]].append ((liga[1], c)) max_i = max (len (ligas[l]) for l in ligas) print - print "static const struct {" + print "static const struct ligature_set_t {" print " uint16_t first;" - print " struct {" + print " struct ligature_pairs_t {" print " uint16_t second;" print " uint16_t ligature;" print " } ligatures[%d];" % max_i @@ -160,9 +231,9 @@ def print_shaping_table(f): keys.sort () for first in keys: - print " { 0x%04X, {" % (first) + print " { 0x%04Xu, {" % (first) for liga in ligas[first]: - print " { 0x%04X, 0x%04X }, /* %s */" % (liga[0], liga[1], names[liga[1]]) + print " { 0x%04Xu, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]]) print " }}," print "};" @@ -174,7 +245,7 @@ print "/* == Start of generated table == */" print "/*" print " * The following table is generated by running:" print " *" -print " * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt" +print " * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt" print " *" print " * on files with these headers:" print " *" @@ -187,6 +258,7 @@ 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]) diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py index 94aa2ab..f5716bd 100755 --- a/src/gen-indic-table.py +++ b/src/gen-indic-table.py @@ -6,11 +6,12 @@ if len (sys.argv) != 4: print >>sys.stderr, "usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.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 f in files] -blocks = {} data = [{} for f in files] values = [{} for f in files] for i, f in enumerate (files): @@ -35,10 +36,7 @@ for i, f in enumerate (files): for u in range (start, end + 1): data[i][u] = t - values[i][t] = values[i].get (t, 0) + 1 - - if i == 2: - blocks[t] = (start, end) + values[i][t] = values[i].get (t, 0) + end - start + 1 # Merge data into one dict: defaults = ('Other', 'Not_Applicable', 'No_Block') @@ -52,10 +50,15 @@ for i,d in enumerate (data): if not u in combined: combined[u] = list (defaults) combined[u][i] = v +combined = {k:v for k,v in combined.items() if v[2] not in BLACKLISTED_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 [0x00A0, 0x25CC]: @@ -75,13 +78,16 @@ for h in headers: print " * %s" % (l.strip()) print " */" print -print "#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH" -print "#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH" +print '#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', @@ -89,14 +95,14 @@ short = [{ },{ "Not_Applicable": 'x', }] -all_shorts = [[],[]] +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].append (s) + all_shorts[i][s] = v what = ["INDIC_SYLLABIC_CATEGORY", "INDIC_MATRA_CATEGORY"] what_short = ["ISC", "IMC"] @@ -111,8 +117,8 @@ for i in range (2): 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, s) - all_shorts[i].append (s) + 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 (), \ @@ -125,11 +131,16 @@ print total = 0 used = 0 +last_block = None def print_block (block, start, end, data): - print - print - print " /* %s (%04X..%04X) */" % (block, start, end) + 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 @@ -139,14 +150,15 @@ def print_block (block, start, end, data): d = data.get (u, defaults) sys.stdout.write ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]]))) - global total, used total += end - start + 1 used += num + if block: + last_block = block uu = data.keys () uu.sort () -last = -1 +last = -100000 num = 0 offset = 0 starts = [] @@ -156,11 +168,16 @@ for u in uu: if u <= last: continue block = data[u][2] - (start, end) = blocks[block] + + 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 <= 33: - print_block ("FILLER", last+1, start-1, data) + if start - last <= 1+16*3: + print_block (None, last+1, start-1, data) last = start-1 else: if last >= 0: @@ -168,7 +185,7 @@ for u in uu: offset += ends[-1] - starts[-1] print print - print "#define indic_offset_0x%04x %d" % (start, offset) + print "#define indic_offset_0x%04xu %d" % (start, offset) starts.append (start) print_block (block, start, end, data) @@ -177,19 +194,30 @@ ends.append (last + 1) offset += ends[-1] - starts[-1] print print -print "#define indic_offset_total %d" % offset -print occupancy = used * 100. / total -print "}; /* Table occupancy: %d%% */" % occupancy +page_bits = 12 +print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy) print -print "static INDIC_TABLE_ELEMENT_TYPE" -print "get_indic_categories (hb_codepoint_t u)" +print "INDIC_TABLE_ELEMENT_TYPE" +print "hb_indic_get_categories (hb_codepoint_t u)" print "{" -for (start,end) in zip (starts, ends): - offset = "indic_offset_0x%04x" % start - print " if (0x%04X <= u && u <= 0x%04X) return indic_table[u - 0x%04X + %s];" % (start, end, start, offset) -for u,d in singles.items (): - print " if (unlikely (u == 0x%04X)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]]) +print " 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 @@ -202,8 +230,6 @@ for i in range (2): print "#undef %s_%s" % \ (what_short[i], short[i][v]) print -print "#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */" -print print "/* == End of generated table == */" # Maintain at least 30% occupancy in the table */ diff --git a/src/harfbuzz-gobject.pc.in b/src/harfbuzz-gobject.pc.in new file mode 100644 index 0000000..7008360 --- /dev/null +++ b/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/src/harfbuzz-icu.pc.in b/src/harfbuzz-icu.pc.in new file mode 100644 index 0000000..949869a --- /dev/null +++ b/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/src/harfbuzz.pc.in b/src/harfbuzz.pc.in new file mode 100644 index 0000000..7f27bbb --- /dev/null +++ b/src/harfbuzz.pc.in @@ -0,0 +1,11 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library +Version: %VERSION% + +Libs: -L${libdir} -lharfbuzz +Cflags: -I${includedir}/harfbuzz diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh index c543a8d..e6738b7 100644 --- a/src/hb-atomic-private.hh +++ b/src/hb-atomic-private.hh @@ -42,30 +42,54 @@ #if 0 -#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600 +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) -#include <intrin.h> -#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchangePointer) +#include <windows.h> -typedef long hb_atomic_int_t; -#define hb_atomic_int_add(AI, V) _InterlockedExchangeAdd (&(AI), (V)) +/* 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 +} -#define hb_atomic_ptr_get(P) (MemoryBarrier (), (void *) *(P)) -#define hb_atomic_ptr_cmpexch(P,O,N) (_InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) +typedef LONG hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) InterlockedExchangeAdd (&(AI), (V)) + +#define hb_atomic_ptr_get(P) (_HBMemoryBarrier (), (void *) *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) #elif !defined(HB_NO_MT) && defined(__APPLE__) #include <libkern/OSAtomic.h> +#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_t; #define hb_atomic_int_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) #define hb_atomic_ptr_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_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) +#else +#if __ppc64__ || __x86_64__ || __aarch64__ +#define hb_atomic_ptr_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P)) +#else +#define hb_atomic_ptr_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) && !defined(__MINGW32__) +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) typedef int hb_atomic_int_t; #define hb_atomic_int_add(AI, V) __sync_fetch_and_add (&(AI), (V)) @@ -73,18 +97,17 @@ typedef int hb_atomic_int_t; #define hb_atomic_ptr_get(P) (void *) (__sync_synchronize (), *(P)) #define hb_atomic_ptr_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) -#elif !defined(HB_NO_MT) && defined(HAVE_GLIB) -#include <glib.h> -typedef int hb_atomic_int_t; -#if GLIB_CHECK_VERSION(2,29,5) -#define hb_atomic_int_add(AI, V) g_atomic_int_add (&(AI), (V)) -#else -#define hb_atomic_int_add(AI, V) g_atomic_int_exchange_and_add (&(AI), (V)) -#endif +#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) + +#include <atomic.h> +#include <mbarrier.h> + +typedef unsigned int hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) ( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V)) -#define hb_atomic_ptr_get(P) g_atomic_pointer_get (P) -#define hb_atomic_ptr_cmpexch(P,O,N) g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (N)) +#define hb_atomic_ptr_get(P) ( ({__machine_rw_barrier ();}), (void *) *(P)) +#define hb_atomic_ptr_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) diff --git a/src/hb-blob.cc b/src/hb-blob.cc index 3cc2d9d..8759a25 100644 --- a/src/hb-blob.cc +++ b/src/hb-blob.cc @@ -24,9 +24,13 @@ * 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-blob.h" #include "hb-object-private.hh" #ifdef HAVE_SYS_MMAN_H @@ -46,7 +50,7 @@ #endif -struct _hb_blob_t { +struct hb_blob_t { hb_object_header_t header; ASSERT_POD (); @@ -73,6 +77,22 @@ _hb_blob_destroy_user_data (hb_blob_t *blob) } } +/** + * 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: 1.0 + **/ hb_blob_t * hb_blob_create (const char *data, unsigned int length, @@ -82,7 +102,10 @@ hb_blob_create (const char *data, { hb_blob_t *blob; - if (!length || !(blob = hb_object_create<hb_blob_t> ())) { + if (!length || + length >= 1u << 31 || + data + length < data /* overflows */ || + !(blob = hb_object_create<hb_blob_t> ())) { if (destroy) destroy (user_data); return hb_blob_get_empty (); @@ -106,6 +129,26 @@ hb_blob_create (const char *data, 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: 1.0 + **/ hb_blob_t * hb_blob_create_sub_blob (hb_blob_t *parent, unsigned int offset, @@ -120,13 +163,24 @@ hb_blob_create_sub_blob (hb_blob_t *parent, blob = hb_blob_create (parent->data + offset, MIN (length, parent->length - offset), - parent->mode, + 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: 1.0 + **/ hb_blob_t * hb_blob_get_empty (void) { @@ -146,12 +200,36 @@ hb_blob_get_empty (void) 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: 1.0 + **/ 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: 1.0 + **/ void hb_blob_destroy (hb_blob_t *blob) { @@ -162,6 +240,18 @@ hb_blob_destroy (hb_blob_t *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: 1.0 + **/ hb_bool_t hb_blob_set_user_data (hb_blob_t *blob, hb_user_data_key_t *key, @@ -172,6 +262,17 @@ hb_blob_set_user_data (hb_blob_t *blob, 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: 1.0 + **/ void * hb_blob_get_user_data (hb_blob_t *blob, hb_user_data_key_t *key) @@ -180,6 +281,14 @@ hb_blob_get_user_data (hb_blob_t *blob, } +/** + * hb_blob_make_immutable: + * @blob: a blob. + * + * + * + * Since: 1.0 + **/ void hb_blob_make_immutable (hb_blob_t *blob) { @@ -189,6 +298,16 @@ hb_blob_make_immutable (hb_blob_t *blob) blob->immutable = true; } +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * + * + * Return value: TODO + * + * Since: 1.0 + **/ hb_bool_t hb_blob_is_immutable (hb_blob_t *blob) { @@ -196,12 +315,33 @@ hb_blob_is_immutable (hb_blob_t *blob) } +/** + * hb_blob_get_length: + * @blob: a blob. + * + * + * + * Return value: the length of blob data in bytes. + * + * Since: 1.0 + **/ 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: 1.0 + **/ const char * hb_blob_get_data (hb_blob_t *blob, unsigned int *length) { @@ -211,6 +351,22 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *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: 1.0 + **/ char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) { @@ -321,5 +477,3 @@ _try_writable (hb_blob_t *blob) return true; } - - diff --git a/src/hb-blob.h b/src/hb-blob.h index 360310b..b2419ab 100644 --- a/src/hb-blob.h +++ b/src/hb-blob.h @@ -36,6 +36,25 @@ 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, @@ -43,7 +62,7 @@ typedef enum { HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE } hb_memory_mode_t; -typedef struct _hb_blob_t hb_blob_t; +typedef struct hb_blob_t hb_blob_t; hb_blob_t * hb_blob_create (const char *data, @@ -52,6 +71,12 @@ hb_blob_create (const char *data, 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_blob_t * hb_blob_create_sub_blob (hb_blob_t *parent, unsigned int offset, diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl new file mode 100644 index 0000000..91b350f --- /dev/null +++ b/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/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl new file mode 100644 index 0000000..8a682f7 --- /dev/null +++ b/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/src/hb-buffer-private.hh b/src/hb-buffer-private.hh index b539f26..069f925 100644 --- a/src/hb-buffer-private.hh +++ b/src/hb-buffer-private.hh @@ -1,7 +1,7 @@ /* * Copyright © 1998-2004 David Turner and Werner Lemberg * Copyright © 2004,2007,2009,2010 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -31,33 +31,30 @@ #define HB_BUFFER_PRIVATE_HH #include "hb-private.hh" -#include "hb-buffer.h" #include "hb-object-private.hh" #include "hb-unicode-private.hh" - ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)); -typedef struct _hb_segment_properties_t { - hb_direction_t direction; - hb_script_t script; - hb_language_t language; - ASSERT_POD (); -} hb_segment_properties_t; +/* + * hb_buffer_t + */ -struct _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_segment_properties_t props; /* Script, language, direction */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_codepoint_t replacement; /* U+FFFD or something else. */ /* 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 */ @@ -81,49 +78,80 @@ struct _hb_buffer_t { inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; } inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; } + inline bool has_separate_output (void) const { return info != out_info; } + unsigned int serial; + + /* These reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ uint8_t allocated_var_bytes[8]; const char *allocated_var_owner[8]; + /* 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]; + /* 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 allocate_var (unsigned int byte_i, unsigned int count, const char *owner); HB_INTERNAL void deallocate_var (unsigned int byte_i, unsigned int count, const char *owner); + HB_INTERNAL void assert_var (unsigned int byte_i, unsigned int count, const char *owner); HB_INTERNAL void deallocate_var_all (void); HB_INTERNAL void add (hb_codepoint_t codepoint, - hb_mask_t mask, 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_properties (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_be16 (unsigned int num_in, - unsigned int num_out, - const uint16_t *glyph_data_be); + HB_INTERNAL void replace_glyphs (unsigned int num_in, unsigned int num_out, const hb_codepoint_t *glyph_data); + HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index); /* Makes a copy of the glyph at idx to output and replace glyph_index */ HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index); + 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. */ - HB_INTERNAL void next_glyph (void); + 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++; } @@ -151,11 +179,18 @@ struct _hb_buffer_t { HB_INTERNAL bool enlarge (unsigned int size); inline bool ensure (unsigned int size) - { return likely (size <= allocated) ? true : enlarge (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); - HB_INTERNAL void *get_scratch_buffer (unsigned int *size); + 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; } }; @@ -166,7 +201,8 @@ struct _hb_buffer_t { HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var (), #var) #define HB_BUFFER_DEALLOCATE_VAR(b, var) \ HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var) - +#define HB_BUFFER_ASSERT_VAR(b, var) \ + HB_BUFFER_XALLOCATE_VAR (b, assert_var, var (), #var) #endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc new file mode 100644 index 0000000..406d69d --- /dev/null +++ b/src/hb-buffer-serialize.cc @@ -0,0 +1,399 @@ +/* + * 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: + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ +const char ** +hb_buffer_serialize_list_formats (void) +{ + return serialize_formats; +} + +/** + * hb_buffer_serialize_format_from_string: + * @str: + * @len: + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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: + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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 = 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); + } + + *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 = 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)); + } + + 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; +} + +/* Returns number of items, starting at start, that were serialized. */ +/** + * hb_buffer_serialize_glyphs: + * @buffer: a buffer. + * @start: + * @end: + * @buf: (array length=buf_size): + * @buf_size: + * @buf_consumed: (out): + * @font: + * @format: + * @flags: + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + 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 (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: a buffer. + * @buf: (array length=buf_len): + * @buf_len: + * @end_ptr: (out): + * @font: + * @format: + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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/src/hb-buffer.cc b/src/hb-buffer.cc index e2c34f1..b9fe263 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -1,7 +1,7 @@ /* * Copyright © 1998-2004 David Turner and Werner Lemberg * Copyright © 2004,2007,2009,2010 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -28,17 +28,35 @@ */ #include "hb-buffer-private.hh" - -#include <string.h> - +#include "hb-utf-private.hh" #ifndef HB_DEBUG_BUFFER #define HB_DEBUG_BUFFER (HB_DEBUG+0) #endif -#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default)) -#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID } + +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; + +} + +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: * @@ -76,7 +94,7 @@ hb_buffer_t::enlarge (unsigned int size) if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) goto done; - while (size > new_allocated) + while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); @@ -121,17 +139,35 @@ hb_buffer_t::make_room_for (unsigned int num_in, return true; } -void * +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])); + 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; - *size = allocated * sizeof (pos[0]); - return pos; + 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 @@ -141,11 +177,23 @@ hb_buffer_t::reset (void) return; hb_unicode_funcs_destroy (unicode); - unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT; + 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_BUFFER_PROPS_DEFAULT; + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; props = default_props; + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; in_error = false; have_output = false; have_positions = false; @@ -153,17 +201,18 @@ hb_buffer_t::reset (void) idx = 0; len = 0; out_len = 0; + out_info = info; serial = 0; memset (allocated_var_bytes, 0, sizeof allocated_var_bytes); memset (allocated_var_owner, 0, sizeof allocated_var_owner); - out_info = info; + memset (context, 0, sizeof context); + memset (context_len, 0, sizeof context_len); } void hb_buffer_t::add (hb_codepoint_t codepoint, - hb_mask_t mask, unsigned int cluster) { hb_glyph_info_t *glyph; @@ -174,13 +223,37 @@ hb_buffer_t::add (hb_codepoint_t codepoint, memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; - glyph->mask = mask; + 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))) @@ -202,6 +275,9 @@ hb_buffer_t::clear_positions (void) have_output = false; have_positions = true; + out_len = 0; + out_info = info; + memset (pos, 0, sizeof (pos[0]) * len); } @@ -230,46 +306,17 @@ hb_buffer_t::swap_buffers (void) idx = 0; } -void -hb_buffer_t::replace_glyphs_be16 (unsigned int num_in, - unsigned int num_out, - const uint16_t *glyph_data_be) -{ - if (!make_room_for (num_in, num_out)) return; - - hb_glyph_info_t orig_info = info[idx]; - for (unsigned int i = 1; i < num_in; i++) - { - hb_glyph_info_t *inf = &info[idx + i]; - orig_info.cluster = MIN (orig_info.cluster, inf->cluster); - } - - hb_glyph_info_t *pinfo = &out_info[out_len]; - for (unsigned int i = 0; i < num_out; i++) - { - *pinfo = orig_info; - pinfo->codepoint = hb_be_uint16 (glyph_data_be[i]); - pinfo++; - } - - idx += num_in; - out_len += num_out; -} void hb_buffer_t::replace_glyphs (unsigned int num_in, unsigned int num_out, const uint32_t *glyph_data) { - if (!make_room_for (num_in, num_out)) return; + if (unlikely (!make_room_for (num_in, num_out))) return; - hb_glyph_info_t orig_info = info[idx]; - for (unsigned int i = 1; i < num_in; i++) - { - hb_glyph_info_t *inf = &info[idx + i]; - orig_info.cluster = MIN (orig_info.cluster, inf->cluster); - } + 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++) { @@ -285,7 +332,7 @@ hb_buffer_t::replace_glyphs (unsigned int num_in, void hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) { - if (!make_room_for (0, 1)) return; + if (unlikely (!make_room_for (0, 1))) return; out_info[out_len] = info[idx]; out_info[out_len].codepoint = glyph_index; @@ -294,46 +341,77 @@ hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) } void -hb_buffer_t::copy_glyph (void) +hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) { - if (!make_room_for (0, 1)) return; + if (unlikely (!make_room_for (0, 1))) return; - out_info[out_len] = info[idx]; + out_info[out_len] = glyph_info; out_len++; } void -hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) +hb_buffer_t::copy_glyph (void) { - if (!make_room_for (1, 1)) return; + if (unlikely (!make_room_for (0, 1))) return; out_info[out_len] = info[idx]; - out_info[out_len].codepoint = glyph_index; - idx++; out_len++; } -void -hb_buffer_t::next_glyph (void) +bool +hb_buffer_t::move_to (unsigned int i) { - if (have_output) + if (!have_output) { - if (out_info != info) - { - if (unlikely (!ensure (out_len + 1))) return; - out_info[out_len] = info[idx]; - } - else if (out_len != idx) - out_info[out_len] = info[idx]; + assert (i <= len); + idx = i; + return true; + } - out_len++; + 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; + + 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, @@ -365,7 +443,7 @@ hb_buffer_t::reverse_range (unsigned int start, { unsigned int i, j; - if (start == end - 1) + if (end - start < 2) return; for (i = start, j = end - 1; i < j; i++, j--) { @@ -376,7 +454,7 @@ hb_buffer_t::reverse_range (unsigned int start, info[j] = t; } - if (pos) { + if (have_positions) { for (i = start, j = end - 1; i < j; i++, j--) { hb_glyph_position_t t; @@ -423,32 +501,77 @@ void hb_buffer_t::merge_clusters (unsigned int start, unsigned int end) { - unsigned int cluster = this->info[start].cluster; +#ifdef HB_NO_MERGE_CLUSTERS + return; +#endif + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, this->info[i].cluster); + 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 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++) - this->info[i].cluster = cluster; + info[i].cluster = cluster; } void hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int end) { - unsigned int cluster = this->out_info[start].cluster; +#ifdef HB_NO_MERGE_CLUSTERS + return; +#endif + + 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, this->out_info[i].cluster); + 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 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++) - this->out_info[i].cluster = cluster; + out_info[i].cluster = cluster; } void -hb_buffer_t::guess_properties (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 = hb_unicode_script (unicode, info[i].codepoint); + hb_script_t script = unicode->script (info[i].codepoint); if (likely (script != HB_SCRIPT_COMMON && script != HB_SCRIPT_INHERITED && script != HB_SCRIPT_UNKNOWN)) { @@ -487,7 +610,7 @@ void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const c { assert (byte_i < 8 && byte_i + count <= 8); - if (DEBUG (BUFFER)) + if (DEBUG_ENABLED (BUFFER)) dump_var_allocation (this); DEBUG_MSG (BUFFER, this, "Allocating var bytes %d..%d for %s", @@ -502,7 +625,7 @@ void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const c void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner) { - if (DEBUG (BUFFER)) + if (DEBUG_ENABLED (BUFFER)) dump_var_allocation (this); DEBUG_MSG (BUFFER, this, @@ -517,6 +640,22 @@ void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const } } +void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner) +{ + if (DEBUG_ENABLED (BUFFER)) + dump_var_allocation (this); + + DEBUG_MSG (BUFFER, this, + "Asserting var bytes %d..%d for %s", + byte_i, byte_i + count - 1, owner); + + assert (byte_i < 8 && byte_i + count <= 8); + for (unsigned int i = byte_i; i < byte_i + count; i++) { + assert (allocated_var_bytes[i]); + assert (0 == strcmp (allocated_var_owner[i], owner)); + } +} + void hb_buffer_t::deallocate_var_all (void) { memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes)); @@ -525,8 +664,17 @@ void hb_buffer_t::deallocate_var_all (void) /* Public API */ +/** + * hb_buffer_create: (Xconstructor) + * + * + * + * Return value: (transfer full) + * + * Since: 1.0 + **/ hb_buffer_t * -hb_buffer_create () +hb_buffer_create (void) { hb_buffer_t *buffer; @@ -538,29 +686,61 @@ hb_buffer_create () return buffer; } +/** + * hb_buffer_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_buffer_t * hb_buffer_get_empty (void) { static const hb_buffer_t _hb_buffer_nil = { HB_OBJECT_HEADER_STATIC, - _HB_BUFFER_UNICODE_FUNCS_DEFAULT, - _HB_BUFFER_PROPS_DEFAULT, + const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_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: a buffer. + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_buffer_t * hb_buffer_reference (hb_buffer_t *buffer) { return hb_object_reference (buffer); } +/** + * hb_buffer_destroy: (skip) + * @buffer: a buffer. + * + * + * + * Since: 1.0 + **/ void hb_buffer_destroy (hb_buffer_t *buffer) { @@ -574,6 +754,20 @@ hb_buffer_destroy (hb_buffer_t *buffer) free (buffer); } +/** + * hb_buffer_set_user_data: (skip) + * @buffer: a buffer. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_buffer_set_user_data (hb_buffer_t *buffer, hb_user_data_key_t *key, @@ -584,6 +778,17 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, return hb_object_set_user_data (buffer, key, data, destroy, replace); } +/** + * hb_buffer_get_user_data: (skip) + * @buffer: a buffer. + * @key: + * + * + * + * Return value: + * + * Since: 1.0 + **/ void * hb_buffer_get_user_data (hb_buffer_t *buffer, hb_user_data_key_t *key) @@ -592,27 +797,89 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, } +/** + * hb_buffer_set_content_type: + * @buffer: a buffer. + * @content_type: + * + * + * + * Since: 1.0 + **/ +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: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_unicode_funcs: + * @buffer: a buffer. + * @unicode_funcs: + * + * + * + * Since: 1.0 + **/ void hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, - hb_unicode_funcs_t *unicode) + hb_unicode_funcs_t *unicode_funcs) { if (unlikely (hb_object_is_inert (buffer))) return; - if (!unicode) - unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT; + if (!unicode_funcs) + unicode_funcs = hb_unicode_funcs_get_default (); - hb_unicode_funcs_reference (unicode); + + hb_unicode_funcs_reference (unicode_funcs); hb_unicode_funcs_destroy (buffer->unicode); - buffer->unicode = unicode; + buffer->unicode = unicode_funcs; } +/** + * hb_buffer_get_unicode_funcs: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_unicode_funcs_t * hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) { return buffer->unicode; } +/** + * hb_buffer_set_direction: + * @buffer: a buffer. + * @direction: + * + * + * + * Since: 1.0 + **/ void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction) @@ -624,12 +891,31 @@ hb_buffer_set_direction (hb_buffer_t *buffer, buffer->props.direction = direction; } +/** + * hb_buffer_get_direction: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_direction_t hb_buffer_get_direction (hb_buffer_t *buffer) { return buffer->props.direction; } +/** + * hb_buffer_set_script: + * @buffer: a buffer. + * @script: + * + * + * + * Since: 1.0 + **/ void hb_buffer_set_script (hb_buffer_t *buffer, hb_script_t script) @@ -640,12 +926,31 @@ hb_buffer_set_script (hb_buffer_t *buffer, buffer->props.script = script; } +/** + * hb_buffer_get_script: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_script_t hb_buffer_get_script (hb_buffer_t *buffer) { return buffer->props.script; } +/** + * hb_buffer_set_language: + * @buffer: a buffer. + * @language: + * + * + * + * Since: 1.0 + **/ void hb_buffer_set_language (hb_buffer_t *buffer, hb_language_t language) @@ -656,40 +961,221 @@ hb_buffer_set_language (hb_buffer_t *buffer, buffer->props.language = language; } +/** + * hb_buffer_get_language: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_language_t hb_buffer_get_language (hb_buffer_t *buffer) { return buffer->props.language; } +/** + * hb_buffer_set_segment_properties: + * @buffer: a buffer. + * @props: + * + * + * + * Since: 1.0 + **/ +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: a buffer. + * @props: + * + * + * + * Since: 1.0 + **/ +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: a buffer. + * @flags: + * + * + * + * Since: 1.0 + **/ +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: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer) +{ + return buffer->flags; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @buffer: a buffer. + * @replacement: + * + * + * + * Since: 1.0 + **/ +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: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +{ + return buffer->replacement; +} + +/** + * hb_buffer_reset: + * @buffer: a buffer. + * + * + * + * Since: 1.0 + **/ void hb_buffer_reset (hb_buffer_t *buffer) { buffer->reset (); } +/** + * hb_buffer_clear_contents: + * @buffer: a buffer. + * + * + * + * Since: 1.0 + **/ +void +hb_buffer_clear_contents (hb_buffer_t *buffer) +{ + buffer->clear (); +} + +/** + * hb_buffer_pre_allocate: + * @buffer: a buffer. + * @size: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) { return buffer->ensure (size); } +/** + * hb_buffer_allocation_successful: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_buffer_allocation_successful (hb_buffer_t *buffer) { return !buffer->in_error; } +/** + * hb_buffer_add: + * @buffer: a buffer. + * @codepoint: + * @cluster: + * + * + * + * Since: 1.0 + **/ void hb_buffer_add (hb_buffer_t *buffer, hb_codepoint_t codepoint, - hb_mask_t mask, unsigned int cluster) { - buffer->add (codepoint, mask, cluster); + buffer->add (codepoint, cluster); + buffer->clear_context (1); } +/** + * hb_buffer_set_length: + * @buffer: a buffer. + * @length: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length) @@ -708,16 +1194,45 @@ hb_buffer_set_length (hb_buffer_t *buffer, } 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: a buffer. + * + * Returns the number of items in the buffer. + * + * Return value: buffer length. + * + * Since: 1.0 + **/ unsigned int hb_buffer_get_length (hb_buffer_t *buffer) { return buffer->len; } -/* Return value valid as long as buffer not modified */ +/** + * hb_buffer_get_glyph_infos: + * @buffer: a buffer. + * @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): buffer glyph information array. + * + * Since: 1.0 + **/ hb_glyph_info_t * hb_buffer_get_glyph_infos (hb_buffer_t *buffer, unsigned int *length) @@ -728,7 +1243,18 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, return (hb_glyph_info_t *) buffer->info; } -/* Return value valid as long as buffer not modified */ +/** + * hb_buffer_get_glyph_positions: + * @buffer: a buffer. + * @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): buffer glyph position array. + * + * Since: 1.0 + **/ hb_glyph_position_t * hb_buffer_get_glyph_positions (hb_buffer_t *buffer, unsigned int *length) @@ -742,88 +1268,147 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer, return (hb_glyph_position_t *) buffer->pos; } +/** + * hb_buffer_reverse: + * @buffer: a buffer. + * + * Reverses buffer contents. + * + * Since: 1.0 + **/ void hb_buffer_reverse (hb_buffer_t *buffer) { buffer->reverse (); } +/** + * hb_buffer_reverse_clusters: + * @buffer: a buffer. + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 1.0 + **/ void hb_buffer_reverse_clusters (hb_buffer_t *buffer) { buffer->reverse_clusters (); } +/** + * hb_buffer_guess_segment_properties: + * @buffer: a buffer. + * + * 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: 1.0 + **/ void -hb_buffer_guess_properties (hb_buffer_t *buffer) -{ - buffer->guess_properties (); -} - -#define ADD_UTF(T) \ - HB_STMT_START { \ - if (text_length == -1) { \ - text_length = 0; \ - const T *p = (const T *) text; \ - while (*p) { \ - text_length++; \ - p++; \ - } \ - } \ - if (item_length == -1) \ - item_length = text_length - item_offset; \ - buffer->ensure (buffer->len + item_length * sizeof (T) / 4); \ - const T *next = (const T *) text + item_offset; \ - const T *end = next + item_length; \ - while (next < end) { \ - hb_codepoint_t u; \ - const T *old_next = next; \ - next = UTF_NEXT (next, end, u); \ - hb_buffer_add (buffer, u, 1, old_next - (const T *) text); \ - } \ - } HB_STMT_END - - -#define UTF8_COMPUTE(Char, Mask, Len) \ - if (Char < 128) { Len = 1; Mask = 0x7f; } \ - else if ((Char & 0xe0) == 0xc0) { Len = 2; Mask = 0x1f; } \ - else if ((Char & 0xf0) == 0xe0) { Len = 3; Mask = 0x0f; } \ - else if ((Char & 0xf8) == 0xf0) { Len = 4; Mask = 0x07; } \ - else Len = 0; - -static inline const uint8_t * -hb_utf8_next (const uint8_t *text, - const uint8_t *end, - hb_codepoint_t *unicode) -{ - uint8_t c = *text; - unsigned int mask, len; - - /* TODO check for overlong sequences? */ - - UTF8_COMPUTE (c, mask, len); - if (unlikely (!len || (unsigned int) (end - text) < len)) { - *unicode = -1; - return text + 1; - } else { - hb_codepoint_t result; - unsigned int i; - result = c & mask; - for (i = 1; i < len; i++) - { - if (unlikely ((text[i] & 0xc0) != 0x80)) - { - *unicode = -1; - return text + 1; - } - result <<= 6; - result |= (text[i] & 0x3f); - } - *unicode = result; - return text + len; +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: a buffer. + * @text: (array length=text_length) (element-type uint8_t): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 1.0 + **/ void hb_buffer_add_utf8 (hb_buffer_t *buffer, const char *text, @@ -831,45 +1416,43 @@ hb_buffer_add_utf8 (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { -#define UTF_NEXT(S, E, U) hb_utf8_next (S, E, &(U)) - ADD_UTF (uint8_t); -#undef UTF_NEXT -} - -static inline const uint16_t * -hb_utf16_next (const uint16_t *text, - const uint16_t *end, - hb_codepoint_t *unicode) -{ - uint16_t c = *text++; - - if (unlikely (c >= 0xd800 && c < 0xdc00)) { - /* high surrogate */ - uint16_t l; - if (text < end && ((l = *text), likely (l >= 0xdc00 && l < 0xe000))) { - /* low surrogate */ - *unicode = ((hb_codepoint_t) ((c) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000); - text++; - } else - *unicode = -1; - } else - *unicode = c; - - return text; + hb_buffer_add_utf<hb_utf8_t> (buffer, (const uint8_t *) text, text_length, item_offset, item_length); } +/** + * hb_buffer_add_utf16: + * @buffer: a buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 1.0 + **/ void hb_buffer_add_utf16 (hb_buffer_t *buffer, const uint16_t *text, int text_length, unsigned int item_offset, - int item_length) + int item_length) { -#define UTF_NEXT(S, E, U) hb_utf16_next (S, E, &(U)) - ADD_UTF (uint16_t); -#undef UTF_NEXT + hb_buffer_add_utf<hb_utf16_t> (buffer, text, text_length, item_offset, item_length); } +/** + * hb_buffer_add_utf32: + * @buffer: a buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 1.0 + **/ void hb_buffer_add_utf32 (hb_buffer_t *buffer, const uint32_t *text, @@ -877,9 +1460,135 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { -#define UTF_NEXT(S, E, U) ((U) = *(S), (S)+1) - ADD_UTF (uint32_t); -#undef UTF_NEXT + hb_buffer_add_utf<hb_utf32_t<> > (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @buffer: a buffer. + * @text: (array length=text_length) (element-type uint8_t): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 1.0 + **/ +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 buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 1.0 + **/ +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_bubble_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_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @buffer: a buffer. + * + * + * + * Since: 1.0 + **/ +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); +} diff --git a/src/hb-buffer.h b/src/hb-buffer.h index fe53197..e5b46d8 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -1,7 +1,7 @@ /* * Copyright © 1998-2004 David Turner and Werner Lemberg * Copyright © 2004,2007,2009 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -36,13 +36,12 @@ #include "hb-common.h" #include "hb-unicode.h" +#include "hb-font.h" HB_BEGIN_DECLS -typedef struct _hb_buffer_t hb_buffer_t; - -typedef struct _hb_glyph_info_t { +typedef struct hb_glyph_info_t { hb_codepoint_t codepoint; hb_mask_t mask; uint32_t cluster; @@ -52,7 +51,7 @@ typedef struct _hb_glyph_info_t { hb_var_int_t var2; } hb_glyph_info_t; -typedef struct _hb_glyph_position_t { +typedef struct hb_glyph_position_t { hb_position_t x_advance; hb_position_t y_advance; hb_position_t x_offset; @@ -63,6 +62,36 @@ typedef struct _hb_glyph_position_t { } hb_glyph_position_t; +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_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p); + + + +/* + * hb_buffer_t + */ + +typedef struct hb_buffer_t hb_buffer_t; + hb_buffer_t * hb_buffer_create (void); @@ -87,6 +116,20 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, hb_user_data_key_t *key); +typedef enum { + HB_BUFFER_CONTENT_TYPE_INVALID = 0, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type); + +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer); + + void hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, hb_unicode_funcs_t *unicode_funcs); @@ -112,15 +155,59 @@ void hb_buffer_set_language (hb_buffer_t *buffer, hb_language_t language); + hb_language_t hb_buffer_get_language (hb_buffer_t *buffer); +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props); + +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props); + +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +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; + +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer); + + + +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +/* Sets codepoint used to replace invalid UTF-8/16/32 entries. + * Default is 0xFFFDu. */ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); + /* Resets the buffer. Afterwards it's as if it was just created, * except that it has a larger buffer allocated perhaps... */ void hb_buffer_reset (hb_buffer_t *buffer); +/* Like reset, but does NOT clear unicode_funcs and replacement_codepoint. */ +void +hb_buffer_clear_contents (hb_buffer_t *buffer); + /* Returns false if allocation failed */ hb_bool_t hb_buffer_pre_allocate (hb_buffer_t *buffer, @@ -137,16 +224,12 @@ hb_buffer_reverse (hb_buffer_t *buffer); void hb_buffer_reverse_clusters (hb_buffer_t *buffer); -void -hb_buffer_guess_properties (hb_buffer_t *buffer); - /* Filling the buffer in */ void hb_buffer_add (hb_buffer_t *buffer, hb_codepoint_t codepoint, - hb_mask_t mask, unsigned int cluster); void @@ -170,6 +253,22 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, unsigned int item_offset, int item_length); +/* Allows only access to first 256 Unicode codepoints. */ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +/* Like add_utf32 but does NOT check for invalid Unicode codepoints. */ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + /* Clears any new items added at the end */ hb_bool_t @@ -193,6 +292,61 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer, unsigned int *length); +/* 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. */ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +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_flags_t; + +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; + +/* len=-1 means str is NUL-terminated. */ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +const char ** +hb_buffer_serialize_list_formats (void); + +/* Returns number of items, starting at start, that were serialized. */ +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, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +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); + + HB_END_DECLS #endif /* HB_BUFFER_H */ diff --git a/src/hb-cache-private.hh b/src/hb-cache-private.hh index a0928a0..19b70b7 100644 --- a/src/hb-cache-private.hh +++ b/src/hb-cache-private.hh @@ -49,6 +49,8 @@ struct hb_cache_t unsigned int v = values[k]; if ((v >> value_bits) != (key >> cache_bits)) return false; + *value = v & ((1<<value_bits)-1); + return true; } inline bool set (unsigned int key, unsigned int value) diff --git a/src/hb-common.cc b/src/hb-common.cc index 331d255..1516211 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -28,36 +28,81 @@ #include "hb-private.hh" -#include "hb-version.h" - #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: 1.0 + **/ hb_tag_t -hb_tag_from_string (const char *s, int len) +hb_tag_from_string (const char *str, int len) { char tag[4]; unsigned int i; - if (!s || !len || !*s) + if (!str || !len || !*str) return HB_TAG_NONE; if (len < 0 || len > 4) len = 4; - for (i = 0; i < (unsigned) len && s[i]; i++) - tag[i] = s[i]; + 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: (array fixed-size=4): + * + * + * + * Since: 1.0 + **/ +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 */ @@ -68,6 +113,17 @@ const char direction_strings[][4] = { "btt" }; +/** + * hb_direction_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_direction_t hb_direction_from_string (const char *str, int len) { @@ -85,6 +141,16 @@ hb_direction_from_string (const char *str, int len) return HB_DIRECTION_INVALID; } +/** + * hb_direction_to_string: + * @direction: + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ const char * hb_direction_to_string (hb_direction_t direction) { @@ -98,7 +164,7 @@ hb_direction_to_string (hb_direction_t direction) /* hb_language_t */ -struct _hb_language_t { +struct hb_language_impl_t { const char s[1]; }; @@ -160,7 +226,7 @@ struct hb_language_item_t { return *this; } - void finish (void) { free (lang); } + void finish (void) { free ((void *) lang); } }; @@ -168,6 +234,7 @@ struct hb_language_item_t { static hb_language_item_t *langs; +#ifdef HB_USE_ATEXIT static void free_langs (void) { @@ -178,6 +245,7 @@ void free_langs (void) langs = next; } } +#endif static hb_language_item_t * lang_find_or_insert (const char *key) @@ -197,11 +265,12 @@ retry: *lang = key; if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { + lang->finish (); free (lang); goto retry; } -#ifdef HAVE_ATEXIT +#ifdef HB_USE_ATEXIT if (!first_lang) atexit (free_langs); /* First person registers atexit() callback. */ #endif @@ -210,17 +279,32 @@ retry: } +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_language_t hb_language_from_string (const char *str, int len) { + char strbuf[64]; + if (!str || !len || !*str) return HB_LANGUAGE_INVALID; - char strbuf[32]; - if (len >= 0) { + if (len >= 0) + { + /* NUL-terminate it. */ len = MIN (len, (int) sizeof (strbuf) - 1); - str = (char *) memcpy (strbuf, str, len); + memcpy (strbuf, str, len); strbuf[len] = '\0'; + str = strbuf; } hb_language_item_t *item = lang_find_or_insert (str); @@ -228,6 +312,16 @@ hb_language_from_string (const char *str, int len) return likely (item) ? item->lang : HB_LANGUAGE_INVALID; } +/** + * hb_language_to_string: + * @language: + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ const char * hb_language_to_string (hb_language_t language) { @@ -235,20 +329,24 @@ hb_language_to_string (hb_language_t language) return language->s; } +/** + * hb_language_get_default: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_language_t hb_language_get_default (void) { - static hb_language_t default_language; - - if (!default_language) { - /* This block is not quite threadsafe, but is not as bad as - * it looks since it's idempotent. As long as pointer ops - * are atomic, we are safe. */ + static hb_language_t default_language = HB_LANGUAGE_INVALID; - /* I hear that setlocale() doesn't honor env vars on Windows, - * but for now we ignore that. */ - - default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); + 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; @@ -257,6 +355,16 @@ hb_language_get_default (void) /* hb_script_t */ +/** + * hb_script_from_iso15924_tag: + * @tag: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_script_t hb_script_from_iso15924_tag (hb_tag_t tag) { @@ -264,7 +372,7 @@ hb_script_from_iso15924_tag (hb_tag_t tag) return HB_SCRIPT_INVALID; /* Be lenient, adjust case (one capital letter followed by three small letters) */ - tag = (tag & 0xDFDFDFDF) | 0x00202020; + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; switch (tag) { @@ -284,25 +392,56 @@ hb_script_from_iso15924_tag (hb_tag_t tag) } /* If it looks right, just use the tag as a script */ - if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060) + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) return (hb_script_t) tag; /* Otherwise, return unknown */ return HB_SCRIPT_UNKNOWN; } +/** + * hb_script_from_string: + * @s: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_script_t hb_script_from_string (const char *s, int len) { return hb_script_from_iso15924_tag (hb_tag_from_string (s, len)); } +/** + * hb_script_to_iso15924_tag: + * @script: + * + * + * + * Return value: + * + * Since: 1.0 + **/ 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: 1.0 + **/ hb_direction_t hb_script_get_horizontal_direction (hb_script_t script) { @@ -346,6 +485,14 @@ hb_script_get_horizontal_direction (hb_script_t script) 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: + return HB_DIRECTION_RTL; } @@ -359,8 +506,7 @@ bool hb_user_data_array_t::set (hb_user_data_key_t *key, void * data, hb_destroy_func_t destroy, - hb_bool_t replace, - hb_mutex_t &lock) + hb_bool_t replace) { if (!key) return false; @@ -378,23 +524,26 @@ hb_user_data_array_t::set (hb_user_data_key_t *key, } void * -hb_user_data_array_t::get (hb_user_data_key_t *key, - hb_mutex_t &lock) +hb_user_data_array_t::get (hb_user_data_key_t *key) { hb_user_data_item_t item = {NULL }; return items.find (key, &item, lock) ? item.data : NULL; } -void -hb_user_data_array_t::finish (hb_mutex_t &lock) -{ - items.finish (lock); -} - /* 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: 1.0 + **/ void hb_version (unsigned int *major, unsigned int *minor, @@ -405,18 +554,37 @@ hb_version (unsigned int *major, *micro = HB_VERSION_MICRO; } +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: library version string. + * + * Since: 1.0 + **/ const char * hb_version_string (void) { return HB_VERSION_STRING; } +/** + * hb_version_atleast: + * @major: + * @minor: + * @micro: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t -hb_version_check (unsigned int major, - unsigned int minor, - unsigned int micro) +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) { - return HB_VERSION_CHECK (major, minor, micro); + return HB_VERSION_ATLEAST (major, minor, micro); } - - diff --git a/src/hb-common.h b/src/hb-common.h index 562b04c..b6ce3f7 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -33,6 +33,7 @@ #ifndef HB_COMMON_H #define HB_COMMON_H +#ifndef HB_BEGIN_DECLS # ifdef __cplusplus # define HB_BEGIN_DECLS extern "C" { # define HB_END_DECLS } @@ -40,8 +41,7 @@ # define HB_BEGIN_DECLS # define HB_END_DECLS # endif /* !__cplusplus */ - -HB_BEGIN_DECLS +#endif #if !defined (HB_DONT_DEFINE_STDINT) @@ -67,6 +67,8 @@ typedef unsigned __int64 uint64_t; #endif +HB_BEGIN_DECLS + typedef int hb_bool_t; @@ -88,13 +90,20 @@ typedef union _hb_var_int_t { typedef uint32_t hb_tag_t; -#define HB_TAG(a,b,c,d) ((hb_tag_t)((((uint8_t)(a))<<24)|(((uint8_t)(b))<<16)|(((uint8_t)(c))<<8)|((uint8_t)(d)))) +#define HB_TAG(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_tag_t hb_tag_from_string (const char *str, int len); +/* len=-1 means str is NUL-terminated. */ +hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +void +hb_tag_to_string (hb_tag_t tag, char *buf); /* hb_direction_t */ @@ -114,17 +123,18 @@ hb_direction_from_string (const char *str, int len); 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_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) -#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */ +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* hb_language_t */ -typedef struct _hb_language_t *hb_language_t; +typedef const struct hb_language_impl_t *hb_language_t; /* len=-1 means str is NUL-terminated */ hb_language_t @@ -139,178 +149,166 @@ hb_language_t hb_language_get_default (void); -/* hb_unicode_general_category_t */ - -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_script_t */ /* http://unicode.org/iso15924/ */ /* http://goo.gl/x9ilM */ +/* Unicode Character Database property: Script (sc) */ typedef enum { - /* Unicode-1.1 additions */ - HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), - HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), - HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), - HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), - HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), - HB_SCRIPT_CANADIAN_ABORIGINAL = HB_TAG ('C','a','n','s'), - HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), - HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), - HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), - HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), - HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), - HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), - HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), - HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), - HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), - HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), - HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), - HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), - HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), - HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), - HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), - HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), - HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), - HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), - HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), - HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), - HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), - HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), - HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), - HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), - HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), - HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), - HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), - - /* Unicode-2.0 additions */ - HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), - - /* Unicode-3.0 additions */ - HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), - HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), - HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), - HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), - HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), - - /* Unicode-3.1 additions */ - HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), - HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), - HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), - - /* Unicode-3.2 additions */ - HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), - HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), - HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), - HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), - - /* Unicode-4.0 additions */ - HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), - HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), - HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), - HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), - HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), - HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), - HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), - HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), - - /* Unicode-4.1 additions */ - HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), - HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), - HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), - HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), - HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), - HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), - HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), - - /* Unicode-5.0 additions */ - HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), - HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), - HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), - HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), - HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), - HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), - - /* Unicode-5.1 additions */ - HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), - HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), - HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), - HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), - HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), - HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), - HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), - HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), - HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), - HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), - HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), - - /* Unicode-5.2 additions */ - HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), - HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), - HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), - HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), - HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), - HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), - HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), - HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), - HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), - HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), - HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), - HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), - - /* Unicode-6.0 additions */ - HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), - HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), - HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), - - /* Unicode-6.1 additions */ - HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), - HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), - HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), - HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), - HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), - HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), - HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), - - /* No script set */ - HB_SCRIPT_INVALID = HB_TAG_NONE + /*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'), + + /*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'), + + /* 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; @@ -319,7 +317,7 @@ typedef enum hb_script_t hb_script_from_iso15924_tag (hb_tag_t tag); -/* suger for tag_from_string() then script_from_iso15924_tag */ +/* sugar for tag_from_string() then script_from_iso15924_tag */ /* len=-1 means s is NUL-terminated */ hb_script_t hb_script_from_string (const char *s, int len); @@ -333,7 +331,7 @@ hb_script_get_horizontal_direction (hb_script_t script); /* User data */ -typedef struct _hb_user_data_key_t { +typedef struct hb_user_data_key_t { /*< private >*/ char unused; } hb_user_data_key_t; diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc new file mode 100644 index 0000000..4a45175 --- /dev/null +++ b/src/hb-coretext.cc @@ -0,0 +1,1194 @@ +/* + * 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 +#define hb_coretext_shaper_face_data_t CGFont +#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 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); +} + +hb_coretext_shaper_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + hb_coretext_shaper_face_data_t *data = NULL; + + if (face->destroy == (hb_destroy_func_t) CGFontRelease) + { + data = 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)) + { + data = CGFontCreateWithDataProvider (provider); + CGDataProviderRelease (provider); + } + } + + if (unlikely (!data)) { + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + } + + return data; +} + +void +_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) +{ + CFRelease (data); +} + +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; +} + + +/* + * shaper font data + */ + +struct hb_coretext_shaper_font_data_t { + CTFontRef ct_font; + CGFloat x_mult, y_mult; /* From CT space to HB space. */ +}; + +hb_coretext_shaper_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font) +{ + if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; + + hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); + if (unlikely (!data)) + return NULL; + + hb_face_t *face = font->face; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + + /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */ + CGFloat font_size = 36.; /* 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 = (CGFloat) font->x_scale / font_size; + data->y_mult = (CGFloat) font->y_scale / font_size; + data->ct_font = CTFontCreateWithGraphicsFont (face_data, font_size, NULL, NULL); + if (unlikely (!data->ct_font)) { + DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); + free (data); + return NULL; + } + + return data; +} + +void +_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) +{ + CFRelease (data->ct_font); + free (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) +{ + 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) +{ + if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL; + hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + return font_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); + hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + + /* 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... */ + { + 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 (font_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) & ((1 << 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; + } +retry: + { + 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, font_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; + + 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, font_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 + * begin with. + * + * Webkit uses a slightly different approach: it installs LastResort + * as fallback chain, and then checks PS name of used font against + * LastResort. That one is safe for any font except for LastResort, + * as opposed to ours, which can fail if we are using any uninstalled + * font that has the same name as an installed font. + * + * See: http://github.com/behdad/harfbuzz/pull/36 + */ + 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); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (font_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.u32 = x_offset; + info->var2.u32 = 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; + CGFloat x_mult = font_data->x_mult, y_mult = font_data->y_mult; + 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.u32 = x_offset; + info->var2.u32 = 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.u32 = positions[j].x * x_mult; + info->var2.u32 = 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; + } + + /* 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.u32; + pos->y_offset = info->var2.u32; + info++, pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.u32; + pos->y_offset = info->var2.u32; + 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 + */ + +HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, face) +HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, font) + + +/* + * 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) +{ + 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/src/hb-coretext.h b/src/hb-coretext.h new file mode 100644 index 0000000..25267bc --- /dev/null +++ b/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_face_t * +hb_coretext_face_create (CGFontRef cg_font); + + +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/src/hb-version.h b/src/hb-deprecated.h index 43ec9cf..30ae4b1 100644 --- a/src/hb-version.h +++ b/src/hb-deprecated.h @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -28,39 +28,24 @@ #error "Include <hb.h> instead." #endif -#ifndef HB_VERSION_H -#define HB_VERSION_H +#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_VERSION_MAJOR 0 -#define HB_VERSION_MINOR 9 -#define HB_VERSION_MICRO 0 +#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS -#define HB_VERSION_STRING "0.9.0" - -#define HB_VERSION_CHECK(major,minor,micro) \ - ((major)*10000+(minor)*100+(micro) >= \ - HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) - - -void -hb_version (unsigned int *major, - unsigned int *minor, - unsigned int *micro); - -const char * -hb_version_string (void); - -hb_bool_t -hb_version_check (unsigned int major, - unsigned int minor, - unsigned int micro); +#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT +#endif HB_END_DECLS -#endif /* HB_VERSION_H */ +#endif /* HB_DEPRECATED_H */ diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh new file mode 100644 index 0000000..c4266ff --- /dev/null +++ b/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/src/hb-face.cc b/src/hb-face.cc new file mode 100644 index 0000000..9348af7 --- /dev/null +++ b/src/hb-face.cc @@ -0,0 +1,481 @@ +/* + * 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 "hb-cache-private.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: 1.0 + **/ +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 *) malloc (sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return NULL; + + closure->blob = blob; + closure->index = index; + + return closure; +} + +static void +_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) +{ + hb_blob_destroy (closure->blob); + free (closure); +} + +static hb_blob_t * +_hb_face_for_data_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: 1.0 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob || !hb_blob_get_length (blob))) + return hb_face_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: 1.0 + **/ +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: 1.0 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @face: a face. + * + * + * + * Since: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +unsigned int +hb_face_get_index (hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_upem: + * @face: a face. + * @upem: + * + * + * + * Since: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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/src/hb-face.h b/src/hb-face.h new file mode 100644 index 0000000..f682c46 --- /dev/null +++ b/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_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_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 * +hb_face_get_empty (void); + +hb_face_t * +hb_face_reference (hb_face_t *face); + +void +hb_face_destroy (hb_face_t *face); + +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); + + +void * +hb_face_get_user_data (hb_face_t *face, + hb_user_data_key_t *key); + +void +hb_face_make_immutable (hb_face_t *face); + +hb_bool_t +hb_face_is_immutable (hb_face_t *face); + + +hb_blob_t * +hb_face_reference_table (hb_face_t *face, + hb_tag_t tag); + +hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +unsigned int +hb_face_get_index (hb_face_t *face); + +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + +unsigned int +hb_face_get_upem (hb_face_t *face); + +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count); + +unsigned int +hb_face_get_glyph_count (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_FACE_H */ diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc index 5939887..9d061a9 100644 --- a/src/hb-fallback-shape.cc +++ b/src/hb-fallback-shape.cc @@ -24,37 +24,117 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-fallback-shape-private.hh" +#define HB_SHAPER fallback +#include "hb-shaper-impl-private.hh" -#include "hb-buffer-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) +{ + 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_font_t *font, +_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) { - buffer->guess_properties (); + /* 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. + */ - unsigned int count = buffer->len; - - for (unsigned int i = 0; i < count; i++) - hb_font_get_glyph (font, buffer->info[i].codepoint, 0, &buffer->info[i].codepoint); + hb_codepoint_t space; + bool has_space = font->get_glyph (' ', 0, &space); buffer->clear_positions (); - for (unsigned int i = 0; i < count; i++) { - hb_font_get_glyph_advance_for_direction (font, buffer->info[i].codepoint, - buffer->props.direction, - &buffer->pos[i].x_advance, - &buffer->pos[i].y_advance); - hb_font_subtract_glyph_origin_for_direction (font, buffer->info[i].codepoint, - buffer->props.direction, - &buffer->pos[i].x_offset, - &buffer->pos[i].y_offset); + 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_glyph (info[i].codepoint, 0, &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 (buffer->props.direction)) + if (HB_DIRECTION_IS_BACKWARD (direction)) hb_buffer_reverse (buffer); return true; diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh index 91a4304..33bbf71 100644 --- a/src/hb-font-private.hh +++ b/src/hb-font-private.hh @@ -31,8 +31,9 @@ #include "hb-private.hh" -#include "hb-font.h" #include "hb-object-private.hh" +#include "hb-face-private.hh" +#include "hb-shaper-private.hh" @@ -54,7 +55,7 @@ HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ /* ^--- Add new callbacks here */ -struct _hb_font_funcs_t { +struct hb_font_funcs_t { hb_object_header_t header; ASSERT_POD (); @@ -82,32 +83,12 @@ struct _hb_font_funcs_t { }; -/* - * 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; - void *user_data; - hb_destroy_func_t destroy; - - struct hb_ot_layout_t *ot_layout; - - unsigned int index; - unsigned int upem; -}; - /* * hb_font_t */ -struct _hb_font_t { +struct hb_font_t { hb_object_header_t header; ASSERT_POD (); @@ -126,6 +107,8 @@ struct _hb_font_t { void *user_data; hb_destroy_func_t destroy; + struct hb_shaper_data_t shaper_data; + /* Convert from font-space to user-space */ inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); } @@ -134,12 +117,12 @@ struct _hb_font_t { /* 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 v * (int64_t) this->x_scale / this->parent->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 v * (int64_t) this->y_scale / this->parent->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) { @@ -159,10 +142,274 @@ struct _hb_font_t { } + /* Public getters */ + + inline hb_bool_t has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_glyph (unicode, 0, &glyph); + } + + inline hb_bool_t get_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.glyph (this, user_data, + unicode, variation_selector, glyph, + klass->user_data.glyph); + } + + inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.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.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.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.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.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.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.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.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.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.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_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); + } + } + + /* Internal only */ + 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 use font_metics.ascent */ + *y = y_scale; + } + + 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))) + { + 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; + } + } + else + { + 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 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_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_glyph (unichar, 0, glyph)) + return true; + } + + return false; + } + private: - inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); } + inline hb_position_t em_scale (int16_t v, int scale) { return (hb_position_t) (v * (int64_t) 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/src/hb-font.cc b/src/hb-font.cc index 109caff..d42db59 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #include "hb-private.hh" @@ -29,16 +31,15 @@ #include "hb-ot-layout-private.hh" #include "hb-font-private.hh" -#include "hb-blob.h" #include "hb-open-file-private.hh" #include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" #include "hb-cache-private.hh" #include <string.h> - /* * hb_font_funcs_t */ @@ -52,7 +53,7 @@ hb_font_get_glyph_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph); + return font->parent->get_glyph (unicode, variation_selector, glyph); *glyph = 0; return false; @@ -65,7 +66,7 @@ hb_font_get_glyph_h_advance_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph)); + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); return font->x_scale; } @@ -77,7 +78,7 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph)); + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); return font->y_scale; } @@ -91,7 +92,7 @@ hb_font_get_glyph_h_origin_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, glyph, x, y); + hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; @@ -110,7 +111,7 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, glyph, x, y); + hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; @@ -128,7 +129,7 @@ hb_font_get_glyph_h_kerning_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph)); + return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); return 0; } @@ -141,7 +142,7 @@ hb_font_get_glyph_v_kerning_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph)); + return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); return 0; } @@ -154,9 +155,7 @@ hb_font_get_glyph_extents_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_extents (font->parent, - glyph, - extents); + 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); @@ -178,7 +177,7 @@ hb_font_get_glyph_contour_point_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, glyph, point_index, x, y); + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); if (ret) font->parent_scale_position (x, y); return ret; @@ -196,9 +195,9 @@ hb_font_get_glyph_name_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return hb_font_get_glyph_name (font->parent, glyph, name, size); + return font->parent->get_glyph_name (glyph, name, size); - snprintf (name, size, "gid%u", glyph); + if (size) *name = '\0'; return false; } @@ -210,7 +209,7 @@ hb_font_get_glyph_from_name_nil (hb_font_t *font, void *user_data HB_UNUSED) { if (font->parent) - return hb_font_get_glyph_from_name (font->parent, name, len, glyph); + return font->parent->get_glyph_from_name (name, len, glyph); *glyph = 0; return false; @@ -230,6 +229,15 @@ static const hb_font_funcs_t _hb_font_funcs_nil = { }; +/** + * hb_font_funcs_create: (Xconstructor) + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_font_funcs_t * hb_font_funcs_create (void) { @@ -243,18 +251,45 @@ hb_font_funcs_create (void) return ffuncs; } +/** + * hb_font_funcs_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_font_funcs_t * hb_font_funcs_get_empty (void) { return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil); } +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 1.0 + **/ 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: 1.0 + **/ void hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) { @@ -268,6 +303,20 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) free (ffuncs); } +/** + * hb_font_funcs_set_user_data: (skip) + * @ffuncs: font functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key, @@ -278,6 +327,17 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, 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: 1.0 + **/ void * hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key) @@ -286,15 +346,33 @@ hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, } +/** + * hb_font_funcs_make_immutable: + * @ffuncs: font functions. + * + * + * + * Since: 1.0 + **/ void hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) { - if (hb_object_is_inert (ffuncs)) + if (unlikely (hb_object_is_inert (ffuncs))) return; ffuncs->immutable = true; } +/** + * hb_font_funcs_is_immutable: + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) { @@ -334,476 +412,424 @@ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT +/* Public getters */ + +/** + * hb_font_get_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_get_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph) { - *glyph = 0; - return font->klass->get.glyph (font, font->user_data, - unicode, variation_selector, glyph, - font->klass->user_data.glyph); + return font->get_glyph (unicode, variation_selector, glyph); } +/** + * hb_font_get_glyph_h_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_position_t hb_font_get_glyph_h_advance (hb_font_t *font, hb_codepoint_t glyph) { - return font->klass->get.glyph_h_advance (font, font->user_data, - glyph, - font->klass->user_data.glyph_h_advance); + return font->get_glyph_h_advance (glyph); } +/** + * hb_font_get_glyph_v_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_position_t hb_font_get_glyph_v_advance (hb_font_t *font, hb_codepoint_t glyph) { - return font->klass->get.glyph_v_advance (font, font->user_data, - glyph, - font->klass->user_data.glyph_v_advance); + return font->get_glyph_v_advance (glyph); } +/** + * hb_font_get_glyph_h_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ 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) { - *x = *y = 0; - return font->klass->get.glyph_h_origin (font, font->user_data, - glyph, x, y, - font->klass->user_data.glyph_h_origin); + 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: 1.0 + **/ 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) { - *x = *y = 0; - return font->klass->get.glyph_v_origin (font, font->user_data, - glyph, x, y, - font->klass->user_data.glyph_v_origin); + 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: 1.0 + **/ 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->klass->get.glyph_h_kerning (font, font->user_data, - left_glyph, right_glyph, - font->klass->user_data.glyph_h_kerning); + 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: 1.0 + **/ hb_position_t hb_font_get_glyph_v_kerning (hb_font_t *font, - hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) { - return font->klass->get.glyph_v_kerning (font, font->user_data, - left_glyph, right_glyph, - font->klass->user_data.glyph_v_kerning); + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); } +/** + * hb_font_get_glyph_extents: + * @font: a font. + * @glyph: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_get_glyph_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); - return font->klass->get.glyph_extents (font, font->user_data, - glyph, - extents, - font->klass->user_data.glyph_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: 1.0 + **/ 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) { - *x = *y = 0; - return font->klass->get.glyph_contour_point (font, font->user_data, - glyph, point_index, - x, y, - font->klass->user_data.glyph_contour_point); + 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: 1.0 + **/ hb_bool_t hb_font_get_glyph_name (hb_font_t *font, hb_codepoint_t glyph, char *name, unsigned int size) { - return font->klass->get.glyph_name (font, font->user_data, - glyph, - name, size, - font->klass->user_data.glyph_name); + 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: 1.0 + **/ hb_bool_t hb_font_get_glyph_from_name (hb_font_t *font, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph) { - return font->klass->get.glyph_from_name (font, font->user_data, - name, len, - glyph, - font->klass->user_data.glyph_from_name); + return font->get_glyph_from_name (name, len, glyph); } /* A bit higher-level, and with fallback */ +/** + * hb_font_get_glyph_advance_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 1.0 + **/ 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) { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = hb_font_get_glyph_h_advance (font, glyph); - *y = 0; - } else { - *x = 0; - *y = hb_font_get_glyph_v_advance (font, glyph); - } + return font->get_glyph_advance_for_direction (glyph, direction, x, y); } -static void -guess_v_origin_minus_h_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) -{ - *x = hb_font_get_glyph_h_advance (font, glyph) / 2; - - /* TODO use font_metics.ascent */ - *y = font->y_scale; -} - - +/** + * hb_font_get_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 1.0 + **/ 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) { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - hb_bool_t ret = hb_font_get_glyph_h_origin (font, glyph, x, y); - if (!ret && (ret = hb_font_get_glyph_v_origin (font, glyph, x, y))) { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (font, glyph, &dx, &dy); - *x -= dx; *y -= dy; - } - } else { - hb_bool_t ret = hb_font_get_glyph_v_origin (font, glyph, x, y); - if (!ret && (ret = hb_font_get_glyph_h_origin (font, glyph, x, y))) { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (font, glyph, &dx, &dy); - *x += dx; *y += dy; - } - } + 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: 1.0 + **/ 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_position_t origin_x, origin_y; - - hb_font_get_glyph_origin_for_direction (font, glyph, direction, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_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: 1.0 + **/ 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_position_t origin_x, origin_y; - - hb_font_get_glyph_origin_for_direction (font, glyph, direction, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_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: 1.0 + **/ 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) { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = hb_font_get_glyph_h_kerning (font, first_glyph, second_glyph); - *y = 0; - } else { - *x = 0; - *y = hb_font_get_glyph_v_kerning (font, first_glyph, second_glyph); - } + 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: 1.0 + **/ 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_bool_t ret = hb_font_get_glyph_extents (font, glyph, extents); - - if (ret) - hb_font_subtract_glyph_origin_for_direction (font, glyph, direction, &extents->x_bearing, &extents->y_bearing); - - return ret; + 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: 1.0 + **/ 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) { - hb_bool_t ret = hb_font_get_glyph_contour_point (font, glyph, point_index, x, y); - - if (ret) - hb_font_subtract_glyph_origin_for_direction (font, glyph, direction, x, y); - - return ret; -} - - -/* - * hb_face_t - */ - -static const hb_face_t _hb_face_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - NULL, /* reference_table */ - NULL, /* user_data */ - NULL, /* destroy */ - - NULL, /* ot_layout */ - - 0, /* index */ - 1000 /* upem */ -}; - - -hb_face_t * -hb_face_create_for_tables (hb_reference_table_func_t reference_table, - void *user_data, - hb_destroy_func_t destroy) -{ - hb_face_t *face; - - if (!reference_table || !(face = hb_object_create<hb_face_t> ())) { - if (destroy) - destroy (user_data); - return hb_face_get_empty (); - } - - face->reference_table = reference_table; - face->user_data = user_data; - face->destroy = destroy; - - face->ot_layout = _hb_ot_layout_create (face); - - face->upem = 0; - - return face; -} - - -typedef struct _hb_face_for_data_closure_t { - hb_blob_t *blob; - unsigned int index; -} hb_face_for_data_closure_t; - -static hb_face_for_data_closure_t * -_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) -{ - hb_face_for_data_closure_t *closure; - - closure = (hb_face_for_data_closure_t *) malloc (sizeof (hb_face_for_data_closure_t)); - if (unlikely (!closure)) - return NULL; - - closure->blob = blob; - closure->index = index; - - return closure; -} - -static void -_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) -{ - hb_blob_destroy (closure->blob); - free (closure); -} - -static hb_blob_t * -_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) -{ - hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; - - if (tag == HB_TAG_NONE) - return hb_blob_reference (data->blob); - - const OpenTypeFontFile &ot_file = *Sanitizer<OpenTypeFontFile>::lock_instance (data->blob); - const OpenTypeFontFace &ot_face = ot_file.get_face (data->index); - - const OpenTypeTable &table = ot_face.get_table_by_tag (tag); - - hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length); - - return blob; -} - -hb_face_t * -hb_face_create (hb_blob_t *blob, - unsigned int index) -{ - hb_face_t *face; - - if (unlikely (!blob || !hb_blob_get_length (blob))) - return hb_face_get_empty (); - - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index); - - if (unlikely (!closure)) - return hb_face_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_t * -hb_face_get_empty (void) -{ - return const_cast<hb_face_t *> (&_hb_face_nil); -} - - -hb_face_t * -hb_face_reference (hb_face_t *face) -{ - return hb_object_reference (face); -} - -void -hb_face_destroy (hb_face_t *face) -{ - if (!hb_object_destroy (face)) return; - - _hb_ot_layout_destroy (face->ot_layout); - - if (face->destroy) - face->destroy (face->user_data); - - free (face); -} - -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); -} - -void * -hb_face_get_user_data (hb_face_t *face, - hb_user_data_key_t *key) -{ - return hb_object_get_user_data (face, key); + 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: 1.0 + **/ void -hb_face_make_immutable (hb_face_t *face) +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size) { - if (hb_object_is_inert (face)) - return; - - face->immutable = true; + 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: 1.0 + **/ hb_bool_t -hb_face_is_immutable (hb_face_t *face) +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) { - return face->immutable; -} - - -hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag) -{ - hb_blob_t *blob; - - if (unlikely (!face || !face->reference_table)) - return hb_blob_get_empty (); - - blob = face->reference_table (face, tag, face->user_data); - if (unlikely (!blob)) - return hb_blob_get_empty (); - - return blob; -} - -hb_blob_t * -hb_face_reference_blob (hb_face_t *face) -{ - return hb_face_reference_table (face, HB_TAG_NONE); -} - -void -hb_face_set_index (hb_face_t *face, - unsigned int index) -{ - if (hb_object_is_inert (face)) - return; - - face->index = index; -} - -unsigned int -hb_face_get_index (hb_face_t *face) -{ - return face->index; -} - -void -hb_face_set_upem (hb_face_t *face, - unsigned int upem) -{ - if (hb_object_is_inert (face)) - return; - - face->upem = upem; -} - -unsigned int -hb_face_get_upem (hb_face_t *face) -{ - if (unlikely (!face->upem)) { - hb_blob_t *head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head)); - const head *head_table = Sanitizer<head>::lock_instance (head_blob); - face->upem = head_table->get_upem (); - hb_blob_destroy (head_blob); - } - return face->upem; + return font->glyph_from_string (s, len, glyph); } @@ -811,6 +837,16 @@ hb_face_get_upem (hb_face_t *face) * hb_font_t */ +/** + * hb_font_create: (Xconstructor) + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_font_t * hb_font_create (hb_face_t *face) { @@ -830,6 +866,16 @@ hb_font_create (hb_face_t *face) return font; } +/** + * hb_font_create_sub_font: + * @parent: parent font. + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_font_t * hb_font_create_sub_font (hb_font_t *parent) { @@ -852,6 +898,15 @@ hb_font_create_sub_font (hb_font_t *parent) return font; } +/** + * hb_font_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 1.0 + **/ hb_font_t * hb_font_get_empty (void) { @@ -871,32 +926,75 @@ hb_font_get_empty (void) const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */ NULL, /* user_data */ - NULL /* destroy */ + 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: 1.0 + **/ hb_font_t * hb_font_reference (hb_font_t *font) { return hb_object_reference (font); } +/** + * hb_font_destroy: (skip) + * @font: a font. + * + * + * + * Since: 1.0 + **/ 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); - if (font->destroy) - font->destroy (font->user_data); free (font); } +/** + * hb_font_set_user_data: (skip) + * @font: a font. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_set_user_data (hb_font_t *font, hb_user_data_key_t *key, @@ -907,6 +1005,17 @@ hb_font_set_user_data (hb_font_t *font, 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: 1.0 + **/ void * hb_font_get_user_data (hb_font_t *font, hb_user_data_key_t *key) @@ -914,27 +1023,65 @@ hb_font_get_user_data (hb_font_t *font, return hb_object_get_user_data (font, key); } +/** + * hb_font_make_immutable: + * @font: a font. + * + * + * + * Since: 1.0 + **/ void hb_font_make_immutable (hb_font_t *font) { - if (hb_object_is_inert (font)) + if (unlikely (hb_object_is_inert (font))) return; font->immutable = true; } +/** + * hb_font_is_immutable: + * @font: a font. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_font_is_immutable (hb_font_t *font) { return font->immutable; } +/** + * hb_font_get_parent: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ 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: 1.0 + **/ hb_face_t * hb_font_get_face (hb_font_t *font) { @@ -942,15 +1089,26 @@ hb_font_get_face (hb_font_t *font) } +/** + * hb_font_set_funcs: + * @font: a font. + * @klass: (closure font_data) (destroy destroy) (scope notified): + * @font_data: + * @destroy: + * + * + * + * Since: 1.0 + **/ void hb_font_set_funcs (hb_font_t *font, hb_font_funcs_t *klass, - void *user_data, + void *font_data, hb_destroy_func_t destroy) { if (font->immutable) { if (destroy) - destroy (user_data); + destroy (font_data); return; } @@ -963,30 +1121,50 @@ hb_font_set_funcs (hb_font_t *font, hb_font_funcs_reference (klass); hb_font_funcs_destroy (font->klass); font->klass = klass; - font->user_data = user_data; + font->user_data = font_data; font->destroy = destroy; } +/** + * hb_font_set_funcs_data: + * @font: a font. + * @font_data: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Since: 1.0 + **/ void hb_font_set_funcs_data (hb_font_t *font, - void *user_data, + void *font_data, hb_destroy_func_t destroy) { /* Destroy user_data? */ if (font->immutable) { if (destroy) - destroy (user_data); + destroy (font_data); return; } if (font->destroy) font->destroy (font->user_data); - font->user_data = user_data; + font->user_data = font_data; font->destroy = destroy; } +/** + * hb_font_set_scale: + * @font: a font. + * @x_scale: + * @y_scale: + * + * + * + * Since: 1.0 + **/ void hb_font_set_scale (hb_font_t *font, int x_scale, @@ -999,6 +1177,16 @@ hb_font_set_scale (hb_font_t *font, font->y_scale = y_scale; } +/** + * hb_font_get_scale: + * @font: a font. + * @x_scale: (out): + * @y_scale: (out): + * + * + * + * Since: 1.0 + **/ void hb_font_get_scale (hb_font_t *font, int *x_scale, @@ -1008,6 +1196,16 @@ hb_font_get_scale (hb_font_t *font, if (y_scale) *y_scale = font->y_scale; } +/** + * hb_font_set_ppem: + * @font: a font. + * @x_ppem: + * @y_ppem: + * + * + * + * Since: 1.0 + **/ void hb_font_set_ppem (hb_font_t *font, unsigned int x_ppem, @@ -1020,6 +1218,16 @@ hb_font_set_ppem (hb_font_t *font, font->y_ppem = y_ppem; } +/** + * hb_font_get_ppem: + * @font: a font. + * @x_ppem: (out): + * @y_ppem: (out): + * + * + * + * Since: 1.0 + **/ void hb_font_get_ppem (hb_font_t *font, unsigned int *x_ppem, @@ -1028,5 +1236,3 @@ hb_font_get_ppem (hb_font_t *font, if (x_ppem) *x_ppem = font->x_ppem; if (y_ppem) *y_ppem = font->y_ppem; } - - diff --git a/src/hb-font.h b/src/hb-font.h index b98759b..7273db4 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -32,85 +32,19 @@ #define HB_FONT_H #include "hb-common.h" -#include "hb-blob.h" +#include "hb-face.h" HB_BEGIN_DECLS -typedef struct _hb_face_t hb_face_t; -typedef struct _hb_font_t hb_font_t; - -/* - * hb_face_t - */ - -hb_face_t * -hb_face_create (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_face_t * -hb_face_create_for_tables (hb_reference_table_func_t reference_table, - void *user_data, - hb_destroy_func_t destroy); - -hb_face_t * -hb_face_get_empty (void); - -hb_face_t * -hb_face_reference (hb_face_t *face); - -void -hb_face_destroy (hb_face_t *face); - -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); - - -void * -hb_face_get_user_data (hb_face_t *face, - hb_user_data_key_t *key); - -void -hb_face_make_immutable (hb_face_t *face); - -hb_bool_t -hb_face_is_immutable (hb_face_t *face); - - -hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag); - -hb_blob_t * -hb_face_reference_blob (hb_face_t *face); - -void -hb_face_set_index (hb_face_t *face, - unsigned int index); - -unsigned int -hb_face_get_index (hb_face_t *face); - -void -hb_face_set_upem (hb_face_t *face, - unsigned int upem); - -unsigned int -hb_face_get_upem (hb_face_t *face); +typedef struct hb_font_t hb_font_t; /* * hb_font_funcs_t */ -typedef struct _hb_font_funcs_t hb_font_funcs_t; +typedef struct hb_font_funcs_t hb_font_funcs_t; hb_font_funcs_t * hb_font_funcs_create (void); @@ -143,9 +77,10 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); -/* funcs */ -typedef struct _hb_glyph_extents_t +/* glyph extents */ + +typedef struct hb_glyph_extents_t { hb_position_t x_bearing; hb_position_t y_bearing; @@ -204,54 +139,180 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * /* func setters */ +/** + * hb_font_funcs_set_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.0 + **/ void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_func_t glyph_func, + hb_font_get_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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ void hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_name_func_t glyph_func, + 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: 1.0 + **/ void hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_from_name_func_t glyph_func, + hb_font_get_glyph_from_name_func_t func, void *user_data, hb_destroy_func_t destroy); @@ -346,6 +407,17 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, hb_direction_t direction, hb_position_t *x, hb_position_t *y); +/* Generates gidDDD if glyph has no name. */ +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_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 diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 0589c9e..322f93a 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -51,16 +51,16 @@ * In particular, FT_Get_Advance() without the NO_HINTING flag seems to be * buggy. * - * - We don't handle / allow for emboldening / obliqueing. - * - * - Rounding, etc? - * - * - In the future, we should add constructors to create fonts in font space. + * 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 around. This seems to work best, until we go ahead and add a full + * load_flags API. * - * - I believe transforms are not correctly implemented. FreeType does not - * provide any API to get to the transform/delta set on the face. :( + * - We don't handle / allow for emboldening / obliqueing. * - * - Always use FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH? + * - In the future, we should add constructors to create fonts in font space? * * - FT_Load_Glyph() is exteremely costly. Do something about it? */ @@ -77,13 +77,10 @@ hb_ft_get_glyph (hb_font_t *font HB_UNUSED, { FT_Face ft_face = (FT_Face) font_data; -#ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX if (unlikely (variation_selector)) { *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector); - if (*glyph) - return true; + return *glyph != 0; } -#endif *glyph = FT_Get_Char_Index (ft_face, unicode); return *glyph != 0; @@ -102,7 +99,10 @@ hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED, if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v))) return 0; - return v >> 10; + if (font->x_scale < 0) + v = -v; + + return (v + (1<<9)) >> 10; } static hb_position_t @@ -118,9 +118,12 @@ hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED, if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &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 >> 10; + return (-v + (1<<9)) >> 10; } static hb_bool_t @@ -144,7 +147,7 @@ hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { FT_Face ft_face = (FT_Face) font_data; - int load_flags = FT_LOAD_DEFAULT; + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) return false; @@ -154,11 +157,16 @@ hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, *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 HB_UNUSED, +hb_ft_get_glyph_h_kerning (hb_font_t *font, void *font_data, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, @@ -167,7 +175,8 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font HB_UNUSED, FT_Face ft_face = (FT_Face) font_data; FT_Vector kerningv; - if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, FT_KERNING_DEFAULT, &kerningv)) + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, mode, &kerningv)) return 0; return kerningv.x; @@ -192,7 +201,7 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { FT_Face ft_face = (FT_Face) font_data; - int load_flags = FT_LOAD_DEFAULT; + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) return false; @@ -200,7 +209,7 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, 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; + extents->height = -ft_face->glyph->metrics.height; return true; } @@ -232,7 +241,7 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, } static hb_bool_t -hb_ft_get_glyph_name (hb_font_t *font, +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, void *font_data, hb_codepoint_t glyph, char *name, unsigned int size, @@ -241,14 +250,14 @@ hb_ft_get_glyph_name (hb_font_t *font, FT_Face ft_face = (FT_Face) font_data; hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); - if (!ret) - snprintf (name, size, "gid%u", glyph); + if (ret && (size && !*name)) + ret = false; return ret; } static hb_bool_t -hb_ft_get_glyph_from_name (hb_font_t *font, +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, @@ -267,6 +276,15 @@ hb_ft_get_glyph_from_name (hb_font_t *font, *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; } @@ -317,7 +335,16 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) buffer, free); } - +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 1.0 + **/ hb_face_t * hb_ft_face_create (FT_Face ft_face, hb_destroy_func_t destroy) @@ -329,11 +356,7 @@ hb_ft_face_create (FT_Face ft_face, blob = hb_blob_create ((const char *) ft_face->stream->base, (unsigned int) ft_face->stream->size, - /* TODO: We assume that it's mmap()'ed, but FreeType code - * suggests that there are cases we reach here but font is - * not mmapped. For example, when mmap() fails. No idea - * how to deal with it better here. */ - HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, + HB_MEMORY_MODE_READONLY, ft_face, destroy); face = hb_face_create (blob, ft_face->face_index); hb_blob_destroy (blob); @@ -347,12 +370,37 @@ hb_ft_face_create (FT_Face ft_face, return face; } +/** + * hb_ft_face_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 1.0 + **/ +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) FT_Done_Face); +} + 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: 1.0 + **/ hb_face_t * hb_ft_face_create_cached (FT_Face ft_face) { @@ -374,6 +422,16 @@ _do_nothing (void) } +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 1.0 + **/ hb_font_t * hb_ft_font_create (FT_Face ft_face, hb_destroy_func_t destroy) @@ -388,25 +446,45 @@ hb_ft_font_create (FT_Face ft_face, _hb_ft_get_font_funcs (), ft_face, (hb_destroy_func_t) _do_nothing); hb_font_set_scale (font, - ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM) >> 16, - ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM) >> 16); + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<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 return font; } +/** + * hb_ft_font_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 1.0 + **/ +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) FT_Done_Face); +} + /* 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) @@ -425,7 +503,7 @@ retry: goto retry; } -#ifdef HAVE_ATEXIT +#ifdef HB_USE_ATEXIT atexit (free_ft_library); /* First person registers atexit() callback. */ #endif } @@ -464,9 +542,18 @@ hb_ft_font_set_funcs (hb_font_t *font) FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); FT_Set_Char_Size (ft_face, - font->x_scale, font->y_scale, + 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; diff --git a/src/hb-ft.h b/src/hb-ft.h index 696251e..92f4b36 100644 --- a/src/hb-ft.h +++ b/src/hb-ft.h @@ -34,19 +34,76 @@ HB_BEGIN_DECLS -/* Note: FreeType is not thread-safe. Hence, these functions are not either. */ +/* + * 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_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_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_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_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_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); /* Makes an hb_font_t use FreeType internally to implement font functions. */ diff --git a/src/hb-glib.cc b/src/hb-glib.cc index 6b655dd..61dff5e 100644 --- a/src/hb-glib.cc +++ b/src/hb-glib.cc @@ -77,7 +77,7 @@ glib_script_to_script[] = HB_SCRIPT_THAANA, HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, - HB_SCRIPT_CANADIAN_ABORIGINAL, + HB_SCRIPT_CANADIAN_SYLLABICS, HB_SCRIPT_YI, HB_SCRIPT_TAGALOG, HB_SCRIPT_HANUNOO, @@ -192,13 +192,13 @@ hb_glib_script_from_script (hb_script_t script) } -static unsigned int +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 g_unichar_combining_class (unicode); + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); } static unsigned int @@ -250,12 +250,9 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, /* We don't ifdef-out the fallback code such that compiler always * sees it and makes sure it's compilable. */ - if (!a || !b) - return false; - gchar utf8[12]; gchar *normalized; - gint len; + int len; hb_bool_t ret; len = g_unichar_to_utf8 (a, utf8); @@ -292,7 +289,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, gchar utf8[6]; gchar *normalized; - gint len; + int len; hb_bool_t ret; len = g_unichar_to_utf8 (ab, utf8); @@ -336,23 +333,63 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, 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 -extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs; -const hb_unicode_funcs_t _hb_glib_unicode_funcs = { - HB_OBJECT_HEADER_STATIC, + /* 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; - NULL, /* parent */ - true, /* immutable */ - { -#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name, - HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_UNICODE_FUNC_IMPLEMENT - } -}; + /* 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); } +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); +} diff --git a/src/hb-glib.h b/src/hb-glib.h index 63a9d33..1a8f42e 100644 --- a/src/hb-glib.h +++ b/src/hb-glib.h @@ -46,6 +46,9 @@ hb_glib_script_from_script (hb_script_t script); hb_unicode_funcs_t * hb_glib_get_unicode_funcs (void); +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); + HB_END_DECLS diff --git a/src/hb-gobject-enums.cc.tmpl b/src/hb-gobject-enums.cc.tmpl index 05abd89..ca458a3 100644 --- a/src/hb-gobject-enums.cc.tmpl +++ b/src/hb-gobject-enums.cc.tmpl @@ -45,13 +45,12 @@ /*** END file-production ***/ /*** BEGIN value-header ***/ -inline static /* TODO(behdad) disable these for now until we fix them... */ GType @enum_name@_get_type (void) { - static volatile gsize g_define_type_id__volatile = 0; + static gsize type_id = 0; - if (g_once_init_enter (&g_define_type_id__volatile)) + if (g_once_init_enter (&type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ @@ -63,12 +62,12 @@ GType /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; - GType g_define_type_id = + GType id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); - g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + g_once_init_leave (&type_id, id); } - return g_define_type_id__volatile; + return type_id; } /*** END value-tail ***/ diff --git a/src/indic.cc b/src/hb-gobject-enums.h.tmpl index 3b44076..6ecda06 100644 --- a/src/indic.cc +++ b/src/hb-gobject-enums.h.tmpl @@ -1,5 +1,6 @@ +/*** BEGIN file-header ***/ /* - * Copyright © 2012 Google, Inc. + * Copyright © 2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,23 +25,31 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-ot-shape-complex-indic-private.hh" +#ifndef HB_GOBJECT_H_IN +#error "Include <hb-gobject.h> instead." +#endif -int -main (void) -{ - hb_unicode_funcs_t *funcs = hb_unicode_funcs_get_default (); +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H - printf ("There are split matras without a Unicode decomposition:\n"); - for (hb_codepoint_t u = 0; u < 0x110000; u++) - { - unsigned int type = get_indic_categories (u); +#include "hb.h" - unsigned int category = type & 0x0F; - unsigned int position = type >> 4; +#include <glib-object.h> - hb_codepoint_t a, b; - if (!hb_unicode_decompose (funcs, u, &a, &b)) - printf ("U+%04X %x %x\n", u, category, position); - } -} +HB_BEGIN_DECLS + + +/*** END file-header ***/ + +/*** BEGIN value-header ***/ +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/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc index cec4854..2451b66 100644 --- a/src/hb-gobject-structs.cc +++ b/src/hb-gobject-structs.cc @@ -37,27 +37,84 @@ #include "hb-gobject.h" -#define _HB_DEFINE_BOXED_TYPE(Name,underscore_name,copy_func,free_func) \ +#define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \ GType \ -underscore_name##_get_type (void) \ +hb_gobject_##name##_get_type (void) \ { \ - static volatile gsize type = 0; \ - if (g_once_init_enter (&type)) { \ - GType t = g_boxed_type_register_static (g_intern_static_string (#Name), \ - (GBoxedCopyFunc) copy_func, \ - (GBoxedFreeFunc) free_func); \ - g_once_init_leave (&type, t); \ + 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; \ + return type_id; \ } -#define HB_DEFINE_BOXED_TYPE(name) \ - _HB_DEFINE_BOXED_TYPE (hb_##name, hb_gobject_##name, hb_##name##_reference, hb_##name##_destroy); +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy); -HB_DEFINE_BOXED_TYPE (buffer) -HB_DEFINE_BOXED_TYPE (blob) -HB_DEFINE_BOXED_TYPE (face) -HB_DEFINE_BOXED_TYPE (font) -HB_DEFINE_BOXED_TYPE (font_funcs) -HB_DEFINE_BOXED_TYPE (unicode_funcs) +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) + +static hb_feature_t *feature_reference (hb_feature_t *g) +{ + hb_feature_t *c = (hb_feature_t *) calloc (1, sizeof (hb_feature_t)); + if (unlikely (!c)) return NULL; + *c = *g; + return c; +} +static void feature_destroy (hb_feature_t *g) { free (g); } +HB_DEFINE_BOXED_TYPE (feature, feature_reference, feature_destroy) + +static hb_glyph_info_t *glyph_info_reference (hb_glyph_info_t *g) +{ + hb_glyph_info_t *c = (hb_glyph_info_t *) calloc (1, sizeof (hb_glyph_info_t)); + if (unlikely (!c)) return NULL; + *c = *g; + return c; +} +static void glyph_info_destroy (hb_glyph_info_t *g) { free (g); } +HB_DEFINE_BOXED_TYPE (glyph_info, glyph_info_reference, glyph_info_destroy) + +static hb_glyph_position_t *glyph_position_reference (hb_glyph_position_t *g) +{ + hb_glyph_position_t *c = (hb_glyph_position_t *) calloc (1, sizeof (hb_glyph_position_t)); + if (unlikely (!c)) return NULL; + *c = *g; + return c; +} +static void glyph_position_destroy (hb_glyph_position_t *g) { free (g); } +HB_DEFINE_BOXED_TYPE (glyph_position, glyph_position_reference, glyph_position_destroy) + +static hb_segment_properties_t *segment_properties_reference (hb_segment_properties_t *g) +{ + hb_segment_properties_t *c = (hb_segment_properties_t *) calloc (1, sizeof (hb_segment_properties_t)); + if (unlikely (!c)) return NULL; + *c = *g; + return c; +} +static void segment_properties_destroy (hb_segment_properties_t *g) { free (g); } +HB_DEFINE_BOXED_TYPE (segment_properties, segment_properties_reference, segment_properties_destroy) + +static hb_user_data_key_t user_data_key_reference (hb_user_data_key_t l) { return l; } +static void user_data_key_destroy (hb_user_data_key_t l) { } +HB_DEFINE_BOXED_TYPE (user_data_key, user_data_key_reference, user_data_key_destroy) + + +static hb_language_t *language_reference (hb_language_t *l) +{ + hb_language_t *c = (hb_language_t *) calloc (1, sizeof (hb_language_t)); + if (unlikely (!c)) return NULL; + *c = *l; + return c; +} +static void language_destroy (hb_language_t *l) { free (l); } +HB_DEFINE_BOXED_TYPE (language, language_reference, language_destroy) diff --git a/src/hb-gobject-structs.h b/src/hb-gobject-structs.h new file mode 100644 index 0000000..4a88d56 --- /dev/null +++ b/src/hb-gobject-structs.h @@ -0,0 +1,95 @@ +/* + * 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 */ + +GType hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +GType hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +GType hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +GType hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +GType hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +GType hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +GType hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +GType hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +GType hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +GType hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +GType hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +GType hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +GType hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + +/* Currently gobject-introspection doesn't understand that hb_language_t + * can be passed by-value. As such we box it up. May remove in the + * future. + * + * https://bugzilla.gnome.org/show_bug.cgi?id=707656 + */ +GType hb_gobject_language_get_type (void); +#define HB_GOBJECT_TYPE_LANGUAGE (hb_gobject_language_get_type ()) + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/src/hb-gobject.h b/src/hb-gobject.h index 4f23fdd..ea1bd25 100644 --- a/src/hb-gobject.h +++ b/src/hb-gobject.h @@ -26,44 +26,15 @@ #ifndef HB_GOBJECT_H #define HB_GOBJECT_H +#define HB_GOBJECT_H_IN #include "hb.h" -#include <glib-object.h> +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" HB_BEGIN_DECLS - - -/* Objects */ - -#define HB_GOBJECT_TYPE_BLOB hb_gobject_blob_get_type () -GType -hb_gobject_blob_get_type (void); - -#define HB_GOBJECT_TYPE_BUFFER hb_gobject_buffer_get_type () -GType -hb_gobject_buffer_get_type (void); - -#define HB_GOBJECT_TYPE_FACE hb_gobject_face_get_type () -GType -hb_gobject_face_get_type (void); - -#define HB_GOBJECT_TYPE_FONT hb_gobject_font_get_type () -GType -hb_gobject_font_get_type (void); - -#define HB_GOBJECT_TYPE_FONT_FUNCS hb_gobject_font_funcs_get_type () -GType -hb_gobject_font_funcs_get_type (void); - -#define HB_GOBJECT_TYPE_UNICODE_FUNCS hb_gobject_unicode_funcs_get_type () -GType -hb_gobject_unicode_funcs_get_type (void); - - -/* Enums */ - - HB_END_DECLS +#undef HB_GOBJECT_H_IN #endif /* HB_GOBJECT_H */ diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc index 3fa9f79..807c330 100644 --- a/src/hb-graphite2.cc +++ b/src/hb-graphite2.cc @@ -1,7 +1,7 @@ /* * Copyright © 2011 Martin Hosken * Copyright © 2011 SIL International - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -26,79 +26,65 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#define HB_SHAPER graphite2 +#define hb_graphite2_shaper_font_data_t gr_font +#include "hb-shaper-impl-private.hh" #include "hb-graphite2.h" -#include "hb-buffer-private.hh" -#include "hb-font-private.hh" -#include "hb-ot-tag.h" - -#include <graphite2/Font.h> #include <graphite2/Segment.h> -struct hb_gr_cluster_t { - unsigned int base_char; - unsigned int num_chars; - unsigned int base_glyph; - unsigned int num_glyphs; -}; +HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face) +HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font) -typedef struct hb_gr_tablelist_t { - hb_blob_t *blob; - struct hb_gr_tablelist_t *next; - unsigned int tag; -} hb_gr_tablelist_t; +/* + * shaper face data + */ -static struct hb_gr_face_data_t { - hb_face_t *face; - gr_face *grface; - hb_gr_tablelist_t *tlist; -} _hb_gr_face_data_nil = {NULL, NULL}; +typedef struct hb_graphite2_tablelist_t { + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; -static struct hb_gr_font_data_t { - gr_font *grfont; +struct hb_graphite2_shaper_face_data_t { + hb_face_t *face; gr_face *grface; -} _hb_gr_font_data_nil = {NULL, NULL}; - + hb_graphite2_tablelist_t *tlist; +}; -static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len) +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) { - hb_gr_tablelist_t *pl = NULL, *p; - hb_gr_face_data_t *face = (hb_gr_face_data_t *) data; - hb_gr_tablelist_t *tlist = face->tlist; - - for (p = tlist; p; p = p->next) - if (p->tag == tag ) { - unsigned int tlen; - const char *d = hb_blob_get_data (p->blob, &tlen); - *len = tlen; - return d; - } else - pl = p; - - if (!face->face) - return NULL; - hb_blob_t *blob = hb_face_reference_table (face->face, tag); + hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; - if (!pl || pl->blob) + 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)) { - p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t)); - if (!p) { + 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->next = NULL; - if (pl) - pl->next = p; - else - face->tlist = p; - pl = p; + 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; } - pl->blob = blob; - pl->tag = tag; unsigned int tlen; const char *d = hb_blob_get_data (blob, &tlen); @@ -106,183 +92,215 @@ static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t * return d; } -static float hb_gr_get_advance (const void *hb_font, unsigned short gid) +hb_graphite2_shaper_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) { - return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid); + 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; } -static void _hb_gr_face_data_destroy (void *data) +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data) { - hb_gr_face_data_t *f = (hb_gr_face_data_t *) data; - hb_gr_tablelist_t *tlist = f->tlist; + hb_graphite2_tablelist_t *tlist = data->tlist; + while (tlist) { - hb_gr_tablelist_t *old = tlist; + hb_graphite2_tablelist_t *old = tlist; hb_blob_destroy (tlist->blob); tlist = tlist->next; free (old); } - gr_face_destroy (f->grface); -} -static void _hb_gr_font_data_destroy (void *data) -{ - hb_gr_font_data_t *f = (hb_gr_font_data_t *) data; + gr_face_destroy (data->grface); - gr_font_destroy (f->grfont); - free (f); + free (data); } -static hb_user_data_key_t hb_gr_data_key; - -static hb_gr_face_data_t * -_hb_gr_face_get_data (hb_face_t *face) +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) { - hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key); - if (likely (data)) return data; - - data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t)); - if (unlikely (!data)) - return &_hb_gr_face_data_nil; + if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL; + return HB_SHAPER_DATA_GET (face)->grface; +} - hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf); - if (!hb_blob_get_length (silf_blob)) - { - hb_blob_destroy (silf_blob); - return &_hb_gr_face_data_nil; - } +/* + * shaper font data + */ - data->face = face; - data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default); +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; - if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data, - (hb_destroy_func_t) _hb_gr_face_data_destroy, - false))) - { - _hb_gr_face_data_destroy (data); - data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key); - if (data) - return data; - else - return &_hb_gr_face_data_nil; - } + hb_face_t *face = font->face; + hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - return data; + return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface); } -static hb_gr_font_data_t * -_hb_gr_font_get_data (hb_font_t *font) +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data) { - hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key); - if (likely (data)) return data; - - data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t)); - if (unlikely (!data)) - return &_hb_gr_font_data_nil; - + gr_font_destroy (data); +} - hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf); - if (!hb_blob_get_length (silf_blob)) - { - hb_blob_destroy (silf_blob); - return &_hb_gr_font_data_nil; - } +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); +} - data->grface = _hb_gr_face_get_data (font->face)->grface; - int scale; - hb_font_get_scale (font, &scale, NULL); - data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance, data->grface); +/* + * shaper shape_plan data + */ - if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data, - (hb_destroy_func_t) _hb_gr_font_data_destroy, - false))) - { - _hb_gr_font_data_destroy (data); - data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key); - if (data) - return data; - else - return &_hb_gr_font_data_nil; - } +struct hb_graphite2_shaper_shape_plan_data_t {}; - return data; +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) +{ + return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } - -hb_bool_t -_hb_graphite_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) +void +_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED) { +} - buffer->guess_properties (); - - /* XXX We do a hell of a lot of stuff just to figure out this font - * is not graphite! Shouldn't do. */ - - hb_gr_font_data_t *data = _hb_gr_font_get_data (font); - if (!data->grface) return false; - unsigned int charlen; - hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen); +/* + * shaper + */ - int success = 0; +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; +}; - if (!charlen) return true; +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 = strchr (lang, '-'); + 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 (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); while (num_features--) { - const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag); + const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag); if (fref) gr_fref_set_feature_value (fref, features->value, feats); features++; } - hb_codepoint_t *gids = NULL, *pg; - hb_gr_cluster_t *clusters = NULL; gr_segment *seg = NULL; - uint32_t *text = NULL; const gr_slot *is; unsigned int ci = 0, ic = 0; float curradvx = 0., curradvy = 0.; - unsigned int glyphlen = 0; - unsigned int *p; - text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t)); - if (!text) goto dieout; + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; - p = text; - for (unsigned int i = 0; i < charlen; ++i) - *p++ = bufferi++->codepoint; - *p = 0; + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; hb_tag_t script_tag[2]; hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]); - seg = gr_make_seg (data->grfont, data->grface, + seg = gr_make_seg (grfont, grface, script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], feats, - gr_utf32, text, charlen, + gr_utf32, chars, buffer->len, 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); - if (!seg) goto dieout; - glyphlen = gr_seg_n_slots (seg); - clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t)); - if (!glyphlen || !clusters) goto dieout; + 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); + return false; + } + + 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); - gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t)); - if (!gids) goto dieout; +#undef ALLOCATE_ARRAY - pg = gids; + memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; 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); @@ -298,8 +316,9 @@ _hb_graphite_shape (hb_font_t *font, if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) { - hb_gr_cluster_t *c = clusters + ci + 1; + 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; @@ -312,39 +331,46 @@ _hb_graphite_shape (hb_font_t *font, } ci++; - buffer->clear_output (); + //buffer->clear_output (); for (unsigned int i = 0; i < ci; ++i) - buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph); - buffer->swap_buffers (); + { + 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; + } + } + buffer->len = glyph_count; + //buffer->swap_buffers (); + + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + curradvx = gr_seg_advance_X(seg); hb_glyph_position_t *pPos; for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg); is; pPos++, is = gr_slot_next_in_segment (is)) { - pPos->x_offset = gr_slot_origin_X(is) - curradvx; - pPos->y_offset = gr_slot_origin_Y(is) - curradvy; - pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont); - pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont); -// if (pPos->x_advance < 0 && gr_slot_attached_to(is)) -// pPos->x_advance = 0; - curradvx += pPos->x_advance; + pPos->x_offset = gr_slot_origin_X (is) - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) - curradvy; + pPos->x_advance = gr_slot_advance_X (is, grface, grfont); + pPos->y_advance = gr_slot_advance_Y (is, grface, grfont); + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + curradvx -= pPos->x_advance; + pPos->x_offset = gr_slot_origin_X (is) - curradvx; + if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + curradvx += pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) - curradvy; curradvy += pPos->y_advance; } - pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx; - - /* TODO(behdad): - * This shaper is badly broken with RTL text. It returns glyphs - * in the logical order! - */ -// if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) -// hb_buffer_reverse (buffer); - - success = 1; - -dieout: - if (gids) free (gids); - if (clusters) free (clusters); - if (seg) gr_seg_destroy (seg); - if (text) free (text); - return success; + if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx; + + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + hb_buffer_reverse_clusters (buffer); + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + return true; } diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h index 2d16cc8..3eae54a 100644 --- a/src/hb-graphite2.h +++ b/src/hb-graphite2.h @@ -28,12 +28,20 @@ #include "hb.h" +#include <graphite2/Font.h> + HB_BEGIN_DECLS -#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f') +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); -/* TODO add gr_font/face etc getters and other glue API */ HB_END_DECLS diff --git a/src/hb-icu.cc b/src/hb-icu.cc index aead6dd..24cec9d 100644 --- a/src/hb-icu.cc +++ b/src/hb-icu.cc @@ -33,11 +33,10 @@ #include "hb-unicode-private.hh" -#include <unicode/uversion.h> #include <unicode/uchar.h> #include <unicode/unorm.h> #include <unicode/ustring.h> - +#include <unicode/uversion.h> hb_script_t @@ -63,13 +62,13 @@ hb_icu_script_from_script (hb_script_t script) } -static unsigned int +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 u_getCombiningClass (unicode); + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); } static unsigned int @@ -164,6 +163,10 @@ hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, 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, @@ -171,11 +174,20 @@ hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t *ab, void *user_data HB_UNUSED) { - if (!a || !b) - return false; +#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]; - int len; + unsigned int len; hb_bool_t ret, err; UErrorCode icu_err; @@ -207,8 +219,34 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t *b, void *user_data HB_UNUSED) { - UChar utf16[2], normalized[20]; - int len; +#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; @@ -255,13 +293,15 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, } 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); - UChar recomposed[20]; + 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; } @@ -269,24 +309,62 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, 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; -extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs; -const hb_unicode_funcs_t _hb_icu_unicode_funcs = { - HB_OBJECT_HEADER_STATIC, + /* 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; +} - NULL, /* parent */ - true, /* immutable */ - { -#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name, - HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_UNICODE_FUNC_IMPLEMENT - } -}; hb_unicode_funcs_t * hb_icu_get_unicode_funcs (void) { - return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs); -} + 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/src/hb-icu.h b/src/hb-icu.h index d22a8e1..f2f35f0 100644 --- a/src/hb-icu.h +++ b/src/hb-icu.h @@ -33,7 +33,6 @@ #include <unicode/uscript.h> - HB_BEGIN_DECLS diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh index f9bd679..a8ea39c 100644 --- a/src/hb-mutex-private.hh +++ b/src/hb-mutex-private.hh @@ -42,12 +42,16 @@ #if 0 -#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__) +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) #include <windows.h> typedef CRITICAL_SECTION hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 } +#define HB_MUTEX_IMPL_INIT {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) @@ -64,17 +68,6 @@ typedef pthread_mutex_t hb_mutex_impl_t; #define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) -#elif !defined(HB_NO_MT) && defined(HAVE_GLIB) - -#include <glib.h> -typedef GStaticMutex hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT G_STATIC_MUTEX_INIT -#define hb_mutex_impl_init(M) g_static_mutex_init (M) -#define hb_mutex_impl_lock(M) g_static_mutex_lock (M) -#define hb_mutex_impl_unlock(M) g_static_mutex_unlock (M) -#define hb_mutex_impl_finish(M) g_static_mutex_free (M) - - #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh index 96d1bd3..7bd0f16 100644 --- a/src/hb-object-private.hh +++ b/src/hb-object-private.hh @@ -65,11 +65,9 @@ struct hb_reference_count_t /* user_data */ -#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT} +#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} struct hb_user_data_array_t { - /* TODO Add tracing. */ - struct hb_user_data_item_t { hb_user_data_key_t *key; void *data; @@ -81,20 +79,19 @@ struct hb_user_data_array_t 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) { items.init (); } + 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_mutex_t &lock); + hb_bool_t replace); - HB_INTERNAL void *get (hb_user_data_key_t *key, - hb_mutex_t &lock); + HB_INTERNAL void *get (hb_user_data_key_t *key); - HB_INTERNAL void finish (hb_mutex_t &lock); + inline void finish (void) { items.finish (lock); lock.finish (); } }; @@ -103,75 +100,9 @@ struct hb_user_data_array_t struct hb_object_header_t { hb_reference_count_t ref_count; - hb_mutex_t lock; hb_user_data_array_t user_data; -#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT} - - static inline void *create (unsigned int size) { - hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size); - - if (likely (obj)) - obj->init (); - - return obj; - } - - inline void init (void) { - ref_count.init (1); - lock.init (); - user_data.init (); - } - - inline bool is_inert (void) const { - return unlikely (ref_count.is_invalid ()); - } - - inline void reference (void) { - if (unlikely (!this || this->is_inert ())) - return; - ref_count.inc (); - } - - inline bool destroy (void) { - if (unlikely (!this || this->is_inert ())) - return false; - if (ref_count.dec () != 1) - return false; - - ref_count.finish (); /* Do this before user_data */ - user_data.finish (lock); - lock.finish (); - - return true; - } - - inline bool set_user_data (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy_func, - hb_bool_t replace) { - if (unlikely (!this || this->is_inert ())) - return false; - - return user_data.set (key, data, destroy_func, replace, lock); - } - - inline void *get_user_data (hb_user_data_key_t *key) { - if (unlikely (!this || this->is_inert ())) - return NULL; - - return user_data.get (key, lock); - } - - inline void trace (const char *function) const { - if (unlikely (!this)) return; - /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only - * prints the class name and throws away the template info. */ - DEBUG_MSG (OBJECT, (void *) this, - "%s refcount=%d", - function, - this ? ref_count.ref_count : 0); - } +#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_USER_DATA_ARRAY_INIT} private: ASSERT_POD (); @@ -183,32 +114,56 @@ struct hb_object_header_t template <typename Type> static inline void hb_object_trace (const Type *obj, const char *function) { - obj->header.trace (function); + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.ref_count : 0); } + template <typename Type> static inline Type *hb_object_create (void) { - Type *obj = (Type *) hb_object_header_t::create (sizeof (Type)); + 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.is_inert ()); + return unlikely (obj->header.ref_count.is_invalid ()); } template <typename Type> static inline Type *hb_object_reference (Type *obj) { hb_object_trace (obj, HB_FUNC); - obj->header.reference (); + if (unlikely (!obj || hb_object_is_inert (obj))) + return 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); - return obj->header.destroy (); + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + 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, @@ -217,14 +172,18 @@ static inline bool hb_object_set_user_data (Type *obj, hb_destroy_func_t destroy, hb_bool_t replace) { - return obj->header.set_user_data (key, data, destroy, replace); + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + 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) { - return obj->header.get_user_data (key); + if (unlikely (!obj || hb_object_is_inert (obj))) + return NULL; + return obj->header.user_data.get (key); } diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index ce18580..178bc7c 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -32,6 +32,8 @@ #include "hb-open-type-private.hh" +namespace OT { + /* * @@ -51,8 +53,9 @@ struct TTCHeader; typedef struct TableRecord { - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } @@ -100,17 +103,18 @@ typedef struct OffsetTable } public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables)); } - private: + protected: Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ USHORT numTables; /* Number of tables. */ - USHORT searchRange; /* (Maximum power of 2 <= numTables) x 16 */ - USHORT entrySelector; /* Log2(maximum power of 2 <= numTables). */ - USHORT rangeShift; /* NumTables x 16-searchRange. */ + 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); @@ -128,16 +132,17 @@ struct TTCHeaderVersion1 inline unsigned int get_face_count (void) const { return table.len; } inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (table.sanitize (c, this)); } - private: + protected: Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ FixedVersion version; /* Version of the TTC Header (1.0), - * 0x00010000 */ - LongOffsetLongArrayOf<OffsetTable> + * 0x00010000u */ + ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG> table; /* Array of offsets to the OffsetTable for each font * from the beginning of the file */ public: @@ -167,8 +172,9 @@ struct TTCHeader } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false); switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ @@ -177,12 +183,12 @@ struct TTCHeader } } - private: + protected: union { struct { Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ FixedVersion version; /* Version of the TTC Header (1.0 or 2.0), - * 0x00010000 or 0x00020000 */ + * 0x00010000u or 0x00020000u */ } header; TTCHeaderVersion1 version1; } u; @@ -195,6 +201,8 @@ struct TTCHeader 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 */ @@ -229,8 +237,9 @@ struct OpenTypeFontFile } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false); switch (u.tag) { case CFFTag: /* All the non-collection tags */ @@ -242,7 +251,7 @@ struct OpenTypeFontFile } } - private: + protected: union { Tag tag; /* 4-byte identifier. */ OpenTypeFontFace fontFace; @@ -253,5 +262,7 @@ struct OpenTypeFontFile }; +} /* namespace OT */ + #endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index 5d90e5b..75a0f56 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -31,7 +31,8 @@ #include "hb-private.hh" -#include "hb-blob.h" + +namespace OT { @@ -41,36 +42,36 @@ /* Cast to struct T, reference to reference */ template<typename Type, typename TObject> -inline const Type& CastR(const TObject &X) +static inline const Type& CastR(const TObject &X) { return reinterpret_cast<const Type&> (X); } template<typename Type, typename TObject> -inline Type& CastR(TObject &X) +static inline Type& CastR(TObject &X) { return reinterpret_cast<Type&> (X); } /* Cast to struct T, pointer to pointer */ template<typename Type, typename TObject> -inline const Type* CastP(const TObject *X) +static inline const Type* CastP(const TObject *X) { return reinterpret_cast<const Type*> (X); } template<typename Type, typename TObject> -inline Type* CastP(TObject *X) +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> -inline const Type& StructAtOffset(const void *P, unsigned int offset) +static inline const Type& StructAtOffset(const void *P, unsigned int offset) { return * reinterpret_cast<const Type*> ((const char *) P + offset); } template<typename Type> -inline Type& StructAtOffset(void *P, unsigned int offset) +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> -inline const Type& StructAfter(const TObject &X) +static inline const Type& StructAfter(const TObject &X) { return StructAtOffset<Type>(&X, X.get_size()); } template<typename Type, typename TObject> -inline Type& StructAfter(TObject &X) +static inline Type& StructAfter(TObject &X) { return StructAtOffset<Type>(&X, X.get_size()); } @@ -130,20 +131,21 @@ inline Type& StructAfter(TObject &X) */ /* Global nul-content Null pool. Enlarge as necessary. */ -static const void *_NullPool[64 / sizeof (void *)]; +/* 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 (Type::min_size <= sizeof (_NullPool)); + 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[Type::min_size + 1] = data; /* +1 is for nul-termination in data */ \ +static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \ template <> \ -inline const Type& Null<Type> (void) { \ +/*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)) @@ -162,12 +164,29 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type)) #endif -#define TRACE_SANITIZE() \ - hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, ""); +#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 100 +#endif struct hb_sanitize_context_t { + inline const char *get_name (void) { return "SANITIZE"; } + static const unsigned int max_debug_depth = HB_DEBUG_SANITIZE; + typedef bool return_t; + 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; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + inline void init (hb_blob_t *b) { this->blob = hb_blob_reference (b); @@ -178,10 +197,11 @@ struct hb_sanitize_context_t { 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, this->blob, 0, +1, + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, "start [%p..%p] (%lu bytes)", this->start, this->end, (unsigned long) (this->end - this->start)); @@ -189,7 +209,7 @@ struct hb_sanitize_context_t inline void end_processing (void) { - DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1, + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, "end [%p..%p] %u edit requests", this->start, this->end, this->edit_count); @@ -201,26 +221,31 @@ struct hb_sanitize_context_t 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; - hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL, - "check_range [%p..%p] (%d bytes) in [%p..%p]", - p, p + len, len, - this->start, this->end); + 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 TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len)); + 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); - hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL, - "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]", - p, p + (record_size * len), record_size, len, (unsigned long) record_size * len, - this->start, this->end); + 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 TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len))); + return likely (ok); } template <typename Type> @@ -231,16 +256,29 @@ struct hb_sanitize_context_t 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++; - hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end); + 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; + } - return TRACE_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; @@ -257,7 +295,7 @@ template <typename Type> struct Sanitizer { static hb_blob_t *sanitize (hb_blob_t *blob) { - hb_sanitize_context_t c[1] = {{0}}; + hb_sanitize_context_t c[1] = {{0, NULL, NULL, false, 0, NULL}}; bool sane; /* TODO is_sane() stuff */ @@ -265,7 +303,7 @@ struct Sanitizer c->init (blob); retry: - DEBUG_MSG_FUNC (SANITIZE, blob, "start"); + DEBUG_MSG_FUNC (SANITIZE, c->start, "start"); c->start_processing (); @@ -279,13 +317,13 @@ struct Sanitizer sane = t->sanitize (c); if (sane) { if (c->edit_count) { - DEBUG_MSG_FUNC (SANITIZE, blob, "passed first round with %d edits; going for second round", 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, blob, "requested %d edits in second round; FAILLING", c->edit_count); + DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count); sane = false; } } @@ -298,7 +336,7 @@ struct Sanitizer if (c->start) { c->writable = true; /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, blob, "retry"); + DEBUG_MSG_FUNC (SANITIZE, c->start, "retry"); goto retry; } } @@ -306,7 +344,7 @@ struct Sanitizer c->end_processing (); - DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED"); + DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED"); if (sane) return blob; else { @@ -324,6 +362,162 @@ struct Sanitizer +/* + * 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 *head) + { + assert (this->start < head && head <= this->head); + this->head = (char *) 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; +}; + + + /* * @@ -341,57 +535,95 @@ struct Sanitizer template <typename Type, int Bytes> struct BEInt; -/* LONGTERMTODO: On machines allowing unaligned access, we can make the - * following tighter by using byteswap instructions on ints directly. */ template <typename Type> struct BEInt<Type, 2> { public: - inline void set (Type i) { hb_be_uint16_put (v,i); } - inline operator Type (void) const { return hb_be_uint16_get (v); } - inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_eq (v, o.v); } - inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); } + 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 i) { hb_be_uint32_put (v,i); } - inline operator Type (void) const { return hb_be_uint32_get (v); } - inline bool operator == (const BEInt<Type, 4>& o) const { return hb_be_uint32_eq (v, o.v); } - inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); } + 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> +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> &o) const { return v == o.v; } - inline bool operator != (const IntType<Type> &o) const { return v != o.v; } - inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (likely (c->check_struct (this))); } protected: - BEInt<Type, sizeof (Type)> v; + BEInt<Type, Size> v; public: - DEFINE_SIZE_STATIC (sizeof (Type)); + DEFINE_SIZE_STATIC (Size); }; -/* Typedef these to avoid clash with windows.h */ -#define USHORT HB_USHORT -#define SHORT HB_SHORT -#define ULONG HB_ULONG -#define LONG HB_LONG -typedef IntType<uint16_t> USHORT; /* 16-bit unsigned integer. */ -typedef IntType<int16_t> SHORT; /* 16-bit signed integer. */ -typedef IntType<uint32_t> ULONG; /* 32-bit unsigned integer. */ -typedef IntType<int32_t> LONG; /* 32-bit signed integer. */ +typedef uint8_t BYTE; /* 8-bit unsigned 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; @@ -403,11 +635,12 @@ typedef USHORT UFWORD; * 1904. The value is represented as a signed 64-bit integer. */ struct LONGDATETIME { - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (likely (c->check_struct (this))); } - private: + protected: LONG major; ULONG minor; public: @@ -427,33 +660,45 @@ struct Tag : ULONG DEFINE_NULL_DATA (Tag, " "); /* Glyph index number, same as uint16 (length = 16 bits) */ -typedef USHORT GlyphID; +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 = 0xFFFF; + static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; }; DEFINE_NULL_DATA (Index, "\xff\xff"); -/* Offset to a table, same as uint16 (length = 16 bits), Null offset = 0x0000 */ -typedef USHORT Offset; - -/* LongOffset to a table, same as uint32 (length = 32 bits), Null offset = 0x00000000 */ -typedef ULONG LongOffset; +/* 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 { - static uint32_t CalcTableChecksum (ULONG *Table, uint32_t Length) + /* This is reference implementation from the spec. */ + static inline uint32_t CalcTableChecksum (const ULONG *Table, uint32_t Length) { uint32_t Sum = 0L; - ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size; + 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); }; @@ -467,8 +712,9 @@ struct FixedVersion { inline uint32_t to_int (void) const { return (major << 16) + minor; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } @@ -481,12 +727,12 @@ struct FixedVersion /* - * Template subclasses of Offset and LongOffset that do the dereferencing. + * Template subclasses of Offset that do the dereferencing. * Use: (base+offset) */ -template <typename OffsetType, typename Type> -struct GenericOffsetTo : OffsetType +template <typename Type, typename OffsetType=USHORT> +struct OffsetTo : Offset<OffsetType> { inline const Type& operator () (const void *base) const { @@ -495,50 +741,52 @@ struct GenericOffsetTo : OffsetType return StructAtOffset<Type> (base, offset); } - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); + 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_RETURN (false); unsigned int offset = *this; if (unlikely (!offset)) return TRACE_RETURN (true); - Type &obj = StructAtOffset<Type> (base, offset); + const Type &obj = StructAtOffset<Type> (base, offset); return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c)); } template <typename T> - inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) { - TRACE_SANITIZE (); + 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_RETURN (false); unsigned int offset = *this; if (unlikely (!offset)) return TRACE_RETURN (true); - Type &obj = StructAtOffset<Type> (base, offset); + const Type &obj = StructAtOffset<Type> (base, offset); return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c)); } - private: /* Set the offset to Null */ - inline bool neuter (hb_sanitize_context_t *c) { - if (c->may_edit (this, this->static_size)) { - this->set (0); /* 0 is Null offset */ - return true; - } - return false; + 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> -inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); } - -template <typename Type> -struct OffsetTo : GenericOffsetTo<Offset, Type> {}; - -template <typename Type> -struct LongOffsetTo : GenericOffsetTo<LongOffset, Type> {}; +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 */ -template <typename LenType, typename Type> -struct GenericArrayOf +/* 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 { @@ -557,11 +805,38 @@ struct GenericArrayOf 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 sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool serialize (hb_serialize_context_t *c, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!c->extend (*this))) return TRACE_RETURN (false); + return TRACE_RETURN (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_RETURN (false); + for (unsigned int i = 0; i < items_len; i++) + array[i] = items[i]; + items.advance (items_len); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); /* Note: for structs that do not reference other structs, @@ -575,8 +850,9 @@ struct GenericArrayOf return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); unsigned int count = len; for (unsigned int i = 0; i < count; i++) @@ -585,8 +861,9 @@ struct GenericArrayOf return TRACE_RETURN (true); } template <typename T> - inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) { - TRACE_SANITIZE (); + 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_RETURN (false); unsigned int count = len; for (unsigned int i = 0; i < count; i++) @@ -595,9 +872,20 @@ struct GenericArrayOf return TRACE_RETURN (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) { - TRACE_SANITIZE (); + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len)); } @@ -608,26 +896,10 @@ struct GenericArrayOf DEFINE_SIZE_ARRAY (sizeof (LenType), array); }; -/* An array with a USHORT number of elements. */ -template <typename Type> -struct ArrayOf : GenericArrayOf<USHORT, Type> {}; - -/* An array with a ULONG number of elements. */ -template <typename Type> -struct LongArrayOf : GenericArrayOf<ULONG, Type> {}; - /* Array of Offset's */ template <typename Type> struct OffsetArrayOf : ArrayOf<OffsetTo<Type> > {}; -/* Array of LongOffset's */ -template <typename Type> -struct LongOffsetArrayOf : ArrayOf<LongOffsetTo<Type> > {}; - -/* LongArray of LongOffset's */ -template <typename Type> -struct LongOffsetLongArrayOf : LongArrayOf<LongOffsetTo<Type> > {}; - /* Array of offsets relative to the beginning of the array itself. */ template <typename Type> struct OffsetListOf : OffsetArrayOf<Type> @@ -638,21 +910,22 @@ struct OffsetListOf : OffsetArrayOf<Type> return this+this->array[i]; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this)); } template <typename T> - inline bool sanitize (hb_sanitize_context_t *c, T user_data) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, T user_data) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data)); } }; -/* An array with a USHORT number of elements, - * starting at second element. */ -template <typename Type> +/* An array starting at second element. */ +template <typename Type, typename LenType=USHORT> struct HeadlessArrayOf { inline const Type& operator [] (unsigned int i) const @@ -663,13 +936,30 @@ struct HeadlessArrayOf inline unsigned int get_size (void) const { return len.static_size + (len ? len - 1 : 0) * Type::static_size; } - inline bool sanitize_shallow (hb_sanitize_context_t *c) { + 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_RETURN (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!items_len)) return TRACE_RETURN (true); + if (unlikely (!c->extend (*this))) return TRACE_RETURN (false); + for (unsigned int i = 0; i < items_len - 1; i++) + array[i] = items[i]; + items.advance (items_len - 1); + return TRACE_RETURN (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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); /* Note: for structs that do not reference other structs, @@ -684,27 +974,39 @@ struct HeadlessArrayOf return TRACE_RETURN (true); } - USHORT len; + LenType len; Type array[VAR]; public: - DEFINE_SIZE_ARRAY (sizeof (USHORT), array); + DEFINE_SIZE_ARRAY (sizeof (LenType), array); }; /* An array with sorted elements. Supports binary searching. */ -template <typename Type> -struct SortedArrayOf : ArrayOf<Type> { - +template <typename Type, typename LenType=USHORT> +struct SortedArrayOf : ArrayOf<Type, LenType> +{ template <typename SearchType> - inline int search (const SearchType &x) const { - struct Cmp { - static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); } - }; - const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp); - return p ? p - this->array : -1; + 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/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh new file mode 100644 index 0000000..0482312 --- /dev/null +++ b/src/hb-ot-cmap-table.hh @@ -0,0 +1,528 @@ +/* + * 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_RETURN (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 +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + unsigned int segCount; + const USHORT *endCount; + const USHORT *startCount; + const USHORT *idDelta; + const USHORT *idRangeOffset; + const USHORT *glyphIdArray; + unsigned int glyphIdArrayLength; + + segCount = this->segCountX2 / 2; + endCount = this->values; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (this->length - 16 - 8 * segCount) / 2; + + /* Custom two-array bsearch. */ + int min = 0, max = (int) segCount - 1; + 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 = idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - startCount[i]) + i - segCount; + if (unlikely (index >= glyphIdArrayLength)) + return false; + gid = glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += idDelta[i]; + } + + *glyph = gid & 0xFFFFu; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return TRACE_RETURN (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_RETURN (false); + } + + return TRACE_RETURN (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_RETURN (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_RETURN (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_RETURN (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_RETURN (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_RETURN (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_RETURN (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_RETURN (c->check_struct (this) && + record.sanitize (c, this)); + } + + protected: + USHORT format; /* Format number is set to 0. */ + 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 glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 14: return u.format14.get_glyph_variant(codepoint, variation_selector, glyph); + default: return GLYPH_VARIANT_NOT_FOUND; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 0: return TRACE_RETURN (u.format0 .sanitize (c)); + case 4: return TRACE_RETURN (u.format4 .sanitize (c)); + case 6: return TRACE_RETURN (u.format6 .sanitize (c)); + case 10: return TRACE_RETURN (u.format10.sanitize (c)); + case 12: return TRACE_RETURN (u.format12.sanitize (c)); + case 13: return TRACE_RETURN (u.format13.sanitize (c)); + case 14: return TRACE_RETURN (u.format14.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + protected: + 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_RETURN (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_RETURN (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/src/hb-ot-font.cc b/src/hb-ot-font.cc new file mode 100644 index 0000000..2af2f54 --- /dev/null +++ b/src/hb-ot-font.cc @@ -0,0 +1,348 @@ +/* + * 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-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" + + +struct hb_ot_face_metrics_accelerator_t +{ + unsigned int num_metrics; + unsigned int num_advances; + unsigned int default_advance; + 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, + unsigned int default_advance) + { + this->default_advance = default_advance; + this->num_metrics = face->get_num_glyphs (); + + 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; + hb_blob_destroy (_hea_blob); + + this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag)); + if (unlikely (!this->num_advances || + 2 * (this->num_advances + this->num_metrics) < hb_blob_get_length (this->blob))) + { + 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 one EM. 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_cmap_accelerator_t +{ + const OT::CmapSubtable *table; + const OT::CmapSubtable *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::CmapSubtable *subtable_uvs = NULL; + + /* 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); + /* Meh. */ + if (!subtable) subtable = &OT::Null(OT::CmapSubtable); + + /* UVS subtable. */ + if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5); + /* Meh. */ + if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable); + + this->table = subtable; + this->uvs_table = subtable_uvs; + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline bool get_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + if (unlikely (variation_selector)) + { + 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 this->table->get_glyph (unicode, glyph); + } +}; + + +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; +}; + + +static hb_ot_font_t * +_hb_ot_font_create (hb_font_t *font) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); + hb_face_t *face = font->face; + + if (unlikely (!ot_font)) + return NULL; + + unsigned int upem = face->get_upem (); + + ot_font->cmap.init (face); + ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1); + ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, upem); /* TODO Can we do this lazily? */ + + 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 (); + + free (ot_font); +} + + +static hb_bool_t +hb_ot_get_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_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 (-ot_font->v_metrics.get_advance (glyph)); +} + +static hb_bool_t +hb_ot_get_glyph_h_origin (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x HB_UNUSED, + hb_position_t *y HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* We always work in the horizontal coordinates. */ + return true; +} + +static hb_bool_t +hb_ot_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) +{ + /* TODO */ + return false; +} + +static hb_position_t +hb_ot_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) +{ + /* TODO */ + return 0; +} + +static hb_position_t +hb_ot_get_glyph_v_kerning (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* OpenType doesn't have vertical-kerning other than GPOS. */ + return 0; +} + +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) +{ + /* TODO */ + return false; +} + +static hb_bool_t +hb_ot_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) +{ + /* TODO */ + return false; +} + +static hb_bool_t +hb_ot_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) +{ + /* TODO */ + return false; +} + +static hb_bool_t +hb_ot_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) +{ + /* TODO */ + return false; +} + + +static hb_font_funcs_t * +_hb_ot_get_font_funcs (void) +{ + static const hb_font_funcs_t ot_ffuncs = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_ot_get_##name, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + }; + + return const_cast<hb_font_funcs_t *> (&ot_ffuncs); +} + + +void +hb_ot_font_set_funcs (hb_font_t *font) +{ + hb_ot_font_t *ot_font = _hb_ot_font_create (font); + 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/src/hb-fallback-shape-private.hh b/src/hb-ot-font.h index 159456d..7a8c04a 100644 --- a/src/hb-fallback-shape-private.hh +++ b/src/hb-ot-font.h @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2014 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -21,27 +21,21 @@ * 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 + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ -#ifndef HB_FALLBACK_SHAPE_PRIVATE_HH -#define HB_FALLBACK_SHAPE_PRIVATE_HH - -#include "hb-private.hh" - -#include "hb-shape.h" +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H +#include "hb.h" HB_BEGIN_DECLS -HB_INTERNAL hb_bool_t -_hb_fallback_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); +void +hb_ot_font_set_funcs (hb_font_t *font); HB_END_DECLS -#endif /* HB_FALLBACK_SHAPE_PRIVATE_HH */ +#endif /* HB_OT_FONT_H */ diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh index 32d64ca..268f133 100644 --- a/src/hb-ot-head-table.hh +++ b/src/hb-ot-head-table.hh @@ -32,6 +32,8 @@ #include "hb-open-type-private.hh" +namespace OT { + /* * head -- Font Header @@ -41,27 +43,29 @@ struct head { - static const hb_tag_t Tag = HB_OT_TAG_head; + static const hb_tag_t tableTag = HB_OT_TAG_head; - inline unsigned int get_upem (void) const { + inline unsigned int get_upem (void) const + { unsigned int upem = unitsPerEm; - /* If no valid head table found, assume 1000, which matches typicaly Type1 usage. */ + /* 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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1)); } - private: + protected: FixedVersion version; /* Version of the head table--currently - * 0x00010000 for version 1.0. */ + * 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 - * 0xB1B0AFBA - sum. */ - ULONG magicNumber; /* Set to 0x5F0F3CF5. */ + * 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; @@ -141,5 +145,7 @@ struct head }; +} /* namespace OT */ + #endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh index 2eea05a..992fe55 100644 --- a/src/hb-ot-hhea-table.hh +++ b/src/hb-ot-hhea-table.hh @@ -30,63 +30,74 @@ #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 hhea +struct _hea { - static const hb_tag_t Tag = HB_OT_TAG_hhea; + 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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1)); } - private: - FixedVersion version; /* 0x00010000 for version 1.0. */ - FWORD ascender; /* Typographic ascent. <a - * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html"> - * (Distance from baseline of highest - * ascender)</a> */ - FWORD descender; /* Typographic descent. <a - * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html"> - * (Distance from baseline of lowest - * descender)</a> */ - FWORD lineGap; /* Typographic line gap. Negative - * LineGap values are treated as zero - * in Windows 3.1, System 6, and - * System 7. */ - UFWORD advanceWidthMax; /* Maximum advance width value in - * 'hmtx' table. */ - FWORD minLeftSideBearing; /* Minimum left sidebearing value in - * 'hmtx' table. */ - FWORD minRightSideBearing; /* Minimum right sidebearing value; + 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)). */ - FWORD xMaxExtent; /* Max(lsb + (xMax - xMin)). */ + * (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. */ - SHORT caretSlopeRun; /* 0 for vertical. */ + * 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 */ + * non-slanted fonts. */ + SHORT reserved1; /* Set to 0. */ + SHORT reserved2; /* Set to 0. */ + SHORT reserved3; /* Set to 0. */ + SHORT reserved4; /* Set to 0. */ SHORT metricDataFormat; /* 0 for current format. */ - USHORT numberOfHMetrics; /* Number of hMetric entries in 'hmtx' - * table */ + 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/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 35cfb48..a0e3855 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -30,35 +30,43 @@ #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 LongHorMetric +struct LongMetric { - USHORT advanceWidth; - SHORT lsb; + USHORT advance; /* Advance width/height. */ + SHORT lsb; /* Leading (left/top) side bearing. */ public: DEFINE_SIZE_STATIC (4); }; -struct hmtx +struct _mtx { - static const hb_tag_t Tag = HB_OT_TAG_hmtx; + 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) { - TRACE_SANITIZE (); + 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_RETURN (true); } - private: - LongHorMetric longHorMetric[VAR]; /* Paired advance width and left side + 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 @@ -66,21 +74,31 @@ struct hmtx * be in the array, but that entry is * required. The last entry applies to * all subsequent glyphs. */ - SHORT leftSideBearingX[VAR]; /* Here the advanceWidth is assumed - * to be the same as the advanceWidth + SHORT 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 numberOfHMetrics. This - * generally is used with a run of - * monospaced glyphs (e.g., Kanji + * 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 left side bearing + * font to vary the side bearing * values for each glyph. */ public: - DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX); + 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/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh index 2943a7f..3db7f57 100644 --- a/src/hb-ot-layout-common-private.hh +++ b/src/hb-ot-layout-common-private.hh @@ -34,8 +34,18 @@ #include "hb-set-private.hh" -#define NOT_COVERED ((unsigned int) 0x110000) +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) #define MAX_NESTING_LEVEL 8 +#define MAX_CONTEXT_LENGTH 64 @@ -57,9 +67,15 @@ struct Record return tag.cmp (a); } - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); - return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base)); + 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_RETURN (c->check_struct (this) && offset.sanitize (c, base, &closure)); } Tag tag; /* 4-byte Tag identifier */ @@ -94,7 +110,8 @@ struct RecordArrayOf : SortedArrayOf<Record<Type> > { } inline bool find_index (hb_tag_t tag, unsigned int *index) const { - int i = this->search (tag); + /* 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; @@ -111,8 +128,9 @@ struct RecordListOf : RecordArrayOf<Type> inline const Type& operator [] (unsigned int i) const { return this+RecordArrayOf<Type>::operator [](i).offset; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this)); } }; @@ -121,12 +139,12 @@ struct RecordListOf : RecordArrayOf<Type> struct RangeRecord { inline int cmp (hb_codepoint_t g) const { - hb_codepoint_t a = start, b = end; - return g < a ? -1 : g <= b ? 0 : +1 ; + return g < start ? -1 : g <= end ? 0 : +1 ; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } @@ -134,6 +152,11 @@ struct RangeRecord 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 */ @@ -176,24 +199,26 @@ struct LangSys unsigned int *feature_indexes /* OUT */) const { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } - inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; } + inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; } inline unsigned int get_required_feature_index (void) const { - if (reqFeatureIndex == 0xffff) + if (reqFeatureIndex == 0xFFFFu) return Index::NOT_FOUND_INDEX; return reqFeatureIndex;; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, + const Record<LangSys>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c)); } - Offset lookupOrder; /* = Null (reserved for an offset to a + 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 - * = 0xFFFF */ + * = 0xFFFFu */ IndexArray featureIndex; /* Array of indices into the FeatureList */ public: DEFINE_SIZE_ARRAY (6, featureIndex); @@ -222,12 +247,14 @@ struct Script inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, + const Record<Script>::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); } - private: + protected: OffsetTo<LangSys> defaultLangSys; /* Offset to DefaultLangSys table--from * beginning of Script table--may be Null */ @@ -241,6 +268,224 @@ struct Script 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_RETURN (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_RETURN (false); + else if (subfamilyID == 0 && + subfamilyNameID == 0 && + rangeStart == 0 && + rangeEnd == 0) + return TRACE_RETURN (true); + else if (designSize < rangeStart || + designSize > rangeEnd || + subfamilyNameID < 256 || + subfamilyNameID > 32767) + return TRACE_RETURN (false); + else + return TRACE_RETURN (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_RETURN (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_RETURN (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_RETURN (u.size.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return TRACE_RETURN (u.stylisticSet.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return TRACE_RETURN (u.characterVariants.sanitize (c)); + return TRACE_RETURN (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 @@ -252,12 +497,55 @@ struct Feature unsigned int *lookup_tags /* OUT */) const { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c)); + 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) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) + return TRACE_RETURN (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_RETURN (false); + + if (likely (orig_offset.is_null ())) + return TRACE_RETURN (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_RETURN (false); + } + + return TRACE_RETURN (true); } - Offset featureParams; /* Offset to Feature Parameters table (if one + 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 */ @@ -289,6 +577,17 @@ 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 @@ -305,21 +604,55 @@ struct Lookup return flag; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (r); + } + return TRACE_RETURN (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_RETURN (false); + lookupType.set (lookup_type); + lookupFlag.set (lookup_props & 0xFFFFu); + if (unlikely (!subTable.serialize (c, num_subtables))) return TRACE_RETURN (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + USHORT &markFilteringSet = StructAfter<USHORT> (subTable); + markFilteringSet.set (lookup_props >> 16); + } + return TRACE_RETURN (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_RETURN (false); - if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet)) + if (lookupFlag & LookupFlag::UseMarkFilteringSet) { - USHORT &markFilteringSet = StructAfter<USHORT> (subTable); + const USHORT &markFilteringSet = StructAfter<USHORT> (subTable); if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false); } return TRACE_RETURN (true); } + private: USHORT lookupType; /* Different enumerations for GSUB and GPOS */ USHORT lookupFlag; /* Lookup qualifiers */ - ArrayOf<Offset> + 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 @@ -342,14 +675,28 @@ struct CoverageFormat1 private: inline unsigned int get_coverage (hb_codepoint_t glyph_id) const { - int i = glyphArray.search (glyph_id); - if (i != -1) - return i; - return NOT_COVERED; + int i = glyphArray.bsearch (glyph_id); + ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED); + return i; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + glyphArray.len.set (num_glyphs); + if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false); + for (unsigned int i = 0; i < num_glyphs; i++) + glyphArray[i] = glyphs[i]; + glyphs.advance (num_glyphs); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (glyphArray.sanitize (c)); } @@ -357,6 +704,15 @@ struct CoverageFormat1 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; } @@ -368,8 +724,9 @@ struct CoverageFormat1 const struct CoverageFormat1 *c; unsigned int i; }; - private: + + protected: USHORT coverageFormat; /* Format identifier--format = 1 */ SortedArrayOf<GlyphID> glyphArray; /* Array of GlyphIDs--in numerical order */ @@ -384,7 +741,7 @@ struct CoverageFormat2 private: inline unsigned int get_coverage (hb_codepoint_t glyph_id) const { - int i = rangeRecord.search (glyph_id); + int i = rangeRecord.bsearch (glyph_id); if (i != -1) { const RangeRecord &range = rangeRecord[i]; return (unsigned int) range.value + (glyph_id - range.start); @@ -392,8 +749,41 @@ struct CoverageFormat2 return NOT_COVERED; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + + if (unlikely (!num_glyphs)) return TRACE_RETURN (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_RETURN (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_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (rangeRecord.sanitize (c)); } @@ -412,6 +802,15 @@ struct CoverageFormat2 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_; @@ -437,8 +836,9 @@ struct CoverageFormat2 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 @@ -450,8 +850,6 @@ struct CoverageFormat2 struct Coverage { - inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); } - inline unsigned int get_coverage (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -461,8 +859,27 @@ struct Coverage } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (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_RETURN (u.format1.serialize (c, glyphs, num_glyphs)); + case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, num_glyphs)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { case 1: return TRACE_RETURN (u.format1.sanitize (c)); @@ -489,21 +906,30 @@ struct Coverage } } + 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: return u.format1.init (c_.u.format1); - case 2: return u.format2.init (c_.u.format2); - default:return; + 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 true; + default:return false; } } inline void next (void) { @@ -517,14 +943,14 @@ struct Coverage switch (format) { case 1: return u.format1.get_glyph (); case 2: return u.format2.get_glyph (); - default:return true; + default:return 0; } } inline uint16_t get_coverage (void) { switch (format) { case 1: return u.format1.get_coverage (); case 2: return u.format2.get_coverage (); - default:return true; + default:return -1; } } @@ -536,7 +962,7 @@ struct Coverage } u; }; - private: + protected: union { USHORT format; /* Format identifier */ CoverageFormat1 format1; @@ -558,24 +984,48 @@ struct ClassDefFormat1 private: inline unsigned int get_class (hb_codepoint_t glyph_id) const { - if ((unsigned int) (glyph_id - startGlyph) < classValue.len) - return classValue[glyph_id - startGlyph]; + 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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (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> @@ -591,25 +1041,51 @@ struct ClassDefFormat2 private: inline unsigned int get_class (hb_codepoint_t glyph_id) const { - int i = rangeRecord.search (glyph_id); - if (i != -1) + int i = rangeRecord.bsearch (glyph_id); + if (unlikely (i != -1)) return rangeRecord[i].value; return 0; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (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 @@ -620,8 +1096,6 @@ struct ClassDefFormat2 struct ClassDef { - inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); } - inline unsigned int get_class (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -631,8 +1105,9 @@ struct ClassDef } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { case 1: return TRACE_RETURN (u.format1.sanitize (c)); @@ -641,6 +1116,14 @@ struct ClassDef } } + 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); @@ -649,7 +1132,7 @@ struct ClassDef } } - private: + protected: union { USHORT format; /* Format identifier */ ClassDefFormat1 format1; @@ -681,7 +1164,7 @@ struct Device if (!pixels) return 0; - return pixels * (int64_t) scale / ppem; + return (int) (pixels * (int64_t) scale / ppem); } @@ -698,7 +1181,7 @@ struct Device unsigned int byte = deltaValue[s >> (4 - f)]; unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); - unsigned int mask = (0xFFFF >> (16 - (1 << f))); + unsigned int mask = (0xFFFFu >> (16 - (1 << f))); int delta = bits & mask; @@ -715,12 +1198,13 @@ struct Device return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f))); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ())); } - private: + 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 @@ -734,5 +1218,7 @@ struct Device }; +} /* namespace OT */ + #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */ diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh index f29fc14..7a6c04d 100644 --- a/src/hb-ot-layout-gdef-table.hh +++ b/src/hb-ot-layout-gdef-table.hh @@ -34,6 +34,8 @@ #include "hb-font-private.hh" +namespace OT { + /* * Attachment List Table @@ -49,7 +51,7 @@ struct AttachList unsigned int *point_count /* IN/OUT */, unsigned int *point_array /* OUT */) const { - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (index == NOT_COVERED) { if (point_count) @@ -69,12 +71,13 @@ struct AttachList return points.len; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); } - private: + protected: OffsetTo<Coverage> coverage; /* Offset to Coverage table -- from * beginning of AttachList table */ @@ -99,12 +102,13 @@ struct CaretValueFormat1 return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } - private: + protected: USHORT caretValueFormat; /* Format identifier--format = 1 */ SHORT coordinate; /* X or Y value, in design units */ public: @@ -119,18 +123,19 @@ struct CaretValueFormat2 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 (hb_font_get_glyph_contour_point_for_origin (font, glyph_id, caretValuePoint, direction, &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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } - private: + protected: USHORT caretValueFormat; /* Format identifier--format = 2 */ USHORT caretValuePoint; /* Contour point index on glyph */ public: @@ -148,12 +153,13 @@ struct CaretValueFormat3 font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this)); } - private: + protected: USHORT caretValueFormat; /* Format identifier--format = 3 */ SHORT coordinate; /* X or Y value, in design units */ OffsetTo<Device> @@ -176,8 +182,9 @@ struct CaretValue } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { case 1: return TRACE_RETURN (u.format1.sanitize (c)); @@ -187,7 +194,7 @@ struct CaretValue } } - private: + protected: union { USHORT format; /* Format identifier */ CaretValueFormat1 format1; @@ -217,12 +224,13 @@ struct LigGlyph return carets.len; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (carets.sanitize (c, this)); } - private: + protected: OffsetArrayOf<CaretValue> carets; /* Offset array of CaretValue tables * --from beginning of LigGlyph table @@ -240,7 +248,7 @@ struct LigCaretList unsigned int *caret_count /* IN/OUT */, hb_position_t *caret_array /* OUT */) const { - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (index == NOT_COVERED) { if (caret_count) @@ -251,12 +259,13 @@ struct LigCaretList return lig_glyph.get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); } - private: + protected: OffsetTo<Coverage> coverage; /* Offset to Coverage table--from * beginning of LigCaretList table */ @@ -273,14 +282,15 @@ struct MarkGlyphSetsFormat1 inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ - LongOffsetArrayOf<Coverage> + ArrayOf<OffsetTo<Coverage, ULONG> > coverage; /* Array of long offsets to mark set * coverage tables */ public: @@ -297,8 +307,9 @@ struct MarkGlyphSets } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { case 1: return TRACE_RETURN (u.format1.sanitize (c)); @@ -306,7 +317,7 @@ struct MarkGlyphSets } } - private: + protected: union { USHORT format; /* Format identifier */ MarkGlyphSetsFormat1 format1; @@ -322,7 +333,7 @@ struct MarkGlyphSets struct GDEF { - static const hb_tag_t Tag = HB_OT_TAG_GDEF; + static const hb_tag_t tableTag = HB_OT_TAG_GDEF; enum GlyphClasses { UnclassifiedGlyph = 0, @@ -335,6 +346,8 @@ struct GDEF 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 @@ -356,19 +369,20 @@ struct GDEF hb_position_t *caret_array /* OUT */) const { return (this+ligCaretList).get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array); } - inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002 && markGlyphSetsDef[0] != 0; } + inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef[0] != 0; } inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const - { return version.to_int () >= 0x00010002 && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); } + { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) && glyphClassDef.sanitize (c, this) && attachList.sanitize (c, this) && ligCaretList.sanitize (c, this) && markAttachClassDef.sanitize (c, this) && - (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this))); + (version.to_int () < 0x00010002u || markGlyphSetsDef[0].sanitize (c, this))); } @@ -379,22 +393,24 @@ struct GDEF { 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: - case UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; - case BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH; - case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE; - case ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT; + 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_CLASS_MARK | (klass << 8); + return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8); } } - private: + protected: FixedVersion version; /* Version of the GDEF table--currently - * 0x00010002 */ + * 0x00010002u */ OffsetTo<ClassDef> glyphClassDef; /* Offset to class definition table * for glyph type--from beginning of @@ -421,5 +437,7 @@ struct GDEF }; +} /* namespace OT */ + #endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */ diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh index 71c13a2..d88f787 100644 --- a/src/hb-ot-layout-gpos-table.hh +++ b/src/hb-ot-layout-gpos-table.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2010,2012 Google, Inc. + * Copyright © 2010,2012,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -32,6 +32,8 @@ #include "hb-ot-layout-gsubgpos-private.hh" +namespace OT { + /* buffer **position** var allocations */ #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */ @@ -47,18 +49,18 @@ typedef Value ValueRecord[VAR]; struct ValueFormat : USHORT { enum Flags { - xPlacement = 0x0001, /* Includes horizontal adjustment for placement */ - yPlacement = 0x0002, /* Includes vertical adjustment for placement */ - xAdvance = 0x0004, /* Includes horizontal adjustment for advance */ - yAdvance = 0x0008, /* Includes vertical adjustment for advance */ - xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */ - yPlaDevice = 0x0020, /* Includes vertical Device table for placement */ - xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */ - yAdvDevice = 0x0080, /* Includes vertical Device table for advance */ - ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */ - reserved = 0xF000, /* For future use */ - - devices = 0x00F0 /* Mask for having any Device table */ + 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. */ @@ -107,11 +109,13 @@ struct ValueFormat : USHORT 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++)); else values++; + 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++)); else values++; + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values)); + values++; } if (!has_device ()) return; @@ -123,22 +127,27 @@ struct ValueFormat : USHORT /* pixel -> fractional pixel */ if (format & xPlaDevice) { - if (x_ppem) glyph_pos.x_offset += (base + get_device (values++)).get_x_delta (font); else values++; + if (x_ppem) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font); + values++; } if (format & yPlaDevice) { - if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_y_delta (font); else values++; + if (y_ppem) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font); + values++; } if (format & xAdvDevice) { - if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (font); else values++; + if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font); + values++; } if (format & yAdvDevice) { /* y_advance values grow downward but font-space grows upward, hence negation */ - if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values++)).get_y_delta (font); else values++; + if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font); + values++; } } private: - inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) { + 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++; @@ -169,13 +178,15 @@ struct ValueFormat : USHORT return (format & devices) != 0; } - inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) { - TRACE_SANITIZE (); + inline bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); } - inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) { - TRACE_SANITIZE (); + 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_RETURN (false); @@ -192,8 +203,9 @@ struct ValueFormat : USHORT } /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ - inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) { - TRACE_SANITIZE (); + 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_RETURN (true); @@ -210,9 +222,6 @@ struct ValueFormat : USHORT struct AnchorFormat1 { - friend struct Anchor; - - private: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED, hb_position_t *x, hb_position_t *y) const { @@ -220,12 +229,13 @@ struct AnchorFormat1 *y = font->em_scale_y (yCoordinate); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ SHORT xCoordinate; /* Horizontal value--in design units */ SHORT yCoordinate; /* Vertical value--in design units */ @@ -235,29 +245,27 @@ struct AnchorFormat1 struct AnchorFormat2 { - friend struct Anchor; - - private: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id, hb_position_t *x, hb_position_t *y) const { unsigned int x_ppem = font->x_ppem; unsigned int y_ppem = font->y_ppem; hb_position_t cx, cy; - hb_bool_t ret = false; + hb_bool_t ret; - if (x_ppem || y_ppem) - ret = hb_font_get_glyph_contour_point_for_origin (font, glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); - *x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate); - *y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate); + 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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ SHORT xCoordinate; /* Horizontal value--in design units */ SHORT yCoordinate; /* Vertical value--in design units */ @@ -268,9 +276,6 @@ struct AnchorFormat2 struct AnchorFormat3 { - friend struct Anchor; - - private: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED, hb_position_t *x, hb_position_t *y) const { @@ -283,12 +288,13 @@ struct AnchorFormat3 *y += (this+yDeviceTable).get_x_delta (font); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 3 */ SHORT xCoordinate; /* Horizontal value--in design units */ SHORT yCoordinate; /* Vertical value--in design units */ @@ -318,8 +324,9 @@ struct Anchor } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { case 1: return TRACE_RETURN (u.format1.sanitize (c)); @@ -329,7 +336,7 @@ struct Anchor } } - private: + protected: union { USHORT format; /* Format identifier */ AnchorFormat1 format1; @@ -343,29 +350,32 @@ struct Anchor struct AnchorMatrix { - inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const { + 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); - return this+matrix[row * cols + col]; + *found = !matrixZ[row * cols + col].is_null (); + return this+matrixZ[row * cols + col]; } - inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); if (!c->check_struct (this)) return TRACE_RETURN (false); if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false); unsigned int count = rows * cols; - if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false); + if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return TRACE_RETURN (false); for (unsigned int i = 0; i < count; i++) - if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false); + if (!matrixZ[i].sanitize (c, this)) return TRACE_RETURN (false); return TRACE_RETURN (true); } USHORT rows; /* Number of rows */ - private: + protected: OffsetTo<Anchor> - matrix[VAR]; /* Matrix of offsets to Anchor tables-- + matrixZ[VAR]; /* Matrix of offsets to Anchor tables-- * from beginning of AnchorMatrix table */ public: - DEFINE_SIZE_ARRAY (2, matrix); + DEFINE_SIZE_ARRAY (2, matrixZ); }; @@ -373,12 +383,13 @@ struct MarkRecord { friend struct MarkArray; - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base)); } - private: + protected: USHORT klass; /* Class defined for this mark */ OffsetTo<Anchor> markAnchor; /* Offset to Anchor table--from @@ -394,29 +405,35 @@ struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage orde const AnchorMatrix &anchors, unsigned int class_count, unsigned int glyph_pos) const { - TRACE_APPLY (); + 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; - const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count); + 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_RETURN (false); hb_position_t mark_x, mark_y, base_x, base_y; - mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y); - glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y); + mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y); - hb_glyph_position_t &o = c->buffer->cur_pos(); + hb_glyph_position_t &o = buffer->cur_pos(); o.x_offset = base_x - mark_x; o.y_offset = base_y - mark_y; - o.attach_lookback() = c->buffer->idx - glyph_pos; + o.attach_lookback() = buffer->idx - glyph_pos; - c->buffer->idx++; + buffer->idx++; return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this)); } }; @@ -426,28 +443,40 @@ struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage orde struct SinglePosFormat1 { - friend struct SinglePos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (false); valueFormat.apply_value (c->font, c->direction, this, - values, c->buffer->cur_pos()); + values, buffer->cur_pos()); - c->buffer->idx++; + buffer->idx++; return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values)); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return TRACE_RETURN (c->check_struct (this) + && coverage.sanitize (c, this) + && valueFormat.sanitize_value (c, this, values)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -463,31 +492,43 @@ struct SinglePosFormat1 struct SinglePosFormat2 { - friend struct SinglePos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (false); if (likely (index >= valueCount)) return TRACE_RETURN (false); valueFormat.apply_value (c->font, c->direction, this, &values[index * valueFormat.get_len ()], - c->buffer->cur_pos()); + buffer->cur_pos()); - c->buffer->idx++; + buffer->idx++; return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount)); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return TRACE_RETURN (c->check_struct (this) + && coverage.sanitize (c, this) + && valueFormat.sanitize_values (c, this, values, valueCount)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -503,30 +544,19 @@ struct SinglePosFormat2 struct SinglePos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - case 2: return TRACE_RETURN (u.format2.apply (c)); - default:return TRACE_RETURN (false); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + case 2: return TRACE_RETURN (c->dispatch (u.format2)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - case 2: return TRACE_RETURN (u.format2.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ SinglePosFormat1 format1; @@ -539,7 +569,7 @@ struct PairValueRecord { friend struct PairSet; - private: + protected: GlyphID secondGlyph; /* GlyphID of second glyph in the * pair--first glyph is listed in the * Coverage table */ @@ -553,83 +583,126 @@ 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 (); + 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; - const PairValueRecord *record = CastP<PairValueRecord> (array); - for (unsigned int i = 0; i < count; i++) + + /* Hand-coded bsearch. */ + if (unlikely (!count)) + return TRACE_RETURN (false); + hb_codepoint_t x = buffer->info[pos].codepoint; + int min = 0, max = (int) count - 1; + while (min <= max) { - if (c->buffer->info[pos].codepoint == record->secondGlyph) + 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->font, c->direction, this, - &record->values[0], c->buffer->cur_pos()); + &record->values[0], buffer->cur_pos()); valueFormats[1].apply_value (c->font, c->direction, this, - &record->values[len1], c->buffer->pos[pos]); + &record->values[len1], buffer->pos[pos]); if (len2) pos++; - c->buffer->idx = pos; + buffer->idx = pos; return TRACE_RETURN (true); } - record = &StructAtOffset<PairValueRecord> (record, record_size); } return TRACE_RETURN (false); } struct sanitize_closure_t { - void *base; - ValueFormat *valueFormats; + 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) { - TRACE_SANITIZE (); + 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 (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false); + && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false); unsigned int count = len; - PairValueRecord *record = CastP<PairValueRecord> (array); + const PairValueRecord *record = CastP<PairValueRecord> (arrayZ); return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); } - private: + protected: USHORT len; /* Number of PairValueRecords */ - USHORT array[VAR]; /* Array of PairValueRecords--ordered + USHORT arrayZ[VAR]; /* Array of PairValueRecords--ordered * by GlyphID of the second glyph */ public: - DEFINE_SIZE_ARRAY (2, array); + DEFINE_SIZE_ARRAY (2, arrayZ); }; struct PairPosFormat1 { - friend struct PairPos; + 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, &valueFormat1); + } - private: - inline bool apply (hb_apply_context_t *c) const + inline const Coverage &get_coverage (void) const { - TRACE_APPLY (); - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); + return this+coverage; + } - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (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_RETURN (false); return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); unsigned int len1 = valueFormat1.get_len (); unsigned int len2 = valueFormat2.get_len (); @@ -643,7 +716,7 @@ struct PairPosFormat1 return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -663,43 +736,62 @@ struct PairPosFormat1 struct PairPosFormat2 { - friend struct PairPos; + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + TRACE_COLLECT_GLYPHS (this); + /* (this+coverage).add_coverage (c->input); // Don't need this. */ + + 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); + } - private: - inline bool apply (hb_apply_context_t *c) const + inline const Coverage &get_coverage (void) const { - TRACE_APPLY (); - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); + return this+coverage; + } - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (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_RETURN (false); unsigned int len1 = valueFormat1.get_len (); unsigned int len2 = valueFormat2.get_len (); unsigned int record_len = len1 + len2; - unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint); - unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint); + 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_RETURN (false); const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; valueFormat1.apply_value (c->font, c->direction, this, - v, c->buffer->cur_pos()); + v, buffer->cur_pos()); valueFormat2.apply_value (c->font, c->direction, this, - v + len1, c->buffer->pos[skippy_iter.idx]); + v + len1, buffer->pos[skippy_iter.idx]); - c->buffer->idx = skippy_iter.idx; + buffer->idx = skippy_iter.idx; if (len2) - c->buffer->idx++; + buffer->idx++; return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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) @@ -715,7 +807,7 @@ struct PairPosFormat2 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -747,30 +839,19 @@ struct PairPosFormat2 struct PairPos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - case 2: return TRACE_RETURN (u.format2.apply (c)); - default:return TRACE_RETURN (false); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + case 2: return TRACE_RETURN (c->dispatch (u.format2)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - case 2: return TRACE_RETURN (u.format2.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ PairPosFormat1 format1; @@ -783,12 +864,13 @@ struct EntryExitRecord { friend struct CursivePosFormat1; - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); } - private: + protected: OffsetTo<Anchor> entryAnchor; /* Offset to EntryAnchor table--from * beginning of CursivePos @@ -803,35 +885,43 @@ struct EntryExitRecord struct CursivePosFormat1 { - friend struct CursivePos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; /* We don't handle mark glyphs here. */ - if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false); + if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return TRACE_RETURN (false); - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); - - const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)]; + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; if (!this_record.exitAnchor) return TRACE_RETURN (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_RETURN (false); - const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)]; + const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; if (!next_record.entryAnchor) return TRACE_RETURN (false); - unsigned int i = c->buffer->idx; + 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->font, c->buffer->info[i].codepoint, &exit_x, &exit_y); - (this+next_record.entryAnchor).get_anchor (c->font, c->buffer->info[j].codepoint, &entry_x, &entry_y); + (this+this_record.exitAnchor).get_anchor (c->font, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+next_record.entryAnchor).get_anchor (c->font, buffer->info[j].codepoint, &entry_x, &entry_y); - hb_glyph_position_t *pos = c->buffer->pos; + hb_glyph_position_t *pos = buffer->pos; hb_position_t d; /* Main-direction adjustment */ @@ -884,16 +974,17 @@ struct CursivePosFormat1 pos[j].x_offset = exit_x - entry_x; } - c->buffer->idx = j; + buffer->idx = j; return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -907,28 +998,18 @@ struct CursivePosFormat1 struct CursivePos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - default:return TRACE_RETURN (false); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ CursivePosFormat1 format1; @@ -943,36 +1024,53 @@ typedef AnchorMatrix BaseArray; /* base-major-- struct MarkBasePosFormat1 { - friend struct MarkBasePos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint); + 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_RETURN (false); /* now we search backwards for a non-mark glyph */ - unsigned int property; - hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false); - - /* The following assertion is too strong, so we've disabled it. */ - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/} - - unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint); + 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_RETURN (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_RETURN (false);*/ } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); if (base_index == NOT_COVERED) return TRACE_RETURN (false); return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) && markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> markCoverage; /* Offset to MarkCoverage table--from @@ -993,28 +1091,18 @@ struct MarkBasePosFormat1 struct MarkBasePos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - default:return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ MarkBasePosFormat1 format1; @@ -1034,25 +1122,36 @@ typedef OffsetListOf<LigatureAttach> LigatureArray; struct MarkLigPosFormat1 { - friend struct MarkLigPos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint); + 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_RETURN (false); /* now we search backwards for a non-mark glyph */ - unsigned int property; - hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false); + 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_RETURN (false); - /* The following assertion is too strong, so we've disabled it. */ - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (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_RETURN (false);*/ } unsigned int j = skippy_iter.idx; - unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint); + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); if (lig_index == NOT_COVERED) return TRACE_RETURN (false); const LigatureArray& lig_array = this+ligatureArray; @@ -1062,32 +1161,30 @@ struct MarkLigPosFormat1 unsigned int comp_count = lig_attach.rows; if (unlikely (!comp_count)) return TRACE_RETURN (false); - unsigned int comp_index; /* We must now check whether the ligature ID of the current mark glyph * is identical to the ligature ID of the found ligature. If yes, we * can directly use the component index. If not, we attach the mark * glyph to the last component of the ligature. */ - if (get_lig_id (c->buffer->info[j]) && - get_lig_id (c->buffer->cur()) && - get_lig_comp (c->buffer->cur()) > 0) - { - comp_index = get_lig_comp (c->buffer->cur()) - 1; - if (comp_index >= comp_count) - comp_index = comp_count - 1; - } + 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_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) && markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> markCoverage; /* Offset to Mark Coverage table--from @@ -1109,28 +1206,18 @@ struct MarkLigPosFormat1 struct MarkLigPos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - default:return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ MarkLigPosFormat1 format1; @@ -1145,46 +1232,71 @@ typedef AnchorMatrix Mark2Array; /* mark2-major-- struct MarkMarkPosFormat1 { - friend struct MarkMarkPos; + 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; + } - private: inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint); + 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_RETURN (false); /* now we search backwards for a suitable mark glyph until a non-mark glyph */ - unsigned int property; - hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); - if (!skippy_iter.prev (&property)) return TRACE_RETURN (false); + 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_RETURN (false); - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false); + if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return TRACE_RETURN (false); } unsigned int j = skippy_iter.idx; - /* Two marks match only if they belong to the same base, or same component - * of the same ligature. That is, the component numbers must match, and - * if those are non-zero, the ligid number should also match. */ - if ((get_lig_comp (c->buffer->cur())) || - (get_lig_comp (c->buffer->info[j]) > 0 && - get_lig_id (c->buffer->cur()))) - return TRACE_RETURN (false); + unsigned int 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_RETURN (false); - unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint); + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); if (mark2_index == NOT_COVERED) return TRACE_RETURN (false); return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) && mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this) && mark2Array.sanitize (c, this, (unsigned int) classCount)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> mark1Coverage; /* Offset to Combining Mark1 Coverage @@ -1207,28 +1319,18 @@ struct MarkMarkPosFormat1 struct MarkMarkPos { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - default:return TRACE_RETURN (false); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ MarkMarkPosFormat1 format1; @@ -1236,48 +1338,13 @@ struct MarkMarkPos }; -static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index); - -struct ContextPos : Context -{ - friend struct PosLookupSubTable; +struct ContextPos : Context {}; - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return TRACE_RETURN (Context::apply (c, position_lookup)); - } -}; +struct ChainContextPos : ChainContext {}; -struct ChainContextPos : ChainContext +struct ExtensionPos : Extension<ExtensionPos> { - friend struct PosLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return TRACE_RETURN (ChainContext::apply (c, position_lookup)); - } -}; - - -struct ExtensionPos : Extension -{ - friend struct PosLookupSubTable; - - private: - inline const struct PosLookupSubTable& get_subtable (void) const - { - unsigned int offset = get_offset (); - if (unlikely (!offset)) return Null(PosLookupSubTable); - return StructAtOffset<PosLookupSubTable> (this, offset); - } - - inline bool apply (hb_apply_context_t *c) const; - - inline bool sanitize (hb_sanitize_context_t *c); + typedef struct PosLookupSubTable LookupSubTable; }; @@ -1303,40 +1370,27 @@ struct PosLookupSubTable Extension = 9 }; - inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const { - TRACE_APPLY (); - switch (lookup_type) { - case Single: return TRACE_RETURN (u.single.apply (c)); - case Pair: return TRACE_RETURN (u.pair.apply (c)); - case Cursive: return TRACE_RETURN (u.cursive.apply (c)); - case MarkBase: return TRACE_RETURN (u.markBase.apply (c)); - case MarkLig: return TRACE_RETURN (u.markLig.apply (c)); - case MarkMark: return TRACE_RETURN (u.markMark.apply (c)); - case Context: return TRACE_RETURN (u.c.apply (c)); - case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); - case Extension: return TRACE_RETURN (u.extension.apply (c)); - default: return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { - TRACE_SANITIZE (); + TRACE_DISPATCH (this, lookup_type); + /* The sub_format passed to may_dispatch is unnecessary but harmless. */ + if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ()); switch (lookup_type) { - case Single: return TRACE_RETURN (u.single.sanitize (c)); - case Pair: return TRACE_RETURN (u.pair.sanitize (c)); - case Cursive: return TRACE_RETURN (u.cursive.sanitize (c)); - case MarkBase: return TRACE_RETURN (u.markBase.sanitize (c)); - case MarkLig: return TRACE_RETURN (u.markLig.sanitize (c)); - case MarkMark: return TRACE_RETURN (u.markMark.sanitize (c)); - case Context: return TRACE_RETURN (u.c.sanitize (c)); - case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); - case Extension: return TRACE_RETURN (u.extension.sanitize (c)); - default: return TRACE_RETURN (true); + case Single: return TRACE_RETURN (u.single.dispatch (c)); + case Pair: return TRACE_RETURN (u.pair.dispatch (c)); + case Cursive: return TRACE_RETURN (u.cursive.dispatch (c)); + case MarkBase: return TRACE_RETURN (u.markBase.dispatch (c)); + case MarkLig: return TRACE_RETURN (u.markLig.dispatch (c)); + case MarkMark: return TRACE_RETURN (u.markMark.dispatch (c)); + case Context: return TRACE_RETURN (u.context.dispatch (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.dispatch (c)); + case Extension: return TRACE_RETURN (u.extension.dispatch (c)); + default: return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT sub_format; SinglePos single; @@ -1345,7 +1399,7 @@ struct PosLookupSubTable MarkBasePos markBase; MarkLigPos markLig; MarkMarkPos markMark; - ContextPos c; + ContextPos context; ChainContextPos chainContext; ExtensionPos extension; } u; @@ -1357,48 +1411,47 @@ struct PosLookupSubTable struct PosLookup : Lookup { inline const PosLookupSubTable& get_subtable (unsigned int i) const - { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; } + { return Lookup::get_subtable<PosLookupSubTable> (i); } - inline bool apply_once (hb_apply_context_t *c) const + inline bool is_reverse (void) const { - unsigned int lookup_type = get_type (); - - if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property)) - return false; - - for (unsigned int i = 0; i < get_subtable_count (); i++) - if (get_subtable (i).apply (c, lookup_type)) - return true; - return false; } - inline bool apply_string (hb_apply_context_t *c) const + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + return TRACE_RETURN (dispatch (c)); + } + + inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { - bool ret = false; + TRACE_COLLECT_GLYPHS (this); + return TRACE_RETURN (dispatch (c)); + } - if (unlikely (!c->buffer->len)) - return false; + template <typename set_t> + inline void add_coverage (set_t *glyphs) const + { + hb_add_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } - c->set_lookup (*this); + static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index); - c->buffer->idx = 0; - while (c->buffer->idx < c->buffer->len) - { - if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) - ret = true; - else - c->buffer->idx++; - } + template <typename context_t> + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - return ret; - } + 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) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); - OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable); - return TRACE_RETURN (list.sanitize (c, this, get_type ())); + const OffsetArrayOf<PosLookupSubTable> &list = get_subtables<PosLookupSubTable> (); + return TRACE_RETURN (dispatch (c)); } }; @@ -1410,21 +1463,19 @@ typedef OffsetListOf<PosLookup> PosLookupList; struct GPOS : GSUBGPOS { - static const hb_tag_t Tag = HB_OT_TAG_GPOS; + 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)); } - inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const - { return get_lookup (lookup_index).apply_string (c); } + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer); - static inline void position_start (hb_buffer_t *buffer); - static inline void position_finish (hb_buffer_t *buffer); - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); - OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList); + const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList); return TRACE_RETURN (list.sanitize (c, this)); } public: @@ -1435,20 +1486,20 @@ struct GPOS : GSUBGPOS static void fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction) { - unsigned int j = pos[i].cursive_chain(); - if (likely (!j)) - return; + unsigned int j = pos[i].cursive_chain(); + if (likely (!j)) + return; - j += i; + j += i; - pos[i].cursive_chain() = 0; + pos[i].cursive_chain() = 0; - fix_cursive_minor_offset (pos, j, direction); + fix_cursive_minor_offset (pos, j, direction); - if (HB_DIRECTION_IS_HORIZONTAL (direction)) - pos[i].y_offset += pos[j].y_offset; - else - pos[i].x_offset += pos[j].x_offset; + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; } static void @@ -1459,8 +1510,6 @@ fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t di unsigned int j = i - pos[i].attach_lookback(); - pos[i].x_advance = 0; - pos[i].y_advance = 0; pos[i].x_offset += pos[j].x_offset; pos[i].y_offset += pos[j].y_offset; @@ -1477,7 +1526,7 @@ fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t di } void -GPOS::position_start (hb_buffer_t *buffer) +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) { buffer->clear_positions (); @@ -1487,8 +1536,10 @@ GPOS::position_start (hb_buffer_t *buffer) } void -GPOS::position_finish (hb_buffer_t *buffer) +GPOS::position_finish (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; @@ -1500,42 +1551,28 @@ GPOS::position_finish (hb_buffer_t *buffer) /* Handle attachments */ for (unsigned int i = 0; i < len; i++) fix_mark_attachment (pos, i, direction); - - HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); - HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props); - HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache); } /* Out-of-class implementation for methods recursing */ -inline bool ExtensionPos::apply (hb_apply_context_t *c) const +template <typename context_t> +/*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) { - TRACE_APPLY (); - return TRACE_RETURN (get_subtable ().apply (c, get_type ())); -} - -inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c) -{ - TRACE_SANITIZE (); - if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); - unsigned int offset = get_offset (); - if (unlikely (!offset)) return TRACE_RETURN (true); - return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ())); + 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 position_lookup (hb_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) { - const GPOS &gpos = *(c->face->ot_layout->gpos); + const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); const PosLookup &l = gpos.get_lookup (lookup_index); - - if (unlikely (c->nesting_level_left == 0)) - return false; - - hb_apply_context_t new_c (*c); - new_c.nesting_level_left--; - new_c.set_lookup (l); - return l.apply_once (&new_c); + unsigned int saved_lookup_props = c->lookup_props; + c->set_lookup (l); + bool ret = l.dispatch (c); + c->set_lookup_props (saved_lookup_props); + return ret; } @@ -1543,5 +1580,7 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i #undef cursive_chain +} /* namespace OT */ + #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */ diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh index f6a7575..ebe4c9e 100644 --- a/src/hb-ot-layout-gsub-table.hh +++ b/src/hb-ot-layout-gsub-table.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2010,2012 Google, Inc. + * Copyright © 2010,2012,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -32,50 +32,78 @@ #include "hb-ot-layout-gsubgpos-private.hh" +namespace OT { + struct SingleSubstFormat1 { - friend struct SingleSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); Coverage::Iter iter; for (iter.init (this+coverage); iter.more (); iter.next ()) { hb_codepoint_t glyph_id = iter.get_glyph (); if (c->glyphs->has (glyph_id)) - c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF); + 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 ()) { + hb_codepoint_t glyph_id = iter.get_glyph (); + c->input->add (glyph_id); + c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu); } } - inline bool would_apply (hb_codepoint_t glyph_id) const + inline const Coverage &get_coverage (void) const { - return (this+coverage) (glyph_id) != NOT_COVERED; + return this+coverage; + } + + inline bool would_apply (hb_would_apply_context_t *c) const + { + TRACE_WOULD_APPLY (this); + return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); hb_codepoint_t glyph_id = c->buffer->cur().codepoint; - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); /* According to the Adobe Annotated OpenType Suite, result is always * limited to 16bit. */ - glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF; + glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu; c->replace_glyph (glyph_id); return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); + deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */ + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -88,13 +116,9 @@ struct SingleSubstFormat1 struct SingleSubstFormat2 { - friend struct SingleSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); Coverage::Iter iter; for (iter.init (this+coverage); iter.more (); iter.next ()) { if (c->glyphs->has (iter.get_glyph ())) @@ -102,16 +126,32 @@ struct SingleSubstFormat2 } } - inline bool would_apply (hb_codepoint_t glyph_id) const + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { - return (this+coverage) (glyph_id) != NOT_COVERED; + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + 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_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); hb_codepoint_t glyph_id = c->buffer->cur().codepoint; - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); if (unlikely (index >= substitute.len)) return TRACE_RETURN (false); @@ -122,12 +162,25 @@ struct SingleSubstFormat2 return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -141,50 +194,46 @@ struct SingleSubstFormat2 struct SingleSubst { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const + inline bool serialize (hb_serialize_context_t *c, + Supplier<GlyphID> &glyphs, + Supplier<GlyphID> &substitutes, + unsigned int num_glyphs) { - TRACE_CLOSURE (); - switch (u.format) { - case 1: u.format1.closure (c); break; - case 2: u.format2.closure (c); break; - default: break; - } - } - - inline bool would_apply (hb_codepoint_t glyph_id) const - { - switch (u.format) { - case 1: return u.format1.would_apply (glyph_id); - case 2: return u.format2.would_apply (glyph_id); - default:return false; + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (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; + } } - } - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); + u.format.set (format); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - case 2: return TRACE_RETURN (u.format2.apply (c)); + case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs, delta)); + case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, substitutes, num_glyphs)); default:return TRACE_RETURN (false); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (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))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - case 2: return TRACE_RETURN (u.format2.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + case 2: return TRACE_RETURN (c->dispatch (u.format2)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ SingleSubstFormat1 format1; @@ -195,36 +244,74 @@ struct SingleSubst struct Sequence { - friend struct MultipleSubstFormat1; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + 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 (); - if (unlikely (!substitute.len)) return TRACE_RETURN (false); + TRACE_APPLY (this); + unsigned int count = substitute.len; - unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0; - c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass); + /* TODO: + * Testing shows that Uniscribe actually allows zero-len susbstitute, + * which essentially deletes a glyph. We don't allow for now. It + * can be confusing to the client since the cluster from the deleted + * glyph won't be merged with any output cluster... Also, currently + * buffer->move_to() makes assumptions about this too. Perhaps fix + * in the future after figuring out what to do with the clusters. + */ + if (unlikely (!count)) return TRACE_RETURN (false); + + /* 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_RETURN (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_RETURN (true); } - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (substitute.sanitize (c)); } - private: + protected: ArrayOf<GlyphID> substitute; /* String of GlyphIDs to substitute */ public: @@ -233,13 +320,9 @@ struct Sequence struct MultipleSubstFormat1 { - friend struct MultipleSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); Coverage::Iter iter; for (iter.init (this+coverage); iter.more (); iter.next ()) { if (c->glyphs->has (iter.get_glyph ())) @@ -247,27 +330,61 @@ struct MultipleSubstFormat1 } } - inline bool would_apply (hb_codepoint_t glyph_id) const + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { - return (this+coverage) (glyph_id) != NOT_COVERED; + 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_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); return TRACE_RETURN ((this+sequence[index]).apply (c)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (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_RETURN (false); + substitute_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -281,46 +398,34 @@ struct MultipleSubstFormat1 struct MultipleSubst { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const + 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_CLOSURE (); + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false); + unsigned int format = 1; + u.format.set (format); switch (u.format) { - case 1: u.format1.closure (c); break; - default: break; - } - } - - inline bool would_apply (hb_codepoint_t glyph_id) const - { - switch (u.format) { - case 1: return u.format1.would_apply (glyph_id); - default:return false; - } - } - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); + case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list)); default:return TRACE_RETURN (false); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (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))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ MultipleSubstFormat1 format1; @@ -333,13 +438,9 @@ typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in struct AlternateSubstFormat1 { - friend struct AlternateSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); Coverage::Iter iter; for (iter.init (this+coverage); iter.more (); iter.next ()) { if (c->glyphs->has (iter.get_glyph ())) { @@ -351,17 +452,36 @@ struct AlternateSubstFormat1 } } - inline bool would_apply (hb_codepoint_t glyph_id) const + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { - return (this+coverage) (glyph_id) != NOT_COVERED; + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + 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_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); hb_codepoint_t glyph_id = c->buffer->cur().codepoint; - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const AlternateSet &alt_set = this+alternateSet[index]; @@ -384,12 +504,31 @@ struct AlternateSubstFormat1 return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (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_RETURN (false); + alternate_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -403,46 +542,34 @@ struct AlternateSubstFormat1 struct AlternateSubst { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const - { - TRACE_CLOSURE (); - switch (u.format) { - case 1: u.format1.closure (c); break; - default: break; - } - } - - inline bool would_apply (hb_codepoint_t glyph_id) const - { - switch (u.format) { - case 1: return u.format1.would_apply (glyph_id); - default:return false; - } - } - - inline bool apply (hb_apply_context_t *c) const + 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_APPLY (); + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false); + unsigned int format = 1; + u.format.set (format); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); + case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list)); default:return TRACE_RETURN (false); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (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))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ AlternateSubstFormat1 format1; @@ -452,13 +579,9 @@ struct AlternateSubst struct Ligature { - friend struct LigatureSet; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); unsigned int count = component.len; for (unsigned int i = 1; i < count; i++) if (!c->glyphs->has (component[i])) @@ -466,78 +589,90 @@ struct Ligature c->glyphs->add (ligGlyph); } - inline bool would_apply (hb_codepoint_t second) const + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { - return component.len == 2 && component[1] == second; + 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 apply (hb_apply_context_t *c) const + inline bool would_apply (hb_would_apply_context_t *c) const { - TRACE_APPLY (); - unsigned int count = component.len; - if (unlikely (count < 2)) return TRACE_RETURN (false); - - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1); - if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); + TRACE_WOULD_APPLY (this); + if (c->len != component.len) + return TRACE_RETURN (false); - bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); - bool found_non_mark = false; + for (unsigned int i = 1; i < c->len; i++) + if (likely (c->glyphs[i] != component[i])) + return TRACE_RETURN (false); - for (unsigned int i = 1; i < count; i++) - { - unsigned int property; + return TRACE_RETURN (true); + } - if (!skippy_iter.next (&property)) return TRACE_RETURN (false); + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = component.len; - found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); + if (unlikely (!count)) return TRACE_RETURN (false); - if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (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_RETURN (true); } - unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0; - - /* Allocate new ligature id */ - unsigned int lig_id = allocate_lig_id (c->buffer); - set_lig_props (c->buffer->cur(), lig_id, 0); + bool is_mark_ligature = false; + unsigned int total_component_count = 0; - if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */ - { - c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass); - } - else - { - c->replace_glyph (ligGlyph); + unsigned int match_length = 0; + unsigned int match_positions[MAX_CONTEXT_LENGTH]; - /* Now we must do a second loop to copy the skipped glyphs to - `out' and assign component values to it. We start with the - glyph after the first component. Glyphs between component - i and i+1 belong to component i. Together with the lig_id - value it is later possible to check whether a specific - component value really belongs to a given ligature. */ + if (likely (!match_input (c, count, + &component[1], + match_glyph, + NULL, + &match_length, + match_positions, + &is_mark_ligature, + &total_component_count))) + return TRACE_RETURN (false); - for (unsigned int i = 1; i < count; i++) - { - while (c->should_mark_skip_current_glyph ()) - { - set_lig_props (c->buffer->cur(), lig_id, i); - c->replace_glyph (c->buffer->cur().codepoint); - } + ligate_input (c, + count, + match_positions, + match_length, + ligGlyph, + is_mark_ligature, + total_component_count); - /* Skip the base glyph */ - c->buffer->idx++; - } - } + return TRACE_RETURN (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_RETURN (false); + ligGlyph = ligature; + if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false); return TRACE_RETURN (true); } public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c)); } - private: + protected: GlyphID ligGlyph; /* GlyphID of ligature to substitute */ HeadlessArrayOf<GlyphID> component; /* Array of component GlyphIDs--start @@ -549,33 +684,38 @@ struct Ligature struct LigatureSet { - friend struct LigatureSubstFormat1; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); unsigned int num_ligs = ligature.len; for (unsigned int i = 0; i < num_ligs; i++) (this+ligature[i]).closure (c); } - inline bool would_apply (hb_codepoint_t second) const + 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 (second)) - return true; + if (lig.would_apply (c)) + return TRACE_RETURN (true); } - return false; + return TRACE_RETURN (false); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); unsigned int num_ligs = ligature.len; for (unsigned int i = 0; i < num_ligs; i++) { @@ -586,13 +726,32 @@ struct LigatureSet return TRACE_RETURN (false); } - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!ligature.serialize (c, num_ligatures))) return TRACE_RETURN (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_RETURN (false); + ligatures.advance (num_ligatures); + component_count_list.advance (num_ligatures); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (ligature.sanitize (c, this)); } - private: + protected: OffsetArrayOf<Ligature> ligature; /* Array LigatureSet tables * ordered by preference */ @@ -602,13 +761,9 @@ struct LigatureSet struct LigatureSubstFormat1 { - friend struct LigatureSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); Coverage::Iter iter; for (iter.init (this+coverage); iter.more (); iter.next ()) { if (c->glyphs->has (iter.get_glyph ())) @@ -616,31 +771,72 @@ struct LigatureSubstFormat1 } } - inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { - unsigned int index; - return (index = (this+coverage) (first)) != NOT_COVERED && - (this+ligatureSet[index]).would_apply (second); + TRACE_COLLECT_GLYPHS (this); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + 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_RETURN (false); + + const LigatureSet &lig_set = this+ligatureSet[index]; + return TRACE_RETURN (lig_set.would_apply (c)); } inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); hb_codepoint_t glyph_id = c->buffer->cur().codepoint; - unsigned int index = (this+coverage) (glyph_id); + unsigned int index = (this+coverage).get_coverage (glyph_id); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const LigatureSet &lig_set = this+ligatureSet[index]; return TRACE_RETURN (lig_set.apply (c)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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_RETURN (false); + if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (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_RETURN (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_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -654,46 +850,37 @@ struct LigatureSubstFormat1 struct LigatureSubst { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const - { - TRACE_CLOSURE (); - switch (u.format) { - case 1: u.format1.closure (c); break; - default: break; - } - } - - inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + 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_RETURN (false); + unsigned int format = 1; + u.format.set (format); switch (u.format) { - case 1: return u.format1.would_apply (first, second); - default:return false; - } - } - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); + case 1: return TRACE_RETURN (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_RETURN (false); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (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))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ LigatureSubstFormat1 format1; @@ -701,68 +888,13 @@ struct LigatureSubst }; -static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); -static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index); - -struct ContextSubst : Context -{ - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const - { - TRACE_CLOSURE (); - return Context::closure (c, closure_lookup); - } +struct ContextSubst : Context {}; - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return TRACE_RETURN (Context::apply (c, substitute_lookup)); - } -}; +struct ChainContextSubst : ChainContext {}; -struct ChainContextSubst : ChainContext +struct ExtensionSubst : Extension<ExtensionSubst> { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const - { - TRACE_CLOSURE (); - return ChainContext::closure (c, closure_lookup); - } - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return TRACE_RETURN (ChainContext::apply (c, substitute_lookup)); - } -}; - - -struct ExtensionSubst : Extension -{ - friend struct SubstLookupSubTable; - friend struct SubstLookup; - - private: - inline const struct SubstLookupSubTable& get_subtable (void) const - { - unsigned int offset = get_offset (); - if (unlikely (!offset)) return Null(SubstLookupSubTable); - return StructAtOffset<SubstLookupSubTable> (this, offset); - } - - inline void closure (hb_closure_context_t *c) const; - inline bool would_apply (hb_codepoint_t glyph_id) const; - inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const; - - inline bool apply (hb_apply_context_t *c) const; - - inline bool sanitize (hb_sanitize_context_t *c); + typedef struct SubstLookupSubTable LookupSubTable; inline bool is_reverse (void) const; }; @@ -770,13 +902,9 @@ struct ExtensionSubst : Extension struct ReverseChainSingleSubstFormat1 { - friend struct ReverseChainSingleSubst; - - private: - inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); unsigned int count; @@ -799,13 +927,48 @@ struct ReverseChainSingleSubstFormat1 } } + 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_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); + } + inline bool apply (hb_apply_context_t *c) const { - TRACE_APPLY (); + TRACE_APPLY (this); if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL)) return TRACE_RETURN (false); /* No chaining to this type */ - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); @@ -819,26 +982,29 @@ struct ReverseChainSingleSubstFormat1 match_coverage, this, 1)) { - c->buffer->cur().codepoint = substitute[index]; - c->buffer->idx--; /* Reverse! */ + 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_RETURN (true); } return TRACE_RETURN (false); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) return TRACE_RETURN (false); - OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false); - ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); + const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); return TRACE_RETURN (substitute.sanitize (c)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -860,38 +1026,18 @@ struct ReverseChainSingleSubstFormat1 struct ReverseChainSingleSubst { - friend struct SubstLookupSubTable; - - private: - - inline void closure (hb_closure_context_t *c) const - { - TRACE_CLOSURE (); - switch (u.format) { - case 1: u.format1.closure (c); break; - default: break; - } - } - - inline bool apply (hb_apply_context_t *c) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_APPLY (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c)); - default:return TRACE_RETURN (false); + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ ReverseChainSingleSubstFormat1 format1; @@ -919,84 +1065,33 @@ struct SubstLookupSubTable ReverseChainSingle = 8 }; - inline void closure (hb_closure_context_t *c, - unsigned int lookup_type) const - { - TRACE_CLOSURE (); - switch (lookup_type) { - case Single: u.single.closure (c); break; - case Multiple: u.multiple.closure (c); break; - case Alternate: u.alternate.closure (c); break; - case Ligature: u.ligature.closure (c); break; - case Context: u.c.closure (c); break; - case ChainContext: u.chainContext.closure (c); break; - case Extension: u.extension.closure (c); break; - case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break; - default: break; - } - } - - inline bool would_apply (hb_codepoint_t glyph_id, - unsigned int lookup_type) const - { - switch (lookup_type) { - case Single: return u.single.would_apply (glyph_id); - case Multiple: return u.multiple.would_apply (glyph_id); - case Alternate: return u.alternate.would_apply (glyph_id); - case Extension: return u.extension.would_apply (glyph_id); - default: return false; - } - } - inline bool would_apply (hb_codepoint_t first, - hb_codepoint_t second, - unsigned int lookup_type) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const { + TRACE_DISPATCH (this, lookup_type); + /* The sub_format passed to may_dispatch is unnecessary but harmless. */ + if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ()); switch (lookup_type) { - case Ligature: return u.ligature.would_apply (first, second); - case Extension: return u.extension.would_apply (first, second); - default: return false; + case Single: return TRACE_RETURN (u.single.dispatch (c)); + case Multiple: return TRACE_RETURN (u.multiple.dispatch (c)); + case Alternate: return TRACE_RETURN (u.alternate.dispatch (c)); + case Ligature: return TRACE_RETURN (u.ligature.dispatch (c)); + case Context: return TRACE_RETURN (u.context.dispatch (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.dispatch (c)); + case Extension: return TRACE_RETURN (u.extension.dispatch (c)); + case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.dispatch (c)); + default: return TRACE_RETURN (c->default_return_value ()); } } - inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const - { - TRACE_APPLY (); - switch (lookup_type) { - case Single: return TRACE_RETURN (u.single.apply (c)); - case Multiple: return TRACE_RETURN (u.multiple.apply (c)); - case Alternate: return TRACE_RETURN (u.alternate.apply (c)); - case Ligature: return TRACE_RETURN (u.ligature.apply (c)); - case Context: return TRACE_RETURN (u.c.apply (c)); - case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); - case Extension: return TRACE_RETURN (u.extension.apply (c)); - case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c)); - default: return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { - TRACE_SANITIZE (); - switch (lookup_type) { - case Single: return TRACE_RETURN (u.single.sanitize (c)); - case Multiple: return TRACE_RETURN (u.multiple.sanitize (c)); - case Alternate: return TRACE_RETURN (u.alternate.sanitize (c)); - case Ligature: return TRACE_RETURN (u.ligature.sanitize (c)); - case Context: return TRACE_RETURN (u.c.sanitize (c)); - case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); - case Extension: return TRACE_RETURN (u.extension.sanitize (c)); - case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c)); - default: return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT sub_format; SingleSubst single; MultipleSubst multiple; AlternateSubst alternate; LigatureSubst ligature; - ContextSubst c; + ContextSubst context; ChainContextSubst chainContext; ExtensionSubst extension; ReverseChainSingleSubst reverseChainContextSingle; @@ -1009,7 +1104,7 @@ struct SubstLookupSubTable struct SubstLookup : Lookup { inline const SubstLookupSubTable& get_subtable (unsigned int i) const - { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } + { return Lookup::get_subtable<SubstLookupSubTable> (i); } inline static bool lookup_type_is_reverse (unsigned int lookup_type) { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } @@ -1022,110 +1117,126 @@ struct SubstLookup : Lookup return lookup_type_is_reverse (type); } - inline void closure (hb_closure_context_t *c) const + inline bool apply (hb_apply_context_t *c) const { - unsigned int lookup_type = get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 0; i < count; i++) - get_subtable (i).closure (c, lookup_type); + TRACE_APPLY (this); + return TRACE_RETURN (dispatch (c)); } - inline bool would_apply (hb_codepoint_t glyph_id) const + inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const { - unsigned int lookup_type = get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 0; i < count; i++) - if (get_subtable (i).would_apply (glyph_id, lookup_type)) - return true; - return false; + TRACE_CLOSURE (this); + c->set_recurse_func (dispatch_recurse_func<hb_closure_context_t>); + return TRACE_RETURN (dispatch (c)); } - inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + + inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { - unsigned int lookup_type = get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 0; i < count; i++) - if (get_subtable (i).would_apply (first, second, lookup_type)) - return true; - return false; + TRACE_COLLECT_GLYPHS (this); + c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>); + return TRACE_RETURN (dispatch (c)); } - inline bool apply_once (hb_apply_context_t *c) const + template <typename set_t> + inline void add_coverage (set_t *glyphs) const { - unsigned int lookup_type = get_type (); + hb_add_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } - if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property)) - return false; + 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_RETURN (false); + if (!accel->may_have (c->glyphs[0])) return TRACE_RETURN (false); + return TRACE_RETURN (dispatch (c)); + } - if (unlikely (lookup_type == SubstLookupSubTable::Extension)) - { - /* The spec says all subtables should have the same type. - * This is specially important if one has a reverse type! - * - * This is rather slow to do this here for every glyph, - * but it's easiest, and who uses extension lookups anyway?!*/ - unsigned int type = get_subtable(0).u.extension.get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 1; i < count; i++) - if (get_subtable(i).u.extension.get_type () != type) - return false; - } + static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index); - unsigned int count = get_subtable_count (); - for (unsigned int i = 0; i < count; i++) - if (get_subtable (i).apply (c, lookup_type)) - return true; + inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c, + unsigned int i) + { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); } - return false; + 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_RETURN (false); + return TRACE_RETURN (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs)); } - inline bool apply_string (hb_apply_context_t *c) const + 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_RETURN (false); + return TRACE_RETURN (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) { - bool ret = false; + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return TRACE_RETURN (false); + return TRACE_RETURN (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_RETURN (false); + return TRACE_RETURN (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)); + } - if (unlikely (!c->buffer->len)) - return false; + template <typename context_t> + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - c->set_lookup (*this); + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const + { return Lookup::dispatch<SubstLookupSubTable> (c); } - if (likely (!is_reverse ())) - { - /* in/out forward substitution */ - c->buffer->clear_output (); - c->buffer->idx = 0; - while (c->buffer->idx < c->buffer->len) - { - if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) - ret = true; - else - c->buffer->next_glyph (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); + const OffsetArrayOf<SubstLookupSubTable> &list = get_subtables<SubstLookupSubTable> (); + if (unlikely (!dispatch (c))) return TRACE_RETURN (false); - } - if (ret) - c->buffer->swap_buffers (); - } - else + if (unlikely (get_type () == SubstLookupSubTable::Extension)) { - /* in-place backward substitution */ - c->buffer->idx = c->buffer->len - 1; - do - { - if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) - ret = true; - else - c->buffer->idx--; - - } - while ((int) c->buffer->idx >= 0); + /* 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_RETURN (false); } - - return ret; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); - OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable); - return TRACE_RETURN (list.sanitize (c, this, get_type ())); + return TRACE_RETURN (true); } }; @@ -1137,25 +1248,19 @@ typedef OffsetListOf<SubstLookup> SubstLookupList; struct GSUB : GSUBGPOS { - static const hb_tag_t Tag = HB_OT_TAG_GSUB; + 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)); } - inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const - { return get_lookup (lookup_index).apply_string (c); } - - static inline void substitute_start (hb_buffer_t *buffer); - static inline void substitute_finish (hb_buffer_t *buffer); + static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer); - inline void closure_lookup (hb_closure_context_t *c, - unsigned int lookup_index) const - { return get_lookup (lookup_index).closure (c); } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); - OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); + const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); return TRACE_RETURN (list.sanitize (c, this)); } public: @@ -1164,90 +1269,57 @@ struct GSUB : GSUBGPOS void -GSUB::substitute_start (hb_buffer_t *buffer) +GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer) { - HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); - HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); - HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + _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++) - buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0; + { + _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; + } } void -GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED) +GSUB::substitute_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) { } /* Out-of-class implementation for methods recursing */ -inline void ExtensionSubst::closure (hb_closure_context_t *c) const -{ - get_subtable ().closure (c, get_type ()); -} - -inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const -{ - return get_subtable ().would_apply (glyph_id, get_type ()); -} - -inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const -{ - return get_subtable ().would_apply (first, second, get_type ()); -} - -inline bool ExtensionSubst::apply (hb_apply_context_t *c) const -{ - TRACE_APPLY (); - return TRACE_RETURN (get_subtable ().apply (c, get_type ())); -} - -inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) -{ - TRACE_SANITIZE (); - if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); - unsigned int offset = get_offset (); - if (unlikely (!offset)) return TRACE_RETURN (true); - return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ())); -} - -inline bool ExtensionSubst::is_reverse (void) const +/*static*/ inline bool ExtensionSubst::is_reverse (void) const { unsigned int type = get_type (); if (unlikely (type == SubstLookupSubTable::Extension)) - return CastR<ExtensionSubst> (get_subtable()).is_reverse (); + return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse (); return SubstLookup::lookup_type_is_reverse (type); } -static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index) +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 = *(c->face->ot_layout->gsub); + const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); const SubstLookup &l = gsub.get_lookup (lookup_index); - - if (unlikely (c->nesting_level_left == 0)) - return; - - c->nesting_level_left--; - l.closure (c); - c->nesting_level_left++; + return l.dispatch (c); } -static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) { - const GSUB &gsub = *(c->face->ot_layout->gsub); + const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); const SubstLookup &l = gsub.get_lookup (lookup_index); - - if (unlikely (c->nesting_level_left == 0)) - return false; - - hb_apply_context_t new_c (*c); - new_c.nesting_level_left--; - new_c.set_lookup (l); - return l.apply_once (&new_c); + unsigned int saved_lookup_props = c->lookup_props; + c->set_lookup (l); + bool ret = l.dispatch (c); + c->set_lookup_props (saved_lookup_props); + return ret; } +} /* namespace OT */ + #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh index 12cb694..cbc6840 100644 --- a/src/hb-ot-layout-gsubgpos-private.hh +++ b/src/hb-ot-layout-gsubgpos-private.hh @@ -31,231 +31,599 @@ #include "hb-buffer-private.hh" #include "hb-ot-layout-gdef-table.hh" +#include "hb-set-private.hh" - -/* unique ligature id */ -/* component number in the ligature (0 = base) */ -static inline void -set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp) -{ - info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F); -} -static inline unsigned int -get_lig_id (hb_glyph_info_t &info) -{ - return info.lig_props() >> 4; -} -static inline unsigned int -get_lig_comp (hb_glyph_info_t &info) -{ - return info.lig_props() & 0x0F; -} - -static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) { - uint8_t lig_id = buffer->next_serial () & 0x0F; - if (unlikely (!lig_id)) - lig_id = allocate_lig_id (buffer); /* in case of overflow */ - return lig_id; -} - +namespace OT { #ifndef HB_DEBUG_CLOSURE #define HB_DEBUG_CLOSURE (HB_DEBUG+0) #endif -#define TRACE_CLOSURE() \ - hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, ""); - - -/* TODO Add TRACE_RETURN annotation for would_apply */ - +#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 { + inline const char *get_name (void) { return "CLOSURE"; } + static const unsigned int max_debug_depth = HB_DEBUG_CLOSURE; + typedef hb_void_t return_t; + typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index); + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + 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_ = MAX_NESTING_LEVEL) : - face (face_), glyphs (glyphs_), + 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_APPLY -#define HB_DEBUG_APPLY (HB_DEBUG+0) +#ifndef HB_DEBUG_WOULD_APPLY +#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) #endif -#define TRACE_APPLY() \ - hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint); +#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 +{ + inline const char *get_name (void) { return "WOULD_APPLY"; } + static const unsigned int max_debug_depth = HB_DEBUG_WOULD_APPLY; + typedef bool return_t; + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + 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; -struct hb_apply_context_t + 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_font_t *font; + inline const char *get_name (void) { return "COLLECT_GLYPHS"; } + static const unsigned int max_debug_depth = HB_DEBUG_COLLECT_GLYPHS; + typedef hb_void_t return_t; + typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index); + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + 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_buffer_t *buffer; - hb_direction_t direction; - hb_mask_t lookup_mask; + 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 lookup_props; - unsigned int property; /* propety of first glyph */ 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_ = 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 (); + } - hb_apply_context_t (hb_font_t *font_, - hb_face_t *face_, - hb_buffer_t *buffer_, - hb_mask_t lookup_mask_) : - font (font_), face (face_), buffer (buffer_), - direction (buffer_->props.direction), - lookup_mask (lookup_mask_), - nesting_level_left (MAX_NESTING_LEVEL), - lookup_props (0), property (0), debug_depth (0) {} + void set_recurse_func (recurse_func_t func) { recurse_func = func; } +}; + + + +#ifndef HB_DEBUG_GET_COVERAGE +#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) +#endif - void set_lookup (const Lookup &l) { - lookup_props = l.get_props (); +template <typename set_t> +struct hb_add_coverage_context_t +{ + inline const char *get_name (void) { return "GET_COVERAGE"; } + static const unsigned int max_debug_depth = HB_DEBUG_GET_COVERAGE; + typedef const Coverage &return_t; + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + 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; } - struct mark_skipping_forward_iterator_t + 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 codepoint %u", c->buffer->idx, c->buffer->cur().codepoint); + +struct hb_apply_context_t +{ + struct matcher_t { - inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_, - unsigned int start_index_, - unsigned int num_items_, - bool context_match = false) - { - c = c_; - idx = start_index_; - num_items = num_items_; - mask = context_match ? -1 : c->lookup_mask; - syllable = context_match ? 0 : c->buffer->cur().syllable (); - end = c->buffer->len; - } - inline bool has_no_chance (void) const - { - return unlikely (num_items && idx + num_items >= end); - } - inline bool next (unsigned int *property_out, - unsigned int lookup_props) + 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 { - assert (num_items > 0); - do - { - if (has_no_chance ()) - return false; - idx++; - } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out)); - num_items--; - return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ()); + 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; } - inline bool next (unsigned int *property_out = NULL) + + 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 { - return next (property_out, c->lookup_props); + if (!c->check_glyph_property (&info, lookup_props)) + return SKIP_YES; + + if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && + (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && + !_hb_glyph_info_ligated (&info))) + return SKIP_MAYBE; + + return SKIP_NO; } - unsigned int idx; - private: - hb_apply_context_t *c; - unsigned int num_items; + protected: + unsigned int lookup_props; + bool ignore_zwnj; + bool ignore_zwj; hb_mask_t mask; uint8_t syllable; - unsigned int end; + match_func_t match_func; + const void *match_data; }; - struct mark_skipping_backward_iterator_t + struct skipping_iterator_t { - inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_, - unsigned int start_index_, - unsigned int num_items_, - hb_mask_t mask_ = 0, - bool match_syllable_ = true) + inline void init (hb_apply_context_t *c_, bool context_match = false) { c = c_; - idx = start_index_; - num_items = num_items_; - mask = mask_ ? mask_ : c->lookup_mask; - syllable = match_syllable_ ? c->buffer->cur().syllable () : 0; + 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 bool has_no_chance (void) const + inline void set_lookup_props (unsigned int lookup_props) { - return unlikely (idx < num_items); + 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 bool prev (unsigned int *property_out, - unsigned int lookup_props) + + inline void reject (void) { num_items++; match_glyph_data--; } + + inline bool next (void) { assert (num_items > 0); - do + while (idx + num_items < end) { - if (has_no_chance ()) + 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; - idx--; - } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out)); - num_items--; - return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ()); + } + return false; } - inline bool prev (unsigned int *property_out = NULL) + inline bool prev (void) { - return prev (property_out, c->lookup_props); + 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; - private: + protected: hb_apply_context_t *c; + matcher_t matcher; + const USHORT *match_glyph_data; + unsigned int num_items; - hb_mask_t mask; - uint8_t syllable; + unsigned int end; }; - inline bool should_mark_skip_current_glyph (void) const + + inline const char *get_name (void) { return "APPLY"; } + static const unsigned int max_debug_depth = HB_DEBUG_APPLY; + typedef bool return_t; + typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index); + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + 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) { - return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL); + 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; + skipping_iterator_t iter_input, iter_context; + 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 (MAX_NESTING_LEVEL), + lookup_props (0), + gdef (*hb_ot_layout_from_face (face)->gdef), + has_glyph_classes (gdef.has_glyph_classes ()), + iter_input (), + iter_context (), + 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 (const Lookup &l) { set_lookup_props (l.get_props ()); } + inline void set_lookup_props (unsigned int lookup_props_) + { + lookup_props = lookup_props_; + iter_input.init (this, false); + iter_context.init (this, true); + } - inline void replace_glyph (hb_codepoint_t glyph_index, - unsigned int klass = 0) const + inline bool + match_properties_mark (hb_codepoint_t glyph, + unsigned int glyph_props, + unsigned int lookup_props) const { - buffer->cur().props_cache() = klass; /*XXX if has gdef? */ + /* If using mark filtering sets, the high short of + * lookup_props has the set index. + */ + if (lookup_props & LookupFlag::UseMarkFilteringSet) + return gdef.mark_set_covers (lookup_props >> 16, glyph); + + /* The second byte of lookup_props has the meaning + * "ignore marks of attachment type different than + * the attachment type specified." + */ + if (lookup_props & LookupFlag::MarkAttachmentType) + return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType); + + return true; + } + + inline bool + check_glyph_property (const hb_glyph_info_t *info, + unsigned int lookup_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 + * lookup_props includes LookupFlags::IgnoreLigatures + */ + if (glyph_props & lookup_props & LookupFlag::IgnoreFlags) + return false; + + if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) + return match_properties_mark (glyph, glyph_props, lookup_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 replace_glyphs_be16 (unsigned int num_in, - unsigned int num_out, - const uint16_t *glyph_data_be, - unsigned int klass = 0) const + inline void output_glyph_for_component (hb_codepoint_t glyph_index, + unsigned int class_guess) const { - buffer->cur().props_cache() = klass; /* XXX if has gdef? */ - buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be); + _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); -typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index); -typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index); struct ContextClosureFuncs { intersects_func_t intersects; - closure_lookup_func_t closure; +}; +struct ContextCollectGlyphsFuncs +{ + collect_glyphs_func_t collect; }; struct ContextApplyFuncs { match_func_t match; - apply_lookup_func_t apply; }; + static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED) { return glyphs->has (value); @@ -284,48 +652,237 @@ static inline bool intersects_array (hb_closure_context_t *c, } +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 = NULL) + unsigned int *end_offset, + unsigned int match_positions[MAX_CONTEXT_LENGTH], + bool *p_is_mark_ligature = NULL, + unsigned int *p_total_component_count = NULL) { - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1); - if (skippy_iter.has_no_chance ()) - return false; + TRACE_APPLY (NULL); + + if (unlikely (count > MAX_CONTEXT_LENGTH)) TRACE_RETURN (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 false; + if (!skippy_iter.next ()) return TRACE_RETURN (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_RETURN (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_RETURN (false); + } - if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data))) - return 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]); } - if (end_offset) - *end_offset = skippy_iter.idx - c->buffer->idx + 1; + *end_offset = skippy_iter.idx - buffer->idx + 1; - return true; + 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_RETURN (true); +} +static inline void ligate_input (hb_apply_context_t *c, + unsigned int count, /* Including the first glyph */ + unsigned int match_positions[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); + _hb_glyph_info_set_modified_combining_class (&buffer->cur(), 0); + } + } + c->replace_glyph_with_ligature (lig_glyph, klass); + + for (unsigned int i = 1; i < count; i++) + { + while (buffer->idx < match_positions[i]) + { + if (!is_mark_ligature) { + unsigned int new_lig_comp = components_so_far - last_num_components + + MIN (MAX (_hb_glyph_info_get_lig_comp (&buffer->cur()), 1u), 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 new_lig_comp = components_so_far - last_num_components + + MIN (MAX (_hb_glyph_info_get_lig_comp (&buffer->info[i]), 1u), last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); + } else + break; + } + } + TRACE_RETURN (true); } static inline bool match_backtrack (hb_apply_context_t *c, @@ -334,20 +891,17 @@ static inline bool match_backtrack (hb_apply_context_t *c, match_func_t match_func, const void *match_data) { - hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true); - if (skippy_iter.has_no_chance ()) - return false; + 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 false; - - if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data))) - return false; - } + return TRACE_RETURN (false); - return true; + return TRACE_RETURN (true); } static inline bool match_lookahead (hb_apply_context_t *c, @@ -357,28 +911,26 @@ static inline bool match_lookahead (hb_apply_context_t *c, const void *match_data, unsigned int offset) { - hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true); - if (skippy_iter.has_no_chance ()) - return false; + 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 false; - - if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data))) - return false; - } + return TRACE_RETURN (false); - return true; + return TRACE_RETURN (true); } struct LookupRecord { - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this)); } @@ -391,71 +943,96 @@ struct LookupRecord }; -static inline void closure_lookup (hb_closure_context_t *c, - unsigned int lookupCount, - const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ - closure_lookup_func_t closure_func) +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++) - closure_func (c, lookupRecord->lookupListIndex); + 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[MAX_CONTEXT_LENGTH], /* Including the first glyph */ unsigned int lookupCount, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ - apply_lookup_func_t apply_func) + unsigned int match_length) { - unsigned int end = c->buffer->len; - if (unlikely (count == 0 || c->buffer->idx + count > end)) - return false; + TRACE_APPLY (NULL); - /* TODO We don't support lookupRecord arrays that are not increasing: - * Should be easy for in_place ones at least. */ + hb_buffer_t *buffer = c->buffer; + unsigned int end; - /* Note: If sublookup is reverse, it will underflow after the first loop - * and we jump out of it. Not entirely disastrous. So we don't check - * for reverse lookup here. - */ - for (unsigned int i = 0; i < count; /* NOP */) + /* All positions are distance from beginning of *output* buffer. + * Adjust. */ { - if (unlikely (c->buffer->idx == end)) - return true; - while (c->should_mark_skip_current_glyph ()) - { - /* No lookup applied for this index */ - c->buffer->next_glyph (); - if (unlikely (c->buffer->idx == end)) - return true; - } + unsigned int bl = buffer->backtrack_len (); + end = bl + match_length; - if (lookupCount && i == lookupRecord->sequenceIndex) - { - unsigned int old_pos = c->buffer->idx; + int delta = bl - buffer->idx; + /* Convert positions to new indexing. */ + for (unsigned int j = 0; j < count; j++) + match_positions[j] += delta; + } - /* Apply a lookup */ - bool done = apply_func (c, lookupRecord->lookupListIndex); + for (unsigned int i = 0; i < lookupCount; i++) + { + unsigned int idx = lookupRecord[i].sequenceIndex; + if (idx >= count) + 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; - lookupRecord++; - lookupCount--; - /* Err, this is wrong if the lookup jumped over some glyphs */ - i += c->buffer->idx - old_pos; - if (unlikely (c->buffer->idx == end)) - return true; + if (!delta) + continue; - if (!done) - goto not_applied; + /* Recursed lookup changed buffer len. Adjust. */ + + /* end can't go back past the current match position. + * Note: this is only true because we do NOT allow MultipleSubst + * with zero sequence len. */ + end = MAX ((int) match_positions[idx] + 1, int (end) + delta); + + unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */ + + if (delta > 0) + { + if (unlikely (delta + count > MAX_CONTEXT_LENGTH)) + break; } else { - not_applied: - /* No lookup applied for this index */ - c->buffer->next_glyph (); - i++; + /* 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; } - return true; + buffer->move_to (end); + + return TRACE_RETURN (true); } @@ -468,6 +1045,12 @@ struct ContextClosureLookupContext const void *intersects_data; }; +struct ContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data; +}; + struct ContextApplyLookupContext { ContextApplyFuncs funcs; @@ -484,12 +1067,35 @@ static inline void context_closure_lookup (hb_closure_context_t *c, if (intersects_array (c, inputCount ? inputCount - 1 : 0, input, lookup_context.funcs.intersects, lookup_context.intersects_data)) - closure_lookup (c, - lookupCount, lookupRecord, - lookup_context.funcs.closure); + 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 */ @@ -497,74 +1103,111 @@ static inline bool context_apply_lookup (hb_apply_context_t *c, const LookupRecord lookupRecord[], ContextApplyLookupContext &lookup_context) { + unsigned int match_length = 0; + unsigned int match_positions[MAX_CONTEXT_LENGTH]; return match_input (c, inputCount, input, - lookup_context.funcs.match, lookup_context.match_data) + lookup_context.funcs.match, lookup_context.match_data, + &match_length, match_positions) && apply_lookup (c, - inputCount, + inputCount, match_positions, lookupCount, lookupRecord, - lookup_context.funcs.apply); + match_length); } struct Rule { - friend struct RuleSet; - - private: - inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - TRACE_CLOSURE (); - const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); + TRACE_CLOSURE (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); context_closure_lookup (c, - inputCount, input, + 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_RETURN (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 (); - const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); - return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context)); + TRACE_APPLY (this); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0)); + return TRACE_RETURN (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context)); } public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return inputCount.sanitize (c) && lookupCount.sanitize (c) - && c->check_range (input, - input[0].static_size * inputCount + && c->check_range (inputZ, + inputZ[0].static_size * inputCount + lookupRecordX[0].static_size * lookupCount); } - private: + protected: USHORT inputCount; /* Total number of glyphs in input * glyph sequence--includes the first * glyph */ USHORT lookupCount; /* Number of LookupRecords */ - USHORT input[VAR]; /* Array of match inputs--start with + 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, input, lookupRecordX); + DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX); }; struct RuleSet { inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - TRACE_CLOSURE (); + 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_RETURN (true); + } + return TRACE_RETURN (false); + } + inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { - TRACE_APPLY (); + TRACE_APPLY (this); unsigned int num_rules = rule.len; for (unsigned int i = 0; i < num_rules; i++) { @@ -574,12 +1217,13 @@ struct RuleSet return TRACE_RETURN (false); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (rule.sanitize (c, this)); } - private: + protected: OffsetArrayOf<Rule> rule; /* Array of Rule tables * ordered by preference */ @@ -590,18 +1234,14 @@ struct RuleSet struct ContextFormat1 { - friend struct Context; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); const Coverage &cov = (this+coverage); struct ContextClosureLookupContext lookup_context = { - {intersects_glyph, closure_func}, + {intersects_glyph}, NULL }; @@ -613,27 +1253,60 @@ struct ContextFormat1 } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_RETURN (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 (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const RuleSet &rule_set = this+ruleSet[index]; struct ContextApplyLookupContext lookup_context = { - {match_glyph, apply_func}, + {match_glyph}, NULL }; return TRACE_RETURN (rule_set.apply (c, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -648,21 +1321,17 @@ struct ContextFormat1 struct ContextFormat2 { - friend struct Context; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); if (!(this+coverage).intersects (c->glyphs)) return; const ClassDef &class_def = this+classDef; struct ContextClosureLookupContext lookup_context = { - {intersects_class, closure_func}, - NULL + {intersects_class}, + &class_def }; unsigned int count = ruleSet.len; @@ -673,28 +1342,64 @@ struct ContextFormat2 } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_APPLY (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (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_RETURN (false); const ClassDef &class_def = this+classDef; - index = class_def (c->buffer->cur().codepoint); + index = class_def.get_class (c->buffer->cur().codepoint); const RuleSet &rule_set = this+ruleSet[index]; struct ContextApplyLookupContext lookup_context = { - {match_class, apply_func}, + {match_class}, &class_def }; return TRACE_RETURN (rule_set.apply (c, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -712,104 +1417,114 @@ struct ContextFormat2 struct ContextFormat3 { - friend struct Context; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); - if (!(this+coverage[0]).intersects (c->glyphs)) + TRACE_CLOSURE (this); + if (!(this+coverageZ[0]).intersects (c->glyphs)) return; - const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); struct ContextClosureLookupContext lookup_context = { - {intersects_coverage, closure_func}, + {intersects_coverage}, this }; context_closure_lookup (c, - glyphCount, (const USHORT *) (coverage + 1), + glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context); } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context)); + } + + inline const Coverage &get_coverage (void) const { - TRACE_APPLY (); - unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint); + 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_RETURN (false); - const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount); struct ContextApplyLookupContext lookup_context = { - {match_coverage, apply_func}, + {match_coverage}, this }; - return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context)); + return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!c->check_struct (this)) return TRACE_RETURN (false); unsigned int count = glyphCount; - if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false); + if (!count) return TRACE_RETURN (false); /* We want to access coverageZ[0] freely. */ + if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return TRACE_RETURN (false); for (unsigned int i = 0; i < count; i++) - if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false); - LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count); + if (!coverageZ[i].sanitize (c, this)) return TRACE_RETURN (false); + const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count); return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount)); } - private: + protected: USHORT format; /* Format identifier--format = 3 */ USHORT glyphCount; /* Number of glyphs in the input glyph * sequence */ USHORT lookupCount; /* Number of LookupRecords */ OffsetTo<Coverage> - coverage[VAR]; /* Array of offsets to Coverage + 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, coverage, lookupRecordX); + DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX); }; struct Context { - protected: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_CLOSURE (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: u.format1.closure (c, closure_func); break; - case 2: u.format2.closure (c, closure_func); break; - case 3: u.format3.closure (c, closure_func); break; - default: break; + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + case 2: return TRACE_RETURN (c->dispatch (u.format2)); + case 3: return TRACE_RETURN (c->dispatch (u.format3)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c, apply_func)); - case 2: return TRACE_RETURN (u.format2.apply (c, apply_func)); - case 3: return TRACE_RETURN (u.format3.apply (c, apply_func)); - default:return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - case 2: return TRACE_RETURN (u.format2.sanitize (c)); - case 3: return TRACE_RETURN (u.format3.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ ContextFormat1 format1; @@ -827,6 +1542,12 @@ struct ChainContextClosureLookupContext const void *intersects_data[3]; }; +struct ChainContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data[3]; +}; + struct ChainContextApplyLookupContext { ContextApplyFuncs funcs; @@ -850,12 +1571,52 @@ static inline void chain_context_closure_lookup (hb_closure_context_t *c, && intersects_array (c, inputCount ? inputCount - 1 : 0, input, lookup_context.funcs.intersects, lookup_context.intersects_data[1]) - && intersects_array (c, + && intersects_array (c, lookaheadCount, lookahead, lookup_context.funcs.intersects, lookup_context.intersects_data[2])) - closure_lookup (c, - lookupCount, lookupRecord, - lookup_context.funcs.closure); + 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, @@ -869,33 +1630,30 @@ static inline bool chain_context_apply_lookup (hb_apply_context_t *c, const LookupRecord lookupRecord[], ChainContextApplyLookupContext &lookup_context) { - unsigned int lookahead_offset; - return match_backtrack (c, - backtrackCount, backtrack, - lookup_context.funcs.match, lookup_context.match_data[0]) - && match_input (c, + unsigned int match_length = 0; + unsigned int match_positions[MAX_CONTEXT_LENGTH]; + return match_input (c, inputCount, input, lookup_context.funcs.match, lookup_context.match_data[1], - &lookahead_offset) + &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], - lookahead_offset) + match_length) && apply_lookup (c, - inputCount, + inputCount, match_positions, lookupCount, lookupRecord, - lookup_context.funcs.apply); + match_length); } struct ChainRule { - friend struct ChainRuleSet; - - private: - inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - TRACE_CLOSURE (); + 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); @@ -907,9 +1665,36 @@ struct ChainRule 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_RETURN (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 (); + 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); @@ -920,19 +1705,19 @@ struct ChainRule lookup.array, lookup_context)); } - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!backtrack.sanitize (c)) return TRACE_RETURN (false); - HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); + const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); if (!input.sanitize (c)) return TRACE_RETURN (false); - ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); + const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); if (!lookahead.sanitize (c)) return TRACE_RETURN (false); - ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); return TRACE_RETURN (lookup.sanitize (c)); } - private: + protected: ArrayOf<USHORT> backtrack; /* Array of backtracking values * (to be matched before the input @@ -954,15 +1739,34 @@ struct ChainRuleSet { inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - TRACE_CLOSURE (); + 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_RETURN (true); + + return TRACE_RETURN (false); + } + inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { - TRACE_APPLY (); + 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)) @@ -971,12 +1775,13 @@ struct ChainRuleSet return TRACE_RETURN (false); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (rule.sanitize (c, this)); } - private: + protected: OffsetArrayOf<ChainRule> rule; /* Array of ChainRule tables * ordered by preference */ @@ -986,17 +1791,13 @@ struct ChainRuleSet struct ChainContextFormat1 { - friend struct ChainContext; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); const Coverage &cov = (this+coverage); struct ChainContextClosureLookupContext lookup_context = { - {intersects_glyph, closure_func}, + {intersects_glyph}, {NULL, NULL, NULL} }; @@ -1008,26 +1809,59 @@ struct ChainContextFormat1 } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_RETURN (rule_set.would_apply (c, lookup_context)); + } + + inline const Coverage &get_coverage (void) const { - TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (false); const ChainRuleSet &rule_set = this+ruleSet[index]; struct ChainContextApplyLookupContext lookup_context = { - {match_glyph, apply_func}, + {match_glyph}, {NULL, NULL, NULL} }; return TRACE_RETURN (rule_set.apply (c, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 1 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -1041,13 +1875,9 @@ struct ChainContextFormat1 struct ChainContextFormat2 { - friend struct ChainContext; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); if (!(this+coverage).intersects (c->glyphs)) return; @@ -1056,7 +1886,7 @@ struct ChainContextFormat2 const ClassDef &lookahead_class_def = this+lookaheadClassDef; struct ChainContextClosureLookupContext lookup_context = { - {intersects_class, closure_func}, + {intersects_class}, {&backtrack_class_def, &input_class_def, &lookahead_class_def} @@ -1070,20 +1900,65 @@ struct ChainContextFormat2 } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_APPLY (); - unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + 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_RETURN (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_RETURN (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; - index = input_class_def (c->buffer->cur().codepoint); + index = input_class_def.get_class (c->buffer->cur().codepoint); const ChainRuleSet &rule_set = this+ruleSet[index]; struct ChainContextApplyLookupContext lookup_context = { - {match_class, apply_func}, + {match_class}, {&backtrack_class_def, &input_class_def, &lookahead_class_def} @@ -1091,14 +1966,15 @@ struct ChainContextFormat2 return TRACE_RETURN (rule_set.apply (c, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) && inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) && ruleSet.sanitize (c, this)); } - private: + protected: USHORT format; /* Format identifier--format = 2 */ OffsetTo<Coverage> coverage; /* Offset to Coverage table--from @@ -1124,13 +2000,9 @@ struct ChainContextFormat2 struct ChainContextFormat3 { - friend struct ChainContext; - - private: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + inline void closure (hb_closure_context_t *c) const { - TRACE_CLOSURE (); + TRACE_CLOSURE (this); const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); if (!(this+input[0]).intersects (c->glyphs)) @@ -1139,7 +2011,7 @@ struct ChainContextFormat3 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); struct ChainContextClosureLookupContext lookup_context = { - {intersects_coverage, closure_func}, + {intersects_coverage}, {this, this, this} }; chain_context_closure_lookup (c, @@ -1150,18 +2022,63 @@ struct ChainContextFormat3 lookup_context); } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const + 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_RETURN (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 (); + TRACE_APPLY (this); const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); - unsigned int index = (this+input[0]) (c->buffer->cur().codepoint); + unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); struct ChainContextApplyLookupContext lookup_context = { - {match_coverage, apply_func}, + {match_coverage}, {this, this, this} }; return TRACE_RETURN (chain_context_apply_lookup (c, @@ -1171,18 +2088,20 @@ struct ChainContextFormat3 lookup.len, lookup.array, lookup_context)); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false); - OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); + const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); if (!input.sanitize (c, this)) return TRACE_RETURN (false); - OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); + if (!input.len) return TRACE_RETURN (false); /* To be consistent with Context. */ + const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false); - ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); + const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); return TRACE_RETURN (lookup.sanitize (c)); } - private: + protected: USHORT format; /* Format identifier--format = 3 */ OffsetArrayOf<Coverage> backtrack; /* Array of coverage tables @@ -1205,42 +2124,20 @@ struct ChainContextFormat3 struct ChainContext { - protected: - - inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + template <typename context_t> + inline typename context_t::return_t dispatch (context_t *c) const { - TRACE_CLOSURE (); + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: u.format1.closure (c, closure_func); break; - case 2: u.format2.closure (c, closure_func); break; - case 3: u.format3.closure (c, closure_func); break; - default: break; + case 1: return TRACE_RETURN (c->dispatch (u.format1)); + case 2: return TRACE_RETURN (c->dispatch (u.format2)); + case 3: return TRACE_RETURN (c->dispatch (u.format3)); + default:return TRACE_RETURN (c->default_return_value ()); } } - inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.apply (c, apply_func)); - case 2: return TRACE_RETURN (u.format2.apply (c, apply_func)); - case 3: return TRACE_RETURN (u.format3.apply (c, apply_func)); - default:return TRACE_RETURN (false); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (false); - switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - case 2: return TRACE_RETURN (u.format2.sanitize (c)); - case 3: return TRACE_RETURN (u.format3.sanitize (c)); - default:return TRACE_RETURN (true); - } - } - - private: + protected: union { USHORT format; /* Format identifier */ ChainContextFormat1 format1; @@ -1250,20 +2147,35 @@ struct ChainContext }; +template <typename T> struct ExtensionFormat1 { - friend struct Extension; - - protected: inline unsigned int get_type (void) const { return extensionLookupType; } - inline unsigned int get_offset (void) const { return extensionOffset; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return TRACE_RETURN (c->check_struct (this)); + 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))) TRACE_RETURN (c->default_return_value ()); + return 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_RETURN (c->check_struct (this) && extensionOffset != 0); } - private: + protected: USHORT format; /* Format identifier. Set to 1. */ USHORT extensionLookupType; /* Lookup type of subtable referenced * by ExtensionOffset (i.e. the @@ -1274,6 +2186,7 @@ struct ExtensionFormat1 DEFINE_SIZE_STATIC (8); }; +template <typename T> struct Extension { inline unsigned int get_type (void) const @@ -1283,27 +2196,30 @@ struct Extension default:return 0; } } - inline unsigned int get_offset (void) const + template <typename X> + inline const X& get_subtable (void) const { switch (u.format) { - case 1: return u.format1.get_offset (); - default:return 0; + case 1: return u.format1.template get_subtable<typename T::LookupSubTable> (); + default:return Null(typename T::LookupSubTable); } } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return TRACE_RETURN (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))) TRACE_RETURN (c->default_return_value ()); switch (u.format) { - case 1: return TRACE_RETURN (u.format1.sanitize (c)); - default:return TRACE_RETURN (true); + case 1: return TRACE_RETURN (u.format1.dispatch (c)); + default:return TRACE_RETURN (c->default_return_value ()); } } - private: + protected: union { USHORT format; /* Format identifier */ - ExtensionFormat1 format1; + ExtensionFormat1<T> format1; } u; }; @@ -1332,8 +2248,8 @@ struct GSUBGPOS inline unsigned int get_feature_count (void) const { return (this+featureList).len; } - inline const Tag& get_feature_tag (unsigned int i) const - { return (this+featureList).get_tag (i); } + inline 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 @@ -1348,8 +2264,9 @@ struct GSUBGPOS inline const Lookup& get_lookup (unsigned int i) const { return (this+lookupList)[i]; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) && scriptList.sanitize (c, this) && featureList.sanitize (c, this) && @@ -1358,7 +2275,7 @@ struct GSUBGPOS protected: FixedVersion version; /* Version of the GSUB/GPOS table--initially set - * to 0x00010000 */ + * to 0x00010000u */ OffsetTo<ScriptList> scriptList; /* ScriptList table */ OffsetTo<FeatureList> @@ -1370,5 +2287,7 @@ struct GSUBGPOS }; +} /* namespace OT */ + #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ diff --git a/src/hb-ot-layout-jstf-table.hh b/src/hb-ot-layout-jstf-table.hh new file mode 100644 index 0000000..739dfd9 --- /dev/null +++ b/src/hb-ot-layout-jstf-table.hh @@ -0,0 +1,233 @@ +/* + * 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_RETURN (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_RETURN (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_RETURN (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_RETURN (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/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh index f860e7b..47fecd2 100644 --- a/src/hb-ot-layout-private.hh +++ b/src/hb-ot-layout-private.hh @@ -1,5 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #ifndef HB_OT_LAYOUT_PRIVATE_HH @@ -29,43 +31,79 @@ #include "hb-private.hh" -#include "hb-ot-layout.h" - #include "hb-font-private.hh" #include "hb-buffer-private.hh" -#include "hb-ot-shape-complex-private.hh" - +#include "hb-set-private.hh" /* * GDEF */ -/* XXX cleanup */ -typedef enum { - HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0x0001, - HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 0x0002, - HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE = 0x0004, - HB_OT_LAYOUT_GLYPH_CLASS_MARK = 0x0008, - HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT = 0x0010 -} hb_ot_layout_glyph_class_t; +typedef enum +{ + /* 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_INTERNAL unsigned int -_hb_ot_layout_get_glyph_property (hb_face_t *face, - hb_glyph_info_t *info); + 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_ot_layout_glyph_class_mask_t; -HB_INTERNAL hb_bool_t -_hb_ot_layout_check_glyph_property (hb_face_t *face, - hb_glyph_info_t *ginfo, - unsigned int lookup_props, - unsigned int *property_out); + +/* + * GSUB/GPOS + */ HB_INTERNAL hb_bool_t -_hb_ot_layout_skip_mark (hb_face_t *face, - hb_glyph_info_t *ginfo, - unsigned int lookup_props, - unsigned int *property_out); +hb_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 after all the substitute_lookup's are done */ +HB_INTERNAL void +hb_ot_layout_substitute_finish (hb_font_t *font, + hb_buffer_t *buffer); + + +/* Should be called before all the position_lookup's are done. Resets positions to zero. */ +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 */ +HB_INTERNAL void +hb_ot_layout_position_finish (hb_font_t *font, + hb_buffer_t *buffer); @@ -73,15 +111,48 @@ _hb_ot_layout_skip_mark (hb_face_t *face, * hb_ot_layout_t */ +namespace OT { + struct GDEF; + struct GSUB; + struct GPOS; +} + +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; - const struct GDEF *gdef; - const struct GSUB *gsub; - const struct GPOS *gpos; + const struct OT::GDEF *gdef; + const struct OT::GSUB *gsub; + const struct OT::GPOS *gpos; + + 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; }; @@ -92,5 +163,302 @@ 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_props0() var2.u8[0] +#define unicode_props1() var2.u8[1] + +/* 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 */ + +/* unicode_props */ + +enum { + MASK0_ZWJ = 0x20u, + MASK0_ZWNJ = 0x40u, + MASK0_IGNORABLE = 0x80u, + MASK0_GEN_CAT = 0x1Fu +}; + +static inline void +_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode) +{ + /* XXX This shouldn't be inlined, or at least not while is_default_ignorable() is inline. */ + info->unicode_props0() = ((unsigned int) unicode->general_category (info->codepoint)) | + (unicode->is_default_ignorable (info->codepoint) ? MASK0_IGNORABLE : 0) | + (info->codepoint == 0x200Cu ? MASK0_ZWNJ : 0) | + (info->codepoint == 0x200Du ? MASK0_ZWJ : 0); + info->unicode_props1() = unicode->modified_combining_class (info->codepoint); +} + +static inline void +_hb_glyph_info_set_general_category (hb_glyph_info_t *info, + hb_unicode_general_category_t gen_cat) +{ + info->unicode_props0() = (unsigned int) gen_cat | ((info->unicode_props0()) & ~MASK0_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_props0() & MASK0_GEN_CAT); +} + +static inline void +_hb_glyph_info_set_modified_combining_class (hb_glyph_info_t *info, + unsigned int modified_class) +{ + info->unicode_props1() = modified_class; +} + +static inline unsigned int +_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) +{ + return info->unicode_props1(); +} + +static inline hb_bool_t +_hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info) +{ + return !!(info->unicode_props0() & MASK0_IGNORABLE); +} + +static inline hb_bool_t +_hb_glyph_info_is_zwnj (const hb_glyph_info_t *info) +{ + return !!(info->unicode_props0() & MASK0_ZWNJ); +} + +static inline hb_bool_t +_hb_glyph_info_is_zwj (const hb_glyph_info_t *info) +{ + return !!(info->unicode_props0() & MASK0_ZWJ); +} + +static inline void +_hb_glyph_info_flip_joiners (hb_glyph_info_t *info) +{ + info->unicode_props0() ^= MASK0_ZWNJ | MASK0_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); +} + + +/* Allocation / deallocation. */ + +static inline void +_hb_buffer_allocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props0); + HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props1); +} + +static inline void +_hb_buffer_deallocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, unicode_props0); + HB_BUFFER_DEALLOCATE_VAR (buffer, unicode_props1); +} + +static inline void +_hb_buffer_assert_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ASSERT_VAR (buffer, unicode_props0); + HB_BUFFER_ASSERT_VAR (buffer, unicode_props1); +} + +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/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index 0621f86..b1e69e8 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -2,6 +2,7 @@ * 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. * @@ -24,6 +25,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #include "hb-ot-layout-private.hh" @@ -31,28 +33,49 @@ #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" -#include "hb-ot-maxp-table.hh" +#include "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) { - /* TODO Remove this object altogether */ hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); + if (unlikely (!layout)) + return NULL; - layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF)); - layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob); + 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 = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB)); - layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_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 = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS)); - layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_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); + + 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; } @@ -60,6 +83,14 @@ _hb_ot_layout_create (hb_face_t *face) 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); @@ -67,20 +98,23 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout) free (layout); } -static inline const GDEF& +static inline const OT::GDEF& _get_gdef (hb_face_t *face) { - return likely (face->ot_layout && face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF); + 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 GSUB& +static inline const OT::GSUB& _get_gsub (hb_face_t *face) { - return likely (face->ot_layout && face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB); + 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 GPOS& +static inline const OT::GPOS& _get_gpos (hb_face_t *face) { - return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS); + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); + return *hb_ot_layout_from_face (face)->gpos; } @@ -94,85 +128,21 @@ hb_ot_layout_has_glyph_classes (hb_face_t *face) return _get_gdef (face).has_glyph_classes (); } -unsigned int -_hb_ot_layout_get_glyph_property (hb_face_t *face, - hb_glyph_info_t *info) -{ - if (!info->props_cache()) - { - const GDEF &gdef = _get_gdef (face); - info->props_cache() = gdef.get_glyph_props (info->codepoint); - } - - return info->props_cache(); -} - -static hb_bool_t -_hb_ot_layout_match_properties (hb_face_t *face, - hb_codepoint_t codepoint, - unsigned int glyph_props, - unsigned int lookup_props) +hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph) { - /* Not covered, if, for example, glyph class is ligature and - * lookup_props includes LookupFlags::IgnoreLigatures - */ - if (glyph_props & lookup_props & LookupFlag::IgnoreFlags) - return false; - - if (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK) - { - /* If using mark filtering sets, the high short of - * lookup_props has the set index. - */ - if (lookup_props & LookupFlag::UseMarkFilteringSet) - return _get_gdef (face).mark_set_covers (lookup_props >> 16, codepoint); - - /* The second byte of lookup_props has the meaning - * "ignore marks of attachment type different than - * the attachment type specified." - */ - if (lookup_props & LookupFlag::MarkAttachmentType && glyph_props & LookupFlag::MarkAttachmentType) - return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType); - } - - return true; + return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); } -hb_bool_t -_hb_ot_layout_check_glyph_property (hb_face_t *face, - hb_glyph_info_t *ginfo, - unsigned int lookup_props, - unsigned int *property_out) -{ - unsigned int property; - - property = _hb_ot_layout_get_glyph_property (face, ginfo); - (void) (property_out && (*property_out = property)); - - return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props); -} - -hb_bool_t -_hb_ot_layout_skip_mark (hb_face_t *face, - hb_glyph_info_t *ginfo, - unsigned int lookup_props, - unsigned int *property_out) +void +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, + hb_ot_layout_glyph_class_t klass, + hb_set_t *glyphs /* OUT */) { - unsigned int property; - - property = _hb_ot_layout_get_glyph_property (face, ginfo); - (void) (property_out && (*property_out = property)); - - /* If it's a mark, skip it we don't accept it. */ - if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) - return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props); - - /* If not a mark, don't skip. */ - return false; + 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, @@ -194,18 +164,19 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); } + /* * GSUB/GPOS */ -static const GSUBGPOS& +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 Null(GSUBGPOS); + default: return OT::Null(OT::GSUBGPOS); } } @@ -217,19 +188,21 @@ hb_ot_layout_table_get_script_tags (hb_face_t *face, unsigned int *script_count /* IN/OUT */, hb_tag_t *script_tags /* OUT */) { - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + 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 (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + 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; @@ -243,6 +216,11 @@ hb_ot_layout_table_find_script (hb_face_t *face, 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; } @@ -254,8 +232,8 @@ hb_ot_layout_table_choose_script (hb_face_t *face, unsigned int *script_index, hb_tag_t *chosen_script) { - ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + 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) { @@ -283,7 +261,6 @@ hb_ot_layout_table_choose_script (hb_face_t *face, /* try with 'latn'; some old fonts put their features there even though they're really trying to support Thai, for example :( */ -#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { if (chosen_script) *chosen_script = HB_OT_TAG_LATIN_SCRIPT; @@ -303,7 +280,7 @@ hb_ot_layout_table_get_feature_tags (hb_face_t *face, unsigned int *feature_count /* IN/OUT */, hb_tag_t *feature_tags /* OUT */) { - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); return g.get_feature_tags (start_offset, feature_count, feature_tags); } @@ -317,7 +294,7 @@ hb_ot_layout_script_get_language_tags (hb_face_t *face, unsigned int *language_count /* IN/OUT */, hb_tag_t *language_tags /* OUT */) { - const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + 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); } @@ -329,8 +306,8 @@ hb_ot_layout_script_find_language (hb_face_t *face, hb_tag_t language_tag, unsigned int *language_index) { - ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); - const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + 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; @@ -350,9 +327,28 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, unsigned int language_index, unsigned int *feature_index) { - const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); + return hb_ot_layout_language_get_required_feature (face, + table_tag, + script_index, + language_index, + feature_index, + NULL); +} - if (feature_index) *feature_index = l.get_required_feature_index (); +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 (); } @@ -366,8 +362,8 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face, unsigned int *feature_count /* IN/OUT */, unsigned int *feature_indexes /* OUT */) { - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + 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); } @@ -381,8 +377,8 @@ hb_ot_layout_language_get_feature_tags (hb_face_t *face, unsigned int *feature_count /* IN/OUT */, hb_tag_t *feature_tags /* OUT */) { - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + 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); @@ -405,9 +401,9 @@ hb_ot_layout_language_find_feature (hb_face_t *face, hb_tag_t feature_tag, unsigned int *feature_index) { - ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + 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++) { @@ -424,91 +420,576 @@ hb_ot_layout_language_find_feature (hb_face_t *face, } unsigned int -hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face, - hb_tag_t table_tag, - unsigned int feature_index, - unsigned int start_offset, - unsigned int *lookup_count /* IN/OUT */, - unsigned int *lookup_indexes /* OUT */) +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 */) { - const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - const Feature &f = g.get_feature (feature_index); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::Feature &f = g.get_feature (feature_index); return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); } +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); + } + } +} + +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); + } + } +} + +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; + } + } +} + /* - * GSUB + * OT::GSUB */ hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face) { - return &_get_gsub (face) != &Null(GSUB); + return &_get_gsub (face) != &OT::Null(OT::GSUB); } -void -hb_ot_layout_substitute_start (hb_buffer_t *buffer) +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) { - GSUB::substitute_start (buffer); + 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_substitute_lookup (hb_face_t *face, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask) +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) { - hb_apply_context_t c (NULL, face, buffer, mask); - return _get_gsub (face).substitute_lookup (&c, lookup_index); + 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, 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_finish (hb_buffer_t *buffer HB_UNUSED) +hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) { - GSUB::substitute_finish (buffer); + OT::GSUB::substitute_start (font, buffer); } void -hb_ot_layout_substitute_closure_lookup (hb_face_t *face, - hb_set_t *glyphs, - unsigned int lookup_index) +hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) { - hb_closure_context_t c (face, glyphs); - _get_gsub (face).closure_lookup (&c, lookup_index); + OT::GSUB::substitute_finish (font, buffer); +} + +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); } /* - * GPOS + * OT::GPOS */ hb_bool_t hb_ot_layout_has_positioning (hb_face_t *face) { - return &_get_gpos (face) != &Null(GPOS); + return &_get_gpos (face) != &OT::Null(OT::GPOS); } void -hb_ot_layout_position_start (hb_buffer_t *buffer) +hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) { - GPOS::position_start (buffer); + OT::GPOS::position_start (font, buffer); +} + +void +hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) +{ + OT::GPOS::position_finish (font, buffer); } hb_bool_t -hb_ot_layout_position_lookup (hb_font_t *font, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask) +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 { - hb_apply_context_t c (font, font->face, buffer, mask); - return _get_gpos (font->face).position_lookup (&c, lookup_index); + 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; +}; + + +template <typename Obj> +static inline bool +apply_forward (OT::hb_apply_context_t *c, + const Obj &obj, + const hb_ot_layout_lookup_accelerator_t &accel) +{ + bool ret = false; + hb_buffer_t *buffer = c->buffer; + while (buffer->idx < buffer->len) + { + if (accel.may_have (buffer->cur().codepoint) && + (buffer->cur().mask & c->lookup_mask) && + c->check_glyph_property (&buffer->cur(), c->lookup_props) && + obj.apply (c)) + ret = true; + else + buffer->next_glyph (); + } + return ret; } -void -hb_ot_layout_position_finish (hb_buffer_t *buffer) +template <typename Obj> +static inline bool +apply_backward (OT::hb_apply_context_t *c, + const Obj &obj, + const hb_ot_layout_lookup_accelerator_t &accel) +{ + 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) && + obj.apply (c)) + ret = true; + /* The reverse lookup doesn't "advance" cursor (for good reason). */ + buffer->idx--; + + } + while ((int) buffer->idx >= 0); + return ret; +} + +struct hb_apply_forward_context_t +{ + inline const char *get_name (void) { return "APPLY_FORWARD"; } + static const unsigned int max_debug_depth = HB_DEBUG_APPLY; + typedef bool return_t; + template <typename T, typename F> + inline bool may_dispatch (const T *obj, const F *format) { return true; } + template <typename T> + inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); } + static return_t default_return_value (void) { return false; } + bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; } + + hb_apply_forward_context_t (OT::hb_apply_context_t *c_, + const hb_ot_layout_lookup_accelerator_t &accel_) : + c (c_), + accel (accel_), + debug_depth (0) {} + + OT::hb_apply_context_t *c; + const hb_ot_layout_lookup_accelerator_t &accel; + unsigned int debug_depth; +}; + +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 (lookup); + + if (likely (!lookup.is_reverse ())) + { + /* in/out forward substitution/positioning */ + if (Proxy::table_index == 0) + buffer->clear_output (); + buffer->idx = 0; + + bool ret; + if (lookup.get_subtable_count () == 1) + { + hb_apply_forward_context_t c_forward (c, accel); + ret = lookup.dispatch (&c_forward); + } + else + ret = apply_forward (c, lookup, accel); + 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, lookup, accel); + } +} + +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; + 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]); + } + + 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 { - GPOS::position_finish (buffer); + 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/src/hb-ot-layout.h b/src/hb-ot-layout.h index b8b5baf..949678a 100644 --- a/src/hb-ot-layout.h +++ b/src/hb-ot-layout.h @@ -41,6 +41,8 @@ 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 @@ -49,6 +51,24 @@ HB_BEGIN_DECLS 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_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph); + +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 */ unsigned int @@ -72,9 +92,9 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, * GSUB/GPOS feature query and enumeration interface */ -#define HB_OT_LAYOUT_NO_SCRIPT_INDEX ((unsigned int) 0xFFFF) -#define HB_OT_LAYOUT_NO_FEATURE_INDEX ((unsigned int) 0xFFFF) -#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX ((unsigned int) 0xFFFF) +#define HB_OT_LAYOUT_NO_SCRIPT_INDEX 0xFFFFu +#define HB_OT_LAYOUT_NO_FEATURE_INDEX 0xFFFFu +#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX 0xFFFFu unsigned int hb_ot_layout_table_get_script_tags (hb_face_t *face, @@ -126,6 +146,14 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, unsigned int language_index, unsigned int *feature_index); +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); + unsigned int hb_ot_layout_language_get_feature_indexes (hb_face_t *face, hb_tag_t table_tag, @@ -153,12 +181,60 @@ hb_ot_layout_language_find_feature (hb_face_t *face, unsigned int *feature_index); unsigned int -hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face, +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 */); + +unsigned int +hb_ot_layout_table_get_lookup_count (hb_face_t *face, + hb_tag_t table_tag); + + +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 */); + +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); + +void +Xhb_ot_layout_lookup_enumerate_sequences (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 */); + unsigned int lookup_index, + hb_ot_layout_glyph_sequence_func_t callback, + void *user_data); +#endif /* @@ -168,25 +244,31 @@ hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face, hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face); -/* Should be called before all the substitute_lookup's are done. */ -void -hb_ot_layout_substitute_start (hb_buffer_t *buffer); - hb_bool_t -hb_ot_layout_substitute_lookup (hb_face_t *face, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask); +hb_ot_layout_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); -/* Should be called after all the substitute_lookup's are done */ void -hb_ot_layout_substitute_finish (hb_buffer_t *buffer); +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_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 -void -hb_ot_layout_substitute_closure_lookup (hb_face_t *face, - hb_set_t *glyphs, - unsigned int lookup_index); /* * GPOS @@ -195,19 +277,24 @@ hb_ot_layout_substitute_closure_lookup (hb_face_t *face, hb_bool_t hb_ot_layout_has_positioning (hb_face_t *face); -/* Should be called before all the position_lookup's are done. Resets positions to zero. */ -void -hb_ot_layout_position_start (hb_buffer_t *buffer); - +#ifdef HB_NOT_IMPLEMENTED +/* Note: You better have GDEF when using this API, or marks won't do much. */ hb_bool_t -hb_ot_layout_position_lookup (hb_font_t *font, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask); +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 -/* Should be called after all the position_lookup's are done */ -void -hb_ot_layout_position_finish (hb_buffer_t *buffer); +/* Optical 'size' feature info. Returns true if found. + * http://www.microsoft.com/typography/otspec/features_pt.htm#size */ +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 diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh index 3811206..86b7e9f 100644 --- a/src/hb-ot-map-private.hh +++ b/src/hb-ot-map-private.hh @@ -1,6 +1,6 @@ /* * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2012,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -31,9 +31,8 @@ #include "hb-buffer-private.hh" -#include "hb-ot-layout.h" - +struct hb_ot_shape_plan_t; static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS}; @@ -43,46 +42,6 @@ struct hb_ot_map_t public: - hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); } - - typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data); - typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data); - - inline hb_mask_t get_global_mask (void) const { return global_mask; } - - inline hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift = NULL) const { - const feature_map_t *map = features.bsearch (&tag); - if (shift) *shift = map ? map->shift : 0; - return map ? map->mask : 0; - } - - inline hb_mask_t get_1_mask (hb_tag_t tag) const { - const feature_map_t *map = features.bsearch (&tag); - return map ? map->_1_mask : 0; - } - - inline hb_tag_t get_chosen_script (unsigned int table_index) const - { return chosen_script[table_index]; } - - inline void substitute (hb_face_t *face, hb_buffer_t *buffer) const - { apply (0, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_substitute_lookup, face, buffer); } - inline void position (hb_font_t *font, hb_buffer_t *buffer) const - { apply (1, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_position_lookup, font, buffer); } - - HB_INTERNAL void substitute_closure (hb_face_t *face, - hb_set_t *glyphs) const; - - - inline void finish (void) { - features.finish (); - lookups[0].finish (); - lookups[1].finish (); - pauses[0].finish (); - pauses[1].finish (); - } - - private: - struct feature_map_t { hb_tag_t tag; /* should be first for our bsearch to work */ unsigned int index[2]; /* GSUB/GPOS */ @@ -90,78 +49,160 @@ struct hb_ot_map_t 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 int index; + 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 hb_ot_map_t *map, void *face_or_font, hb_buffer_t *buffer, void *user_data); - typedef struct { - pause_func_t func; - void *user_data; - } pause_callback_t; + typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); - struct pause_map_t { - unsigned int num_lookups; /* Cumulative */ - pause_callback_t callback; + struct stage_map_t { + unsigned int last_lookup; /* Cumulative */ + pause_func_t pause_func; }; - typedef hb_bool_t (*apply_lookup_func_t) (void *face_or_font, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask); + + 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_INTERNAL void add_lookups (hb_face_t *face, unsigned int table_index, unsigned int feature_index, - hb_mask_t mask); - - HB_INTERNAL void apply (unsigned int table_index, - hb_ot_map_t::apply_lookup_func_t apply_lookup_func, - void *face_or_font, - hb_buffer_t *buffer) const; + hb_mask_t mask, + bool auto_zwj); hb_mask_t global_mask; - hb_tag_t chosen_script[2]; 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<pause_map_t, 1> pauses[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, + F_HAS_FALLBACK = 0x0002u, + F_MANUAL_ZWJ = 0x0004u }; +/* Macro version for where const is desired. */ +#define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r))) +static inline hb_ot_map_feature_flags_t +operator | (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r) +{ return hb_ot_map_feature_flags_t ((unsigned int) l | (unsigned int) r); } +static inline hb_ot_map_feature_flags_t +operator & (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r) +{ return hb_ot_map_feature_flags_t ((unsigned int) l & (unsigned int) r); } +static inline hb_ot_map_feature_flags_t +operator ~ (hb_ot_map_feature_flags_t r) +{ return hb_ot_map_feature_flags_t (~(unsigned int) r); } +static inline hb_ot_map_feature_flags_t& +operator |= (hb_ot_map_feature_flags_t &l, hb_ot_map_feature_flags_t r) +{ l = l | r; return l; } +static inline hb_ot_map_feature_flags_t& +operator &= (hb_ot_map_feature_flags_t& l, hb_ot_map_feature_flags_t r) +{ l = l & r; return l; } struct hb_ot_map_builder_t { public: - hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); } + 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, bool global); + HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, + hb_ot_map_feature_flags_t flags); - inline void add_bool_feature (hb_tag_t tag, bool global = true) - { add_feature (tag, 1, global); } + 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::gsub_pause_func_t pause_func, void *user_data) - { add_pause (0, (hb_ot_map_t::pause_func_t) pause_func, user_data); } - inline void add_gpos_pause (hb_ot_map_t::gpos_pause_func_t pause_func, void *user_data) - { add_pause (1, (hb_ot_map_t::pause_func_t) pause_func, user_data); } + 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_face_t *face, - const hb_segment_properties_t *props, - struct hb_ot_map_t &m); + HB_INTERNAL void compile (struct hb_ot_map_t &m); inline void finish (void) { feature_infos.finish (); - pauses[0].finish (); - pauses[1].finish (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + stages[table_index].finish (); + } } private: @@ -170,7 +211,7 @@ struct hb_ot_map_builder_t hb_tag_t tag; unsigned int seq; /* sequence#, used for stable sorting only */ unsigned int max_value; - bool global; /* whether the feature applies value to every glyph in the buffer */ + 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 */ @@ -178,16 +219,27 @@ struct hb_ot_map_builder_t { return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); } }; - struct pause_info_t { - unsigned int stage; - hb_ot_map_t::pause_callback_t callback; + 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, void *user_data); + 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,16> feature_infos; - hb_prealloced_array_t<pause_info_t, 1> pauses[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 */ }; diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc index bebf3ed..4985eb2 100644 --- a/src/hb-ot-map.cc +++ b/src/hb-ot-map.cc @@ -1,6 +1,6 @@ /* * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -28,150 +28,154 @@ #include "hb-ot-map-private.hh" -#include "hb-ot-shape-private.hh" - +#include "hb-ot-layout-private.hh" void hb_ot_map_t::add_lookups (hb_face_t *face, unsigned int table_index, unsigned int feature_index, - hb_mask_t mask) + 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_get_lookup_indexes (face, - table_tags[table_index], - feature_index, - offset, &len, - lookup_indices); + hb_ot_layout_feature_get_lookups (face, + table_tags[table_index], + feature_index, + offset, &len, + lookup_indices); - for (unsigned int i = 0; i < len; i++) { + for (unsigned int i = 0; i < len; i++) + { + if (lookup_indices[i] >= table_lookup_count) + continue; hb_ot_map_t::lookup_map_t *lookup = 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)); } +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] = 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, bool global) +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->global = global; - info->default_value = global ? value : 0; + 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_t::apply (unsigned int table_index, - hb_ot_map_t::apply_lookup_func_t apply_lookup_func, - void *face_or_font, - hb_buffer_t *buffer) const -{ - unsigned int i = 0; - - for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) { - const pause_map_t *pause = &pauses[table_index][pause_index]; - for (; i < pause->num_lookups; i++) - apply_lookup_func (face_or_font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); - - pause->callback.func (this, face_or_font, buffer, pause->callback.user_data); - } - - for (; i < lookups[table_index].len; i++) - apply_lookup_func (face_or_font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); -} -void hb_ot_map_t::substitute_closure (hb_face_t *face, - hb_set_t *glyphs) const +void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const { - unsigned int table_index = 0; - unsigned int i = 0; - - for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) { - const pause_map_t *pause = &pauses[table_index][pause_index]; - for (; i < pause->num_lookups; i++) - hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index); - } - - for (; i < lookups[table_index].len; i++) - hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index); + for (unsigned int i = 0; i < lookups[table_index].len; i++) + hb_set_add (lookups_out, lookups[table_index][i].index); } -void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func, void *user_data) +void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func) { - if (pause_func) { - pause_info_t *p = pauses[table_index].push (); - if (likely (p)) { - p->stage = current_stage[table_index]; - p->callback.func = pause_func; - p->callback.user_data = user_data; - } + 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_face_t *face, - const hb_segment_properties_t *props, - hb_ot_map_t &m) +hb_ot_map_builder_t::compile (hb_ot_map_t &m) { - m.global_mask = 1; + m.global_mask = 1; - if (!feature_infos.len) - return; - - - /* Fetch script/language indices for GSUB/GPOS. We need these later to skip - * features not available in either table and not waste precious bits for them. */ - - hb_tag_t script_tags[3] = {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); + 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}; - unsigned int script_index[2], language_index[2]; - for (unsigned int table_index = 0; table_index < 2; table_index++) { - hb_tag_t table_tag = table_tags[table_index]; - hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index], &m.chosen_script[table_index]); - hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]); + 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.sort (); + 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].global) { - feature_infos[j].global = true; + 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].global = false; + 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]); - /* Inherit default_value from j */ } feature_infos.shrink (j + 1); } @@ -179,12 +183,13 @@ hb_ot_map_builder_t::compile (hb_face_t *face, /* Allocate bits now */ unsigned int next_bit = 1; - for (unsigned int i = 0; i < feature_infos.len; i++) { + for (unsigned int i = 0; i < feature_infos.len; i++) + { const feature_info_t *info = &feature_infos[i]; unsigned int bits_needed; - if (info->global && info->max_value == 1) + if ((info->flags & F_GLOBAL) && info->max_value == 1) /* Uses the global bit */ bits_needed = 0; else @@ -194,16 +199,24 @@ hb_ot_map_builder_t::compile (hb_face_t *face, continue; /* Feature disabled, or not enough bits. */ - bool found = false; + 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 = true; + continue; + } 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) + } + if (!found && !(info->flags & F_HAS_FALLBACK)) continue; @@ -216,7 +229,8 @@ hb_ot_map_builder_t::compile (hb_face_t *face, map->index[1] = feature_index[1]; map->stage[0] = info->stage[0]; map->stage[1] = info->stage[1]; - if (info->global && info->max_value == 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; @@ -224,66 +238,68 @@ hb_ot_map_builder_t::compile (hb_face_t *face, map->shift = next_bit; map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit); next_bit += bits_needed; - if (info->global) - m.global_mask |= (info->default_value << map->shift) & map->mask; + m.global_mask |= (info->default_value << map->shift) & map->mask; } map->_1_mask = (1 << map->shift) & map->mask; + map->needs_fallback = !found; } feature_infos.shrink (0); /* Done with these */ - add_gsub_pause (NULL, NULL); - add_gpos_pause (NULL, NULL); - - for (unsigned int table_index = 0; table_index < 2; table_index++) { - hb_tag_t table_tag = table_tags[table_index]; + 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 required_feature_index; - if (hb_ot_layout_language_get_required_feature_index (face, - table_tag, - script_index[table_index], - language_index[table_index], - &required_feature_index)) - m.add_lookups (face, table_index, required_feature_index, 1); - - unsigned int pause_index = 0; + 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) + m.add_lookups (face, table_index, + required_feature_index[table_index], + 1 /* mask */, + true /* auto_zwj */); + for (unsigned i = 0; i < m.features.len; i++) if (m.features[i].stage[table_index] == stage) - m.add_lookups (face, table_index, m.features[i].index[table_index], m.features[i].mask); + m.add_lookups (face, table_index, + m.features[i].index[table_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].sort (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 (pause_index < pauses[table_index].len && pauses[table_index][pause_index].stage == stage) { - hb_ot_map_t::pause_map_t *pause_map = m.pauses[table_index].push (); - if (likely (pause_map)) { - pause_map->num_lookups = last_num_lookups; - pause_map->callback = pauses[table_index][pause_index].callback; + 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; } - pause_index++; + stage_index++; } } } } - - diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh index e270490..0d9a0fa 100644 --- a/src/hb-ot-maxp-table.hh +++ b/src/hb-ot-maxp-table.hh @@ -30,6 +30,8 @@ #include "hb-open-type-private.hh" +namespace OT { + /* * maxp -- The Maximum Profile Table @@ -39,27 +41,31 @@ struct maxp { - static const hb_tag_t Tag = HB_OT_TAG_maxp; + static const hb_tag_t tableTag = HB_OT_TAG_maxp; - inline unsigned int get_num_glyphs (void) const { + inline unsigned int get_num_glyphs (void) const + { return numGlyphs; } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && - likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000))); + 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. */ - private: + protected: FixedVersion version; /* Version of the maxp table (0.5 or 1.0), - * 0x00005000 or 0x00010000. */ + * 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/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh index 9077c8c..21450c6 100644 --- a/src/hb-ot-name-table.hh +++ b/src/hb-ot-name-table.hh @@ -30,6 +30,8 @@ #include "hb-open-type-private.hh" +namespace OT { + /* * name -- The Naming Table @@ -54,8 +56,9 @@ struct NameRecord return 0; } - inline bool sanitize (hb_sanitize_context_t *c, void *base) { - TRACE_SANITIZE (); + 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_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset)); } @@ -72,7 +75,7 @@ struct NameRecord struct name { - static const hb_tag_t Tag = HB_OT_TAG_name; + static const hb_tag_t tableTag = HB_OT_TAG_name; inline unsigned int get_name (unsigned int platform_id, unsigned int encoding_id, @@ -96,8 +99,11 @@ struct name return length; } - inline bool sanitize_records (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + 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++) @@ -105,8 +111,9 @@ struct name return TRACE_RETURN (true); } - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); return TRACE_RETURN (c->check_struct (this) && likely (format == 0 || format == 1) && c->check_array (nameRecord, nameRecord[0].static_size, count) && @@ -114,15 +121,16 @@ struct name } /* We only implement format 0 for now. */ - private: USHORT format; /* Format selector (=0/1). */ USHORT count; /* Number of name records. */ - Offset stringOffset; /* Offset to start of string storage (from start of table). */ + 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/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh new file mode 100644 index 0000000..a77f24e --- /dev/null +++ b/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! + * May not be good-enough for presidential candidate interviews, but good-enough for us... */ + hb_bubble_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_bubble_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/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh index df85086..1710049 100644 --- a/src/hb-ot-shape-complex-arabic-table.hh +++ b/src/hb-ot-shape-complex-arabic-table.hh @@ -2,12 +2,14 @@ /* * The following table is generated by running: * - * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt + * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt * * on files with these headers: * - * # ArabicShaping-6.1.0.txt - * # Date: 2011-04-15, 23:16:00 GMT [KW] + * # ArabicShaping-7.0.0.txt + * # Date: 2014-02-14, 21:00:00 GMT [RP, KW, LI] + * # Blocks-7.0.0.txt + * # Date: 2014-04-03, 23:23:00 GMT [RP, KW] * UnicodeData.txt does not have a header. */ @@ -15,924 +17,363 @@ #define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH +#define X JOINING_TYPE_X +#define R JOINING_TYPE_R +#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[] = { - /* Arabic Characters */ - - JOINING_TYPE_U, /* 0600; ARABIC NUMBER SIGN; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0601; ARABIC SIGN SANAH; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0603; ARABIC SIGN SAFHA; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0604; ARABIC SIGN SAMVAT; U; No_Joining_Group */ - JOINING_TYPE_X, /* 0605 */ - JOINING_TYPE_X, /* 0606 */ - JOINING_TYPE_X, /* 0607 */ - JOINING_TYPE_U, /* 0608; ARABIC RAY; U; No_Joining_Group */ - JOINING_TYPE_X, /* 0609 */ - JOINING_TYPE_X, /* 060A */ - JOINING_TYPE_U, /* 060B; AFGHANI SIGN; U; No_Joining_Group */ - JOINING_TYPE_X, /* 060C */ - JOINING_TYPE_X, /* 060D */ - JOINING_TYPE_X, /* 060E */ - JOINING_TYPE_X, /* 060F */ - JOINING_TYPE_X, /* 0610 */ - JOINING_TYPE_X, /* 0611 */ - JOINING_TYPE_X, /* 0612 */ - JOINING_TYPE_X, /* 0613 */ - JOINING_TYPE_X, /* 0614 */ - JOINING_TYPE_X, /* 0615 */ - JOINING_TYPE_X, /* 0616 */ - JOINING_TYPE_X, /* 0617 */ - JOINING_TYPE_X, /* 0618 */ - JOINING_TYPE_X, /* 0619 */ - JOINING_TYPE_X, /* 061A */ - JOINING_TYPE_X, /* 061B */ - JOINING_TYPE_X, /* 061C */ - JOINING_TYPE_X, /* 061D */ - JOINING_TYPE_X, /* 061E */ - JOINING_TYPE_X, /* 061F */ - JOINING_TYPE_D, /* 0620; DOTLESS YEH WITH SEPARATE RING BELOW; D; YEH */ - JOINING_TYPE_U, /* 0621; HAMZA; U; No_Joining_Group */ - JOINING_TYPE_R, /* 0622; ALEF WITH MADDA ABOVE; R; ALEF */ - JOINING_TYPE_R, /* 0623; ALEF WITH HAMZA ABOVE; R; ALEF */ - JOINING_TYPE_R, /* 0624; WAW WITH HAMZA ABOVE; R; WAW */ - JOINING_TYPE_R, /* 0625; ALEF WITH HAMZA BELOW; R; ALEF */ - JOINING_TYPE_D, /* 0626; DOTLESS YEH WITH HAMZA ABOVE; D; YEH */ - JOINING_TYPE_R, /* 0627; ALEF; R; ALEF */ - JOINING_TYPE_D, /* 0628; BEH; D; BEH */ - JOINING_TYPE_R, /* 0629; TEH MARBUTA; R; TEH MARBUTA */ - JOINING_TYPE_D, /* 062A; DOTLESS BEH WITH 2 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 062B; DOTLESS BEH WITH 3 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 062C; HAH WITH DOT BELOW; D; HAH */ - JOINING_TYPE_D, /* 062D; HAH; D; HAH */ - JOINING_TYPE_D, /* 062E; HAH WITH DOT ABOVE; D; HAH */ - JOINING_TYPE_R, /* 062F; DAL; R; DAL */ - JOINING_TYPE_R, /* 0630; DAL WITH DOT ABOVE; R; DAL */ - JOINING_TYPE_R, /* 0631; REH; R; REH */ - JOINING_TYPE_R, /* 0632; REH WITH DOT ABOVE; R; REH */ - JOINING_TYPE_D, /* 0633; SEEN; D; SEEN */ - JOINING_TYPE_D, /* 0634; SEEN WITH 3 DOTS ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 0635; SAD; D; SAD */ - JOINING_TYPE_D, /* 0636; SAD WITH DOT ABOVE; D; SAD */ - JOINING_TYPE_D, /* 0637; TAH; D; TAH */ - JOINING_TYPE_D, /* 0638; TAH WITH DOT ABOVE; D; TAH */ - JOINING_TYPE_D, /* 0639; AIN; D; AIN */ - JOINING_TYPE_D, /* 063A; AIN WITH DOT ABOVE; D; AIN */ - JOINING_TYPE_D, /* 063B; KEHEH WITH 2 DOTS ABOVE; D; GAF */ - JOINING_TYPE_D, /* 063C; KEHEH WITH 3 DOTS BELOW; D; GAF */ - JOINING_TYPE_D, /* 063D; FARSI YEH WITH INVERTED V ABOVE; D; FARSI YEH */ - JOINING_TYPE_D, /* 063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH */ - JOINING_TYPE_D, /* 063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH */ - JOINING_TYPE_C, /* 0640; TATWEEL; C; No_Joining_Group */ - JOINING_TYPE_D, /* 0641; FEH; D; FEH */ - JOINING_TYPE_D, /* 0642; QAF; D; QAF */ - JOINING_TYPE_D, /* 0643; KAF; D; KAF */ - JOINING_TYPE_D, /* 0644; LAM; D; LAM */ - JOINING_TYPE_D, /* 0645; MEEM; D; MEEM */ - JOINING_TYPE_D, /* 0646; NOON; D; NOON */ - JOINING_TYPE_D, /* 0647; HEH; D; HEH */ - JOINING_TYPE_R, /* 0648; WAW; R; WAW */ - JOINING_TYPE_D, /* 0649; DOTLESS YEH; D; YEH */ - JOINING_TYPE_D, /* 064A; YEH; D; YEH */ - JOINING_TYPE_X, /* 064B */ - JOINING_TYPE_X, /* 064C */ - JOINING_TYPE_X, /* 064D */ - JOINING_TYPE_X, /* 064E */ - JOINING_TYPE_X, /* 064F */ - JOINING_TYPE_X, /* 0650 */ - JOINING_TYPE_X, /* 0651 */ - JOINING_TYPE_X, /* 0652 */ - JOINING_TYPE_X, /* 0653 */ - JOINING_TYPE_X, /* 0654 */ - JOINING_TYPE_X, /* 0655 */ - JOINING_TYPE_X, /* 0656 */ - JOINING_TYPE_X, /* 0657 */ - JOINING_TYPE_X, /* 0658 */ - JOINING_TYPE_X, /* 0659 */ - JOINING_TYPE_X, /* 065A */ - JOINING_TYPE_X, /* 065B */ - JOINING_TYPE_X, /* 065C */ - JOINING_TYPE_X, /* 065D */ - JOINING_TYPE_X, /* 065E */ - JOINING_TYPE_X, /* 065F */ - JOINING_TYPE_X, /* 0660 */ - JOINING_TYPE_X, /* 0661 */ - JOINING_TYPE_X, /* 0662 */ - JOINING_TYPE_X, /* 0663 */ - JOINING_TYPE_X, /* 0664 */ - JOINING_TYPE_X, /* 0665 */ - JOINING_TYPE_X, /* 0666 */ - JOINING_TYPE_X, /* 0667 */ - JOINING_TYPE_X, /* 0668 */ - JOINING_TYPE_X, /* 0669 */ - JOINING_TYPE_X, /* 066A */ - JOINING_TYPE_X, /* 066B */ - JOINING_TYPE_X, /* 066C */ - JOINING_TYPE_X, /* 066D */ - JOINING_TYPE_D, /* 066E; DOTLESS BEH; D; BEH */ - JOINING_TYPE_D, /* 066F; DOTLESS QAF; D; QAF */ - JOINING_TYPE_X, /* 0670 */ - JOINING_TYPE_R, /* 0671; ALEF WITH WASLA ABOVE; R; ALEF */ - JOINING_TYPE_R, /* 0672; ALEF WITH WAVY HAMZA ABOVE; R; ALEF */ - JOINING_TYPE_R, /* 0673; ALEF WITH WAVY HAMZA BELOW; R; ALEF */ - JOINING_TYPE_U, /* 0674; HIGH HAMZA; U; No_Joining_Group */ - JOINING_TYPE_R, /* 0675; HIGH HAMZA ALEF; R; ALEF */ - JOINING_TYPE_R, /* 0676; HIGH HAMZA WAW; R; WAW */ - JOINING_TYPE_R, /* 0677; HIGH HAMZA WAW WITH DAMMA ABOVE; R; WAW */ - JOINING_TYPE_D, /* 0678; HIGH HAMZA DOTLESS YEH; D; YEH */ - JOINING_TYPE_D, /* 0679; DOTLESS BEH WITH TAH ABOVE; D; BEH */ - JOINING_TYPE_D, /* 067A; DOTLESS BEH WITH VERTICAL 2 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 067B; DOTLESS BEH WITH VERTICAL 2 DOTS BELOW; D; BEH */ - JOINING_TYPE_D, /* 067C; DOTLESS BEH WITH ATTACHED RING BELOW AND 2 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 067D; DOTLESS BEH WITH INVERTED 3 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 067E; DOTLESS BEH WITH 3 DOTS BELOW; D; BEH */ - JOINING_TYPE_D, /* 067F; DOTLESS BEH WITH 4 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 0680; DOTLESS BEH WITH 4 DOTS BELOW; D; BEH */ - JOINING_TYPE_D, /* 0681; HAH WITH HAMZA ABOVE; D; HAH */ - JOINING_TYPE_D, /* 0682; HAH WITH VERTICAL 2 DOTS ABOVE; D; HAH */ - JOINING_TYPE_D, /* 0683; HAH WITH 2 DOTS BELOW; D; HAH */ - JOINING_TYPE_D, /* 0684; HAH WITH VERTICAL 2 DOTS BELOW; D; HAH */ - JOINING_TYPE_D, /* 0685; HAH WITH 3 DOTS ABOVE; D; HAH */ - JOINING_TYPE_D, /* 0686; HAH WITH 3 DOTS BELOW; D; HAH */ - JOINING_TYPE_D, /* 0687; HAH WITH 4 DOTS BELOW; D; HAH */ - JOINING_TYPE_R, /* 0688; DAL WITH TAH ABOVE; R; DAL */ - JOINING_TYPE_R, /* 0689; DAL WITH ATTACHED RING BELOW; R; DAL */ - JOINING_TYPE_R, /* 068A; DAL WITH DOT BELOW; R; DAL */ - JOINING_TYPE_R, /* 068B; DAL WITH DOT BELOW AND TAH ABOVE; R; DAL */ - JOINING_TYPE_R, /* 068C; DAL WITH 2 DOTS ABOVE; R; DAL */ - JOINING_TYPE_R, /* 068D; DAL WITH 2 DOTS BELOW; R; DAL */ - JOINING_TYPE_R, /* 068E; DAL WITH 3 DOTS ABOVE; R; DAL */ - JOINING_TYPE_R, /* 068F; DAL WITH INVERTED 3 DOTS ABOVE; R; DAL */ - JOINING_TYPE_R, /* 0690; DAL WITH 4 DOTS ABOVE; R; DAL */ - JOINING_TYPE_R, /* 0691; REH WITH TAH ABOVE; R; REH */ - JOINING_TYPE_R, /* 0692; REH WITH V ABOVE; R; REH */ - JOINING_TYPE_R, /* 0693; REH WITH ATTACHED RING BELOW; R; REH */ - JOINING_TYPE_R, /* 0694; REH WITH DOT BELOW; R; REH */ - JOINING_TYPE_R, /* 0695; REH WITH V BELOW; R; REH */ - JOINING_TYPE_R, /* 0696; REH WITH DOT BELOW AND DOT WITHIN; R; REH */ - JOINING_TYPE_R, /* 0697; REH WITH 2 DOTS ABOVE; R; REH */ - JOINING_TYPE_R, /* 0698; REH WITH 3 DOTS ABOVE; R; REH */ - JOINING_TYPE_R, /* 0699; REH WITH 4 DOTS ABOVE; R; REH */ - JOINING_TYPE_D, /* 069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 069B; SEEN WITH 3 DOTS BELOW; D; SEEN */ - JOINING_TYPE_D, /* 069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 069D; SAD WITH 2 DOTS BELOW; D; SAD */ - JOINING_TYPE_D, /* 069E; SAD WITH 3 DOTS ABOVE; D; SAD */ - JOINING_TYPE_D, /* 069F; TAH WITH 3 DOTS ABOVE; D; TAH */ - JOINING_TYPE_D, /* 06A0; AIN WITH 3 DOTS ABOVE; D; AIN */ - JOINING_TYPE_D, /* 06A1; DOTLESS FEH; D; FEH */ - JOINING_TYPE_D, /* 06A2; DOTLESS FEH WITH DOT BELOW; D; FEH */ - JOINING_TYPE_D, /* 06A3; FEH WITH DOT BELOW; D; FEH */ - JOINING_TYPE_D, /* 06A4; DOTLESS FEH WITH 3 DOTS ABOVE; D; FEH */ - JOINING_TYPE_D, /* 06A5; DOTLESS FEH WITH 3 DOTS BELOW; D; FEH */ - JOINING_TYPE_D, /* 06A6; DOTLESS FEH WITH 4 DOTS ABOVE; D; FEH */ - JOINING_TYPE_D, /* 06A7; DOTLESS QAF WITH DOT ABOVE; D; QAF */ - JOINING_TYPE_D, /* 06A8; DOTLESS QAF WITH 3 DOTS ABOVE; D; QAF */ - JOINING_TYPE_D, /* 06A9; KEHEH; D; GAF */ - JOINING_TYPE_D, /* 06AA; SWASH KAF; D; SWASH KAF */ - JOINING_TYPE_D, /* 06AB; KEHEH WITH ATTACHED RING BELOW; D; GAF */ - JOINING_TYPE_D, /* 06AC; KAF WITH DOT ABOVE; D; KAF */ - JOINING_TYPE_D, /* 06AD; KAF WITH 3 DOTS ABOVE; D; KAF */ - JOINING_TYPE_D, /* 06AE; KAF WITH 3 DOTS BELOW; D; KAF */ - JOINING_TYPE_D, /* 06AF; GAF; D; GAF */ - JOINING_TYPE_D, /* 06B0; GAF WITH ATTACHED RING BELOW; D; GAF */ - JOINING_TYPE_D, /* 06B1; GAF WITH 2 DOTS ABOVE; D; GAF */ - JOINING_TYPE_D, /* 06B2; GAF WITH 2 DOTS BELOW; D; GAF */ - JOINING_TYPE_D, /* 06B3; GAF WITH VERTICAL 2 DOTS BELOW; D; GAF */ - JOINING_TYPE_D, /* 06B4; GAF WITH 3 DOTS ABOVE; D; GAF */ - JOINING_TYPE_D, /* 06B5; LAM WITH V ABOVE; D; LAM */ - JOINING_TYPE_D, /* 06B6; LAM WITH DOT ABOVE; D; LAM */ - JOINING_TYPE_D, /* 06B7; LAM WITH 3 DOTS ABOVE; D; LAM */ - JOINING_TYPE_D, /* 06B8; LAM WITH 3 DOTS BELOW; D; LAM */ - JOINING_TYPE_D, /* 06B9; NOON WITH DOT BELOW; D; NOON */ - JOINING_TYPE_D, /* 06BA; DOTLESS NOON; D; NOON */ - JOINING_TYPE_D, /* 06BB; DOTLESS NOON WITH TAH ABOVE; D; NOON */ - JOINING_TYPE_D, /* 06BC; NOON WITH ATTACHED RING BELOW; D; NOON */ - JOINING_TYPE_D, /* 06BD; NYA; D; NYA */ - JOINING_TYPE_D, /* 06BE; KNOTTED HEH; D; KNOTTED HEH */ - JOINING_TYPE_D, /* 06BF; HAH WITH 3 DOTS BELOW AND DOT ABOVE; D; HAH */ - JOINING_TYPE_R, /* 06C0; DOTLESS TEH MARBUTA WITH HAMZA ABOVE; R; TEH MARBUTA */ - JOINING_TYPE_D, /* 06C1; HEH GOAL; D; HEH GOAL */ - JOINING_TYPE_D, /* 06C2; HEH GOAL WITH HAMZA ABOVE; D; HEH GOAL */ - JOINING_TYPE_R, /* 06C3; TEH MARBUTA GOAL; R; TEH MARBUTA GOAL */ - JOINING_TYPE_R, /* 06C4; WAW WITH ATTACHED RING WITHIN; R; WAW */ - JOINING_TYPE_R, /* 06C5; WAW WITH BAR; R; WAW */ - JOINING_TYPE_R, /* 06C6; WAW WITH V ABOVE; R; WAW */ - JOINING_TYPE_R, /* 06C7; WAW WITH DAMMA ABOVE; R; WAW */ - JOINING_TYPE_R, /* 06C8; WAW WITH ALEF ABOVE; R; WAW */ - JOINING_TYPE_R, /* 06C9; WAW WITH INVERTED V ABOVE; R; WAW */ - JOINING_TYPE_R, /* 06CA; WAW WITH 2 DOTS ABOVE; R; WAW */ - JOINING_TYPE_R, /* 06CB; WAW WITH 3 DOTS ABOVE; R; WAW */ - JOINING_TYPE_D, /* 06CC; FARSI YEH; D; FARSI YEH */ - JOINING_TYPE_R, /* 06CD; YEH WITH TAIL; R; YEH WITH TAIL */ - JOINING_TYPE_D, /* 06CE; FARSI YEH WITH V ABOVE; D; FARSI YEH */ - JOINING_TYPE_R, /* 06CF; WAW WITH DOT ABOVE; R; WAW */ - JOINING_TYPE_D, /* 06D0; DOTLESS YEH WITH VERTICAL 2 DOTS BELOW; D; YEH */ - JOINING_TYPE_D, /* 06D1; DOTLESS YEH WITH 3 DOTS BELOW; D; YEH */ - JOINING_TYPE_R, /* 06D2; YEH BARREE; R; YEH BARREE */ - JOINING_TYPE_R, /* 06D3; YEH BARREE WITH HAMZA ABOVE; R; YEH BARREE */ - JOINING_TYPE_X, /* 06D4 */ - JOINING_TYPE_R, /* 06D5; DOTLESS TEH MARBUTA; R; TEH MARBUTA */ - JOINING_TYPE_X, /* 06D6 */ - JOINING_TYPE_X, /* 06D7 */ - JOINING_TYPE_X, /* 06D8 */ - JOINING_TYPE_X, /* 06D9 */ - JOINING_TYPE_X, /* 06DA */ - JOINING_TYPE_X, /* 06DB */ - JOINING_TYPE_X, /* 06DC */ - JOINING_TYPE_U, /* 06DD; ARABIC END OF AYAH; U; No_Joining_Group */ - JOINING_TYPE_X, /* 06DE */ - JOINING_TYPE_X, /* 06DF */ - JOINING_TYPE_X, /* 06E0 */ - JOINING_TYPE_X, /* 06E1 */ - JOINING_TYPE_X, /* 06E2 */ - JOINING_TYPE_X, /* 06E3 */ - JOINING_TYPE_X, /* 06E4 */ - JOINING_TYPE_X, /* 06E5 */ - JOINING_TYPE_X, /* 06E6 */ - JOINING_TYPE_X, /* 06E7 */ - JOINING_TYPE_X, /* 06E8 */ - JOINING_TYPE_X, /* 06E9 */ - JOINING_TYPE_X, /* 06EA */ - JOINING_TYPE_X, /* 06EB */ - JOINING_TYPE_X, /* 06EC */ - JOINING_TYPE_X, /* 06ED */ - JOINING_TYPE_R, /* 06EE; DAL WITH INVERTED V ABOVE; R; DAL */ - JOINING_TYPE_R, /* 06EF; REH WITH INVERTED V ABOVE; R; REH */ - JOINING_TYPE_X, /* 06F0 */ - JOINING_TYPE_X, /* 06F1 */ - JOINING_TYPE_X, /* 06F2 */ - JOINING_TYPE_X, /* 06F3 */ - JOINING_TYPE_X, /* 06F4 */ - JOINING_TYPE_X, /* 06F5 */ - JOINING_TYPE_X, /* 06F6 */ - JOINING_TYPE_X, /* 06F7 */ - JOINING_TYPE_X, /* 06F8 */ - JOINING_TYPE_X, /* 06F9 */ - JOINING_TYPE_D, /* 06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 06FB; SAD WITH DOT BELOW AND DOT ABOVE; D; SAD */ - JOINING_TYPE_D, /* 06FC; AIN WITH DOT BELOW AND DOT ABOVE; D; AIN */ - JOINING_TYPE_X, /* 06FD */ - JOINING_TYPE_X, /* 06FE */ - JOINING_TYPE_D, /* 06FF; KNOTTED HEH WITH INVERTED V ABOVE; D; KNOTTED HEH */ - - /* Syriac Characters */ - - JOINING_TYPE_X, /* 0700 */ - JOINING_TYPE_X, /* 0701 */ - JOINING_TYPE_X, /* 0702 */ - JOINING_TYPE_X, /* 0703 */ - JOINING_TYPE_X, /* 0704 */ - JOINING_TYPE_X, /* 0705 */ - JOINING_TYPE_X, /* 0706 */ - JOINING_TYPE_X, /* 0707 */ - JOINING_TYPE_X, /* 0708 */ - JOINING_TYPE_X, /* 0709 */ - JOINING_TYPE_X, /* 070A */ - JOINING_TYPE_X, /* 070B */ - JOINING_TYPE_X, /* 070C */ - JOINING_TYPE_X, /* 070D */ - JOINING_TYPE_X, /* 070E */ - JOINING_TYPE_X, /* 070F */ - JOINING_GROUP_ALAPH, /* 0710; ALAPH; R; ALAPH */ - JOINING_TYPE_X, /* 0711 */ - JOINING_TYPE_D, /* 0712; BETH; D; BETH */ - JOINING_TYPE_D, /* 0713; GAMAL; D; GAMAL */ - JOINING_TYPE_D, /* 0714; GAMAL GARSHUNI; D; GAMAL */ - JOINING_GROUP_DALATH_RISH, /* 0715; DALATH; R; DALATH RISH */ - JOINING_GROUP_DALATH_RISH, /* 0716; DOTLESS DALATH RISH; R; DALATH RISH */ - JOINING_TYPE_R, /* 0717; HE; R; HE */ - JOINING_TYPE_R, /* 0718; WAW; R; SYRIAC WAW */ - JOINING_TYPE_R, /* 0719; ZAIN; R; ZAIN */ - JOINING_TYPE_D, /* 071A; HETH; D; HETH */ - JOINING_TYPE_D, /* 071B; TETH; D; TETH */ - JOINING_TYPE_D, /* 071C; TETH GARSHUNI; D; TETH */ - JOINING_TYPE_D, /* 071D; YUDH; D; YUDH */ - JOINING_TYPE_R, /* 071E; YUDH HE; R; YUDH HE */ - JOINING_TYPE_D, /* 071F; KAPH; D; KAPH */ - JOINING_TYPE_D, /* 0720; LAMADH; D; LAMADH */ - JOINING_TYPE_D, /* 0721; MIM; D; MIM */ - JOINING_TYPE_D, /* 0722; NUN; D; NUN */ - JOINING_TYPE_D, /* 0723; SEMKATH; D; SEMKATH */ - JOINING_TYPE_D, /* 0724; FINAL SEMKATH; D; FINAL SEMKATH */ - JOINING_TYPE_D, /* 0725; E; D; E */ - JOINING_TYPE_D, /* 0726; PE; D; PE */ - JOINING_TYPE_D, /* 0727; REVERSED PE; D; REVERSED PE */ - JOINING_TYPE_R, /* 0728; SADHE; R; SADHE */ - JOINING_TYPE_D, /* 0729; QAPH; D; QAPH */ - JOINING_GROUP_DALATH_RISH, /* 072A; RISH; R; DALATH RISH */ - JOINING_TYPE_D, /* 072B; SHIN; D; SHIN */ - JOINING_TYPE_R, /* 072C; TAW; R; TAW */ - JOINING_TYPE_D, /* 072D; PERSIAN BHETH; D; BETH */ - JOINING_TYPE_D, /* 072E; PERSIAN GHAMAL; D; GAMAL */ - JOINING_GROUP_DALATH_RISH, /* 072F; PERSIAN DHALATH; R; DALATH RISH */ - JOINING_TYPE_X, /* 0730 */ - JOINING_TYPE_X, /* 0731 */ - JOINING_TYPE_X, /* 0732 */ - JOINING_TYPE_X, /* 0733 */ - JOINING_TYPE_X, /* 0734 */ - JOINING_TYPE_X, /* 0735 */ - JOINING_TYPE_X, /* 0736 */ - JOINING_TYPE_X, /* 0737 */ - JOINING_TYPE_X, /* 0738 */ - JOINING_TYPE_X, /* 0739 */ - JOINING_TYPE_X, /* 073A */ - JOINING_TYPE_X, /* 073B */ - JOINING_TYPE_X, /* 073C */ - JOINING_TYPE_X, /* 073D */ - JOINING_TYPE_X, /* 073E */ - JOINING_TYPE_X, /* 073F */ - JOINING_TYPE_X, /* 0740 */ - JOINING_TYPE_X, /* 0741 */ - JOINING_TYPE_X, /* 0742 */ - JOINING_TYPE_X, /* 0743 */ - JOINING_TYPE_X, /* 0744 */ - JOINING_TYPE_X, /* 0745 */ - JOINING_TYPE_X, /* 0746 */ - JOINING_TYPE_X, /* 0747 */ - JOINING_TYPE_X, /* 0748 */ - JOINING_TYPE_X, /* 0749 */ - JOINING_TYPE_X, /* 074A */ - JOINING_TYPE_X, /* 074B */ - JOINING_TYPE_X, /* 074C */ - JOINING_TYPE_R, /* 074D; SOGDIAN ZHAIN; R; ZHAIN */ - JOINING_TYPE_D, /* 074E; SOGDIAN KHAPH; D; KHAPH */ - JOINING_TYPE_D, /* 074F; SOGDIAN FE; D; FE */ - - /* Arabic Supplement Characters */ - - JOINING_TYPE_D, /* 0750; DOTLESS BEH WITH HORIZONTAL 3 DOTS BELOW; D; BEH */ - JOINING_TYPE_D, /* 0751; BEH WITH 3 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 0752; DOTLESS BEH WITH INVERTED 3 DOTS BELOW; D; BEH */ - JOINING_TYPE_D, /* 0753; DOTLESS BEH WITH INVERTED 3 DOTS BELOW AND 2 DOTS ABOVE; D; BEH */ - JOINING_TYPE_D, /* 0754; DOTLESS BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH */ - JOINING_TYPE_D, /* 0755; DOTLESS BEH WITH INVERTED V BELOW; D; BEH */ - JOINING_TYPE_D, /* 0756; DOTLESS BEH WITH V ABOVE; D; BEH */ - JOINING_TYPE_D, /* 0757; HAH WITH 2 DOTS ABOVE; D; HAH */ - JOINING_TYPE_D, /* 0758; HAH WITH INVERTED 3 DOTS BELOW; D; HAH */ - JOINING_TYPE_R, /* 0759; DAL WITH VERTICAL 2 DOTS BELOW AND TAH ABOVE; R; DAL */ - JOINING_TYPE_R, /* 075A; DAL WITH INVERTED V BELOW; R; DAL */ - JOINING_TYPE_R, /* 075B; REH WITH BAR; R; REH */ - JOINING_TYPE_D, /* 075C; SEEN WITH 4 DOTS ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 075D; AIN WITH 2 DOTS ABOVE; D; AIN */ - JOINING_TYPE_D, /* 075E; AIN WITH INVERTED 3 DOTS ABOVE; D; AIN */ - JOINING_TYPE_D, /* 075F; AIN WITH VERTICAL 2 DOTS ABOVE; D; AIN */ - JOINING_TYPE_D, /* 0760; DOTLESS FEH WITH 2 DOTS BELOW; D; FEH */ - JOINING_TYPE_D, /* 0761; DOTLESS FEH WITH INVERTED 3 DOTS BELOW; D; FEH */ - JOINING_TYPE_D, /* 0762; KEHEH WITH DOT ABOVE; D; GAF */ - JOINING_TYPE_D, /* 0763; KEHEH WITH 3 DOTS ABOVE; D; GAF */ - JOINING_TYPE_D, /* 0764; KEHEH WITH INVERTED 3 DOTS BELOW; D; GAF */ - JOINING_TYPE_D, /* 0765; MEEM WITH DOT ABOVE; D; MEEM */ - JOINING_TYPE_D, /* 0766; MEEM WITH DOT BELOW; D; MEEM */ - JOINING_TYPE_D, /* 0767; NOON WITH 2 DOTS BELOW; D; NOON */ - JOINING_TYPE_D, /* 0768; NOON WITH TAH ABOVE; D; NOON */ - JOINING_TYPE_D, /* 0769; NOON WITH V ABOVE; D; NOON */ - JOINING_TYPE_D, /* 076A; LAM WITH BAR; D; LAM */ - JOINING_TYPE_R, /* 076B; REH WITH VERTICAL 2 DOTS ABOVE; R; REH */ - JOINING_TYPE_R, /* 076C; REH WITH HAMZA ABOVE; R; REH */ - JOINING_TYPE_D, /* 076D; SEEN WITH VERTICAL 2 DOTS ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 076E; HAH WITH TAH BELOW; D; HAH */ - JOINING_TYPE_D, /* 076F; HAH WITH TAH AND 2 DOTS BELOW; D; HAH */ - JOINING_TYPE_D, /* 0770; SEEN WITH 2 DOTS AND TAH ABOVE; D; SEEN */ - JOINING_TYPE_R, /* 0771; REH WITH 2 DOTS AND TAH ABOVE; R; REH */ - JOINING_TYPE_D, /* 0772; HAH WITH TAH ABOVE; D; HAH */ - JOINING_TYPE_R, /* 0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF */ - JOINING_TYPE_R, /* 0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF */ - JOINING_TYPE_D, /* 0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH */ - JOINING_TYPE_D, /* 0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH */ - JOINING_TYPE_D, /* 0777; DOTLESS YEH WITH DIGIT FOUR BELOW; D; YEH */ - JOINING_TYPE_R, /* 0778; WAW WITH DIGIT TWO ABOVE; R; WAW */ - JOINING_TYPE_R, /* 0779; WAW WITH DIGIT THREE ABOVE; R; WAW */ - JOINING_TYPE_D, /* 077A; BURUSHASKI YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE */ - JOINING_TYPE_D, /* 077B; BURUSHASKI YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE */ - JOINING_TYPE_D, /* 077C; HAH WITH DIGIT FOUR BELOW; D; HAH */ - JOINING_TYPE_D, /* 077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 077E; SEEN WITH INVERTED V ABOVE; D; SEEN */ - JOINING_TYPE_D, /* 077F; KAF WITH 2 DOTS ABOVE; D; KAF */ - - /* N'Ko Characters */ - - JOINING_TYPE_X, /* 0780 */ - JOINING_TYPE_X, /* 0781 */ - JOINING_TYPE_X, /* 0782 */ - JOINING_TYPE_X, /* 0783 */ - JOINING_TYPE_X, /* 0784 */ - JOINING_TYPE_X, /* 0785 */ - JOINING_TYPE_X, /* 0786 */ - JOINING_TYPE_X, /* 0787 */ - JOINING_TYPE_X, /* 0788 */ - JOINING_TYPE_X, /* 0789 */ - JOINING_TYPE_X, /* 078A */ - JOINING_TYPE_X, /* 078B */ - JOINING_TYPE_X, /* 078C */ - JOINING_TYPE_X, /* 078D */ - JOINING_TYPE_X, /* 078E */ - JOINING_TYPE_X, /* 078F */ - JOINING_TYPE_X, /* 0790 */ - JOINING_TYPE_X, /* 0791 */ - JOINING_TYPE_X, /* 0792 */ - JOINING_TYPE_X, /* 0793 */ - JOINING_TYPE_X, /* 0794 */ - JOINING_TYPE_X, /* 0795 */ - JOINING_TYPE_X, /* 0796 */ - JOINING_TYPE_X, /* 0797 */ - JOINING_TYPE_X, /* 0798 */ - JOINING_TYPE_X, /* 0799 */ - JOINING_TYPE_X, /* 079A */ - JOINING_TYPE_X, /* 079B */ - JOINING_TYPE_X, /* 079C */ - JOINING_TYPE_X, /* 079D */ - JOINING_TYPE_X, /* 079E */ - JOINING_TYPE_X, /* 079F */ - JOINING_TYPE_X, /* 07A0 */ - JOINING_TYPE_X, /* 07A1 */ - JOINING_TYPE_X, /* 07A2 */ - JOINING_TYPE_X, /* 07A3 */ - JOINING_TYPE_X, /* 07A4 */ - JOINING_TYPE_X, /* 07A5 */ - JOINING_TYPE_X, /* 07A6 */ - JOINING_TYPE_X, /* 07A7 */ - JOINING_TYPE_X, /* 07A8 */ - JOINING_TYPE_X, /* 07A9 */ - JOINING_TYPE_X, /* 07AA */ - JOINING_TYPE_X, /* 07AB */ - JOINING_TYPE_X, /* 07AC */ - JOINING_TYPE_X, /* 07AD */ - JOINING_TYPE_X, /* 07AE */ - JOINING_TYPE_X, /* 07AF */ - JOINING_TYPE_X, /* 07B0 */ - JOINING_TYPE_X, /* 07B1 */ - JOINING_TYPE_X, /* 07B2 */ - JOINING_TYPE_X, /* 07B3 */ - JOINING_TYPE_X, /* 07B4 */ - JOINING_TYPE_X, /* 07B5 */ - JOINING_TYPE_X, /* 07B6 */ - JOINING_TYPE_X, /* 07B7 */ - JOINING_TYPE_X, /* 07B8 */ - JOINING_TYPE_X, /* 07B9 */ - JOINING_TYPE_X, /* 07BA */ - JOINING_TYPE_X, /* 07BB */ - JOINING_TYPE_X, /* 07BC */ - JOINING_TYPE_X, /* 07BD */ - JOINING_TYPE_X, /* 07BE */ - JOINING_TYPE_X, /* 07BF */ - JOINING_TYPE_X, /* 07C0 */ - JOINING_TYPE_X, /* 07C1 */ - JOINING_TYPE_X, /* 07C2 */ - JOINING_TYPE_X, /* 07C3 */ - JOINING_TYPE_X, /* 07C4 */ - JOINING_TYPE_X, /* 07C5 */ - JOINING_TYPE_X, /* 07C6 */ - JOINING_TYPE_X, /* 07C7 */ - JOINING_TYPE_X, /* 07C8 */ - JOINING_TYPE_X, /* 07C9 */ - JOINING_TYPE_D, /* 07CA; NKO A; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07CB; NKO EE; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07CC; NKO I; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07CD; NKO E; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07CE; NKO U; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07CF; NKO OO; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D0; NKO O; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D1; NKO DAGBASINNA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D2; NKO N; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D3; NKO BA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D4; NKO PA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D5; NKO TA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D6; NKO JA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D7; NKO CHA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D8; NKO DA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07D9; NKO RA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DA; NKO RRA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DB; NKO SA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DC; NKO GBA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DD; NKO FA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DE; NKO KA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07DF; NKO LA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E0; NKO NA WOLOSO; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E1; NKO MA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E2; NKO NYA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E3; NKO NA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E4; NKO HA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E5; NKO WA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E6; NKO YA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E7; NKO NYA WOLOSO; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E8; NKO JONA JA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07E9; NKO JONA CHA; D; No_Joining_Group */ - JOINING_TYPE_D, /* 07EA; NKO JONA RA; D; No_Joining_Group */ - JOINING_TYPE_X, /* 07EB */ - JOINING_TYPE_X, /* 07EC */ - JOINING_TYPE_X, /* 07ED */ - JOINING_TYPE_X, /* 07EE */ - JOINING_TYPE_X, /* 07EF */ - JOINING_TYPE_X, /* 07F0 */ - JOINING_TYPE_X, /* 07F1 */ - JOINING_TYPE_X, /* 07F2 */ - JOINING_TYPE_X, /* 07F3 */ - JOINING_TYPE_X, /* 07F4 */ - JOINING_TYPE_X, /* 07F5 */ - JOINING_TYPE_X, /* 07F6 */ - JOINING_TYPE_X, /* 07F7 */ - JOINING_TYPE_X, /* 07F8 */ - JOINING_TYPE_X, /* 07F9 */ - JOINING_TYPE_C, /* 07FA; NKO LAJANYALAN; C; No_Joining_Group */ - - /* Mandaic Characters */ - - JOINING_TYPE_X, /* 07FB */ - JOINING_TYPE_X, /* 07FC */ - JOINING_TYPE_X, /* 07FD */ - JOINING_TYPE_X, /* 07FE */ - JOINING_TYPE_X, /* 07FF */ - JOINING_TYPE_X, /* 0800 */ - JOINING_TYPE_X, /* 0801 */ - JOINING_TYPE_X, /* 0802 */ - JOINING_TYPE_X, /* 0803 */ - JOINING_TYPE_X, /* 0804 */ - JOINING_TYPE_X, /* 0805 */ - JOINING_TYPE_X, /* 0806 */ - JOINING_TYPE_X, /* 0807 */ - JOINING_TYPE_X, /* 0808 */ - JOINING_TYPE_X, /* 0809 */ - JOINING_TYPE_X, /* 080A */ - JOINING_TYPE_X, /* 080B */ - JOINING_TYPE_X, /* 080C */ - JOINING_TYPE_X, /* 080D */ - JOINING_TYPE_X, /* 080E */ - JOINING_TYPE_X, /* 080F */ - JOINING_TYPE_X, /* 0810 */ - JOINING_TYPE_X, /* 0811 */ - JOINING_TYPE_X, /* 0812 */ - JOINING_TYPE_X, /* 0813 */ - JOINING_TYPE_X, /* 0814 */ - JOINING_TYPE_X, /* 0815 */ - JOINING_TYPE_X, /* 0816 */ - JOINING_TYPE_X, /* 0817 */ - JOINING_TYPE_X, /* 0818 */ - JOINING_TYPE_X, /* 0819 */ - JOINING_TYPE_X, /* 081A */ - JOINING_TYPE_X, /* 081B */ - JOINING_TYPE_X, /* 081C */ - JOINING_TYPE_X, /* 081D */ - JOINING_TYPE_X, /* 081E */ - JOINING_TYPE_X, /* 081F */ - JOINING_TYPE_X, /* 0820 */ - JOINING_TYPE_X, /* 0821 */ - JOINING_TYPE_X, /* 0822 */ - JOINING_TYPE_X, /* 0823 */ - JOINING_TYPE_X, /* 0824 */ - JOINING_TYPE_X, /* 0825 */ - JOINING_TYPE_X, /* 0826 */ - JOINING_TYPE_X, /* 0827 */ - JOINING_TYPE_X, /* 0828 */ - JOINING_TYPE_X, /* 0829 */ - JOINING_TYPE_X, /* 082A */ - JOINING_TYPE_X, /* 082B */ - JOINING_TYPE_X, /* 082C */ - JOINING_TYPE_X, /* 082D */ - JOINING_TYPE_X, /* 082E */ - JOINING_TYPE_X, /* 082F */ - JOINING_TYPE_X, /* 0830 */ - JOINING_TYPE_X, /* 0831 */ - JOINING_TYPE_X, /* 0832 */ - JOINING_TYPE_X, /* 0833 */ - JOINING_TYPE_X, /* 0834 */ - JOINING_TYPE_X, /* 0835 */ - JOINING_TYPE_X, /* 0836 */ - JOINING_TYPE_X, /* 0837 */ - JOINING_TYPE_X, /* 0838 */ - JOINING_TYPE_X, /* 0839 */ - JOINING_TYPE_X, /* 083A */ - JOINING_TYPE_X, /* 083B */ - JOINING_TYPE_X, /* 083C */ - JOINING_TYPE_X, /* 083D */ - JOINING_TYPE_X, /* 083E */ - JOINING_TYPE_X, /* 083F */ - JOINING_TYPE_R, /* 0840; MANDAIC HALQA; R; No_Joining_Group */ - JOINING_TYPE_D, /* 0841; MANDAIC AB; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0842; MANDAIC AG; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0843; MANDAIC AD; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0844; MANDAIC AH; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0845; MANDAIC USHENNA; D; No_Joining_Group */ - JOINING_TYPE_R, /* 0846; MANDAIC AZ; R; No_Joining_Group */ - JOINING_TYPE_D, /* 0847; MANDAIC IT; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0848; MANDAIC ATT; D; No_Joining_Group */ - JOINING_TYPE_R, /* 0849; MANDAIC AKSA; R; No_Joining_Group */ - JOINING_TYPE_D, /* 084A; MANDAIC AK; D; No_Joining_Group */ - JOINING_TYPE_D, /* 084B; MANDAIC AL; D; No_Joining_Group */ - JOINING_TYPE_D, /* 084C; MANDAIC AM; D; No_Joining_Group */ - JOINING_TYPE_D, /* 084D; MANDAIC AN; D; No_Joining_Group */ - JOINING_TYPE_D, /* 084E; MANDAIC AS; D; No_Joining_Group */ - JOINING_TYPE_R, /* 084F; MANDAIC IN; R; No_Joining_Group */ - JOINING_TYPE_D, /* 0850; MANDAIC AP; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0851; MANDAIC ASZ; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0852; MANDAIC AQ; D; No_Joining_Group */ - JOINING_TYPE_D, /* 0853; MANDAIC AR; D; No_Joining_Group */ - JOINING_TYPE_R, /* 0854; MANDAIC ASH; R; No_Joining_Group */ - JOINING_TYPE_D, /* 0855; MANDAIC AT; D; No_Joining_Group */ - JOINING_TYPE_U, /* 0856; MANDAIC DUSHENNA; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0857; MANDAIC KAD; U; No_Joining_Group */ - JOINING_TYPE_U, /* 0858; MANDAIC AIN; U; No_Joining_Group */ - - /* Arabic Extended-A Characters */ - - JOINING_TYPE_X, /* 0859 */ - JOINING_TYPE_X, /* 085A */ - JOINING_TYPE_X, /* 085B */ - JOINING_TYPE_X, /* 085C */ - JOINING_TYPE_X, /* 085D */ - JOINING_TYPE_X, /* 085E */ - JOINING_TYPE_X, /* 085F */ - JOINING_TYPE_X, /* 0860 */ - JOINING_TYPE_X, /* 0861 */ - JOINING_TYPE_X, /* 0862 */ - JOINING_TYPE_X, /* 0863 */ - JOINING_TYPE_X, /* 0864 */ - JOINING_TYPE_X, /* 0865 */ - JOINING_TYPE_X, /* 0866 */ - JOINING_TYPE_X, /* 0867 */ - JOINING_TYPE_X, /* 0868 */ - JOINING_TYPE_X, /* 0869 */ - JOINING_TYPE_X, /* 086A */ - JOINING_TYPE_X, /* 086B */ - JOINING_TYPE_X, /* 086C */ - JOINING_TYPE_X, /* 086D */ - JOINING_TYPE_X, /* 086E */ - JOINING_TYPE_X, /* 086F */ - JOINING_TYPE_X, /* 0870 */ - JOINING_TYPE_X, /* 0871 */ - JOINING_TYPE_X, /* 0872 */ - JOINING_TYPE_X, /* 0873 */ - JOINING_TYPE_X, /* 0874 */ - JOINING_TYPE_X, /* 0875 */ - JOINING_TYPE_X, /* 0876 */ - JOINING_TYPE_X, /* 0877 */ - JOINING_TYPE_X, /* 0878 */ - JOINING_TYPE_X, /* 0879 */ - JOINING_TYPE_X, /* 087A */ - JOINING_TYPE_X, /* 087B */ - JOINING_TYPE_X, /* 087C */ - JOINING_TYPE_X, /* 087D */ - JOINING_TYPE_X, /* 087E */ - JOINING_TYPE_X, /* 087F */ - JOINING_TYPE_X, /* 0880 */ - JOINING_TYPE_X, /* 0881 */ - JOINING_TYPE_X, /* 0882 */ - JOINING_TYPE_X, /* 0883 */ - JOINING_TYPE_X, /* 0884 */ - JOINING_TYPE_X, /* 0885 */ - JOINING_TYPE_X, /* 0886 */ - JOINING_TYPE_X, /* 0887 */ - JOINING_TYPE_X, /* 0888 */ - JOINING_TYPE_X, /* 0889 */ - JOINING_TYPE_X, /* 088A */ - JOINING_TYPE_X, /* 088B */ - JOINING_TYPE_X, /* 088C */ - JOINING_TYPE_X, /* 088D */ - JOINING_TYPE_X, /* 088E */ - JOINING_TYPE_X, /* 088F */ - JOINING_TYPE_X, /* 0890 */ - JOINING_TYPE_X, /* 0891 */ - JOINING_TYPE_X, /* 0892 */ - JOINING_TYPE_X, /* 0893 */ - JOINING_TYPE_X, /* 0894 */ - JOINING_TYPE_X, /* 0895 */ - JOINING_TYPE_X, /* 0896 */ - JOINING_TYPE_X, /* 0897 */ - JOINING_TYPE_X, /* 0898 */ - JOINING_TYPE_X, /* 0899 */ - JOINING_TYPE_X, /* 089A */ - JOINING_TYPE_X, /* 089B */ - JOINING_TYPE_X, /* 089C */ - JOINING_TYPE_X, /* 089D */ - JOINING_TYPE_X, /* 089E */ - JOINING_TYPE_X, /* 089F */ - JOINING_TYPE_D, /* 08A0; DOTLESS BEH WITH V BELOW; D; BEH */ - JOINING_TYPE_X, /* 08A1 */ - JOINING_TYPE_D, /* 08A2; HAH WITH DOT BELOW AND 2 DOTS ABOVE; D; HAH */ - JOINING_TYPE_D, /* 08A3; TAH WITH 2 DOTS ABOVE; D; TAH */ - JOINING_TYPE_D, /* 08A4; DOTLESS FEH WITH DOT BELOW AND 3 DOTS ABOVE; D; FEH */ - JOINING_TYPE_D, /* 08A5; QAF WITH DOT BELOW; D; QAF */ - JOINING_TYPE_D, /* 08A6; LAM WITH DOUBLE BAR; D; LAM */ - JOINING_TYPE_D, /* 08A7; MEEM WITH 3 DOTS ABOVE; D; MEEM */ - JOINING_TYPE_D, /* 08A8; YEH WITH HAMZA ABOVE; D; YEH */ - JOINING_TYPE_D, /* 08A9; YEH WITH DOT ABOVE; D; YEH */ - JOINING_TYPE_R, /* 08AA; REH WITH LOOP; R; REH */ - JOINING_TYPE_R, /* 08AB; WAW WITH DOT WITHIN; R; WAW */ - JOINING_TYPE_R, /* 08AC; ROHINGYA YEH; R; ROHINGYA YEH */ +#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, + +#define joining_offset_0x1806u 691 + + /* 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,U,U,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 856 + + /* General Punctuation */ + + /* 2000 */ U,C, + +#define joining_offset_0x2066u 858 + + /* General Punctuation */ + + /* 2060 */ U,U,U,U, + +#define joining_offset_0xa840u 862 + + /* 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 914 + + /* 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 962 + + /* 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, + +}; /* Table items: 1010; occupancy: 57% */ + + +static unsigned int +joining_type (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range (u, 0x0600u, 0x08B2u)) 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, 0x200Du)) return joining_table[u - 0x200Cu + joining_offset_0x200cu]; + if (hb_in_range (u, 0x2066u, 0x2069u)) return joining_table[u - 0x2066u + joining_offset_0x2066u]; + 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; + + default: + break; + } + return X; +} -#define JOINING_TABLE_FIRST 0x0600 -#define JOINING_TABLE_LAST 0x08AC +#undef X +#undef R +#undef U +#undef A +#undef DR +#undef L +#undef C +#undef D static const uint16_t shaping_table[][4] = { - {0x0621, 0x0621, 0x0621, 0xFE80}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */ - {0x0622, 0x0622, 0xFE82, 0xFE81}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ - {0x0623, 0x0623, 0xFE84, 0xFE83}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ - {0x0624, 0x0624, 0xFE86, 0xFE85}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ - {0x0625, 0x0625, 0xFE88, 0xFE87}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ - {0xFE8B, 0xFE8C, 0xFE8A, 0xFE89}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ - {0x0627, 0x0627, 0xFE8E, 0xFE8D}, /* U+0627 ARABIC LETTER ALEF */ - {0xFE91, 0xFE92, 0xFE90, 0xFE8F}, /* U+0628 ARABIC LETTER BEH */ - {0x0629, 0x0629, 0xFE94, 0xFE93}, /* U+0629 ARABIC LETTER TEH MARBUTA */ - {0xFE97, 0xFE98, 0xFE96, 0xFE95}, /* U+062A ARABIC LETTER TEH */ - {0xFE9B, 0xFE9C, 0xFE9A, 0xFE99}, /* U+062B ARABIC LETTER THEH */ - {0xFE9F, 0xFEA0, 0xFE9E, 0xFE9D}, /* U+062C ARABIC LETTER JEEM */ - {0xFEA3, 0xFEA4, 0xFEA2, 0xFEA1}, /* U+062D ARABIC LETTER HAH */ - {0xFEA7, 0xFEA8, 0xFEA6, 0xFEA5}, /* U+062E ARABIC LETTER KHAH */ - {0x062F, 0x062F, 0xFEAA, 0xFEA9}, /* U+062F ARABIC LETTER DAL */ - {0x0630, 0x0630, 0xFEAC, 0xFEAB}, /* U+0630 ARABIC LETTER THAL */ - {0x0631, 0x0631, 0xFEAE, 0xFEAD}, /* U+0631 ARABIC LETTER REH */ - {0x0632, 0x0632, 0xFEB0, 0xFEAF}, /* U+0632 ARABIC LETTER ZAIN */ - {0xFEB3, 0xFEB4, 0xFEB2, 0xFEB1}, /* U+0633 ARABIC LETTER SEEN */ - {0xFEB7, 0xFEB8, 0xFEB6, 0xFEB5}, /* U+0634 ARABIC LETTER SHEEN */ - {0xFEBB, 0xFEBC, 0xFEBA, 0xFEB9}, /* U+0635 ARABIC LETTER SAD */ - {0xFEBF, 0xFEC0, 0xFEBE, 0xFEBD}, /* U+0636 ARABIC LETTER DAD */ - {0xFEC3, 0xFEC4, 0xFEC2, 0xFEC1}, /* U+0637 ARABIC LETTER TAH */ - {0xFEC7, 0xFEC8, 0xFEC6, 0xFEC5}, /* U+0638 ARABIC LETTER ZAH */ - {0xFECB, 0xFECC, 0xFECA, 0xFEC9}, /* U+0639 ARABIC LETTER AIN */ - {0xFECF, 0xFED0, 0xFECE, 0xFECD}, /* U+063A ARABIC LETTER GHAIN */ - {0x063B, 0x063B, 0x063B, 0x063B}, /* U+063B */ - {0x063C, 0x063C, 0x063C, 0x063C}, /* U+063C */ - {0x063D, 0x063D, 0x063D, 0x063D}, /* U+063D */ - {0x063E, 0x063E, 0x063E, 0x063E}, /* U+063E */ - {0x063F, 0x063F, 0x063F, 0x063F}, /* U+063F */ - {0x0640, 0x0640, 0x0640, 0x0640}, /* U+0640 */ - {0xFED3, 0xFED4, 0xFED2, 0xFED1}, /* U+0641 ARABIC LETTER FEH */ - {0xFED7, 0xFED8, 0xFED6, 0xFED5}, /* U+0642 ARABIC LETTER QAF */ - {0xFEDB, 0xFEDC, 0xFEDA, 0xFED9}, /* U+0643 ARABIC LETTER KAF */ - {0xFEDF, 0xFEE0, 0xFEDE, 0xFEDD}, /* U+0644 ARABIC LETTER LAM */ - {0xFEE3, 0xFEE4, 0xFEE2, 0xFEE1}, /* U+0645 ARABIC LETTER MEEM */ - {0xFEE7, 0xFEE8, 0xFEE6, 0xFEE5}, /* U+0646 ARABIC LETTER NOON */ - {0xFEEB, 0xFEEC, 0xFEEA, 0xFEE9}, /* U+0647 ARABIC LETTER HEH */ - {0x0648, 0x0648, 0xFEEE, 0xFEED}, /* U+0648 ARABIC LETTER WAW */ - {0xFBE8, 0xFBE9, 0xFEF0, 0xFEEF}, /* U+0649 ARABIC LETTER */ - {0xFEF3, 0xFEF4, 0xFEF2, 0xFEF1}, /* U+064A ARABIC LETTER YEH */ - {0x064B, 0x064B, 0x064B, 0x064B}, /* U+064B */ - {0x064C, 0x064C, 0x064C, 0x064C}, /* U+064C */ - {0x064D, 0x064D, 0x064D, 0x064D}, /* U+064D */ - {0x064E, 0x064E, 0x064E, 0x064E}, /* U+064E */ - {0x064F, 0x064F, 0x064F, 0x064F}, /* U+064F */ - {0x0650, 0x0650, 0x0650, 0x0650}, /* U+0650 */ - {0x0651, 0x0651, 0x0651, 0x0651}, /* U+0651 */ - {0x0652, 0x0652, 0x0652, 0x0652}, /* U+0652 */ - {0x0653, 0x0653, 0x0653, 0x0653}, /* U+0653 */ - {0x0654, 0x0654, 0x0654, 0x0654}, /* U+0654 */ - {0x0655, 0x0655, 0x0655, 0x0655}, /* U+0655 */ - {0x0656, 0x0656, 0x0656, 0x0656}, /* U+0656 */ - {0x0657, 0x0657, 0x0657, 0x0657}, /* U+0657 */ - {0x0658, 0x0658, 0x0658, 0x0658}, /* U+0658 */ - {0x0659, 0x0659, 0x0659, 0x0659}, /* U+0659 */ - {0x065A, 0x065A, 0x065A, 0x065A}, /* U+065A */ - {0x065B, 0x065B, 0x065B, 0x065B}, /* U+065B */ - {0x065C, 0x065C, 0x065C, 0x065C}, /* U+065C */ - {0x065D, 0x065D, 0x065D, 0x065D}, /* U+065D */ - {0x065E, 0x065E, 0x065E, 0x065E}, /* U+065E */ - {0x065F, 0x065F, 0x065F, 0x065F}, /* U+065F */ - {0x0660, 0x0660, 0x0660, 0x0660}, /* U+0660 */ - {0x0661, 0x0661, 0x0661, 0x0661}, /* U+0661 */ - {0x0662, 0x0662, 0x0662, 0x0662}, /* U+0662 */ - {0x0663, 0x0663, 0x0663, 0x0663}, /* U+0663 */ - {0x0664, 0x0664, 0x0664, 0x0664}, /* U+0664 */ - {0x0665, 0x0665, 0x0665, 0x0665}, /* U+0665 */ - {0x0666, 0x0666, 0x0666, 0x0666}, /* U+0666 */ - {0x0667, 0x0667, 0x0667, 0x0667}, /* U+0667 */ - {0x0668, 0x0668, 0x0668, 0x0668}, /* U+0668 */ - {0x0669, 0x0669, 0x0669, 0x0669}, /* U+0669 */ - {0x066A, 0x066A, 0x066A, 0x066A}, /* U+066A */ - {0x066B, 0x066B, 0x066B, 0x066B}, /* U+066B */ - {0x066C, 0x066C, 0x066C, 0x066C}, /* U+066C */ - {0x066D, 0x066D, 0x066D, 0x066D}, /* U+066D */ - {0x066E, 0x066E, 0x066E, 0x066E}, /* U+066E */ - {0x066F, 0x066F, 0x066F, 0x066F}, /* U+066F */ - {0x0670, 0x0670, 0x0670, 0x0670}, /* U+0670 */ - {0x0671, 0x0671, 0xFB51, 0xFB50}, /* U+0671 ARABIC LETTER ALEF WASLA */ - {0x0672, 0x0672, 0x0672, 0x0672}, /* U+0672 */ - {0x0673, 0x0673, 0x0673, 0x0673}, /* U+0673 */ - {0x0674, 0x0674, 0x0674, 0x0674}, /* U+0674 */ - {0x0675, 0x0675, 0x0675, 0x0675}, /* U+0675 */ - {0x0676, 0x0676, 0x0676, 0x0676}, /* U+0676 */ - {0x0677, 0x0677, 0x0677, 0xFBDD}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */ - {0x0678, 0x0678, 0x0678, 0x0678}, /* U+0678 */ - {0xFB68, 0xFB69, 0xFB67, 0xFB66}, /* U+0679 ARABIC LETTER TTEH */ - {0xFB60, 0xFB61, 0xFB5F, 0xFB5E}, /* U+067A ARABIC LETTER TTEHEH */ - {0xFB54, 0xFB55, 0xFB53, 0xFB52}, /* U+067B ARABIC LETTER BEEH */ - {0x067C, 0x067C, 0x067C, 0x067C}, /* U+067C */ - {0x067D, 0x067D, 0x067D, 0x067D}, /* U+067D */ - {0xFB58, 0xFB59, 0xFB57, 0xFB56}, /* U+067E ARABIC LETTER PEH */ - {0xFB64, 0xFB65, 0xFB63, 0xFB62}, /* U+067F ARABIC LETTER TEHEH */ - {0xFB5C, 0xFB5D, 0xFB5B, 0xFB5A}, /* U+0680 ARABIC LETTER BEHEH */ - {0x0681, 0x0681, 0x0681, 0x0681}, /* U+0681 */ - {0x0682, 0x0682, 0x0682, 0x0682}, /* U+0682 */ - {0xFB78, 0xFB79, 0xFB77, 0xFB76}, /* U+0683 ARABIC LETTER NYEH */ - {0xFB74, 0xFB75, 0xFB73, 0xFB72}, /* U+0684 ARABIC LETTER DYEH */ - {0x0685, 0x0685, 0x0685, 0x0685}, /* U+0685 */ - {0xFB7C, 0xFB7D, 0xFB7B, 0xFB7A}, /* U+0686 ARABIC LETTER TCHEH */ - {0xFB80, 0xFB81, 0xFB7F, 0xFB7E}, /* U+0687 ARABIC LETTER TCHEHEH */ - {0x0688, 0x0688, 0xFB89, 0xFB88}, /* U+0688 ARABIC LETTER DDAL */ - {0x0689, 0x0689, 0x0689, 0x0689}, /* U+0689 */ - {0x068A, 0x068A, 0x068A, 0x068A}, /* U+068A */ - {0x068B, 0x068B, 0x068B, 0x068B}, /* U+068B */ - {0x068C, 0x068C, 0xFB85, 0xFB84}, /* U+068C ARABIC LETTER DAHAL */ - {0x068D, 0x068D, 0xFB83, 0xFB82}, /* U+068D ARABIC LETTER DDAHAL */ - {0x068E, 0x068E, 0xFB87, 0xFB86}, /* U+068E ARABIC LETTER DUL */ - {0x068F, 0x068F, 0x068F, 0x068F}, /* U+068F */ - {0x0690, 0x0690, 0x0690, 0x0690}, /* U+0690 */ - {0x0691, 0x0691, 0xFB8D, 0xFB8C}, /* U+0691 ARABIC LETTER RREH */ - {0x0692, 0x0692, 0x0692, 0x0692}, /* U+0692 */ - {0x0693, 0x0693, 0x0693, 0x0693}, /* U+0693 */ - {0x0694, 0x0694, 0x0694, 0x0694}, /* U+0694 */ - {0x0695, 0x0695, 0x0695, 0x0695}, /* U+0695 */ - {0x0696, 0x0696, 0x0696, 0x0696}, /* U+0696 */ - {0x0697, 0x0697, 0x0697, 0x0697}, /* U+0697 */ - {0x0698, 0x0698, 0xFB8B, 0xFB8A}, /* U+0698 ARABIC LETTER JEH */ - {0x0699, 0x0699, 0x0699, 0x0699}, /* U+0699 */ - {0x069A, 0x069A, 0x069A, 0x069A}, /* U+069A */ - {0x069B, 0x069B, 0x069B, 0x069B}, /* U+069B */ - {0x069C, 0x069C, 0x069C, 0x069C}, /* U+069C */ - {0x069D, 0x069D, 0x069D, 0x069D}, /* U+069D */ - {0x069E, 0x069E, 0x069E, 0x069E}, /* U+069E */ - {0x069F, 0x069F, 0x069F, 0x069F}, /* U+069F */ - {0x06A0, 0x06A0, 0x06A0, 0x06A0}, /* U+06A0 */ - {0x06A1, 0x06A1, 0x06A1, 0x06A1}, /* U+06A1 */ - {0x06A2, 0x06A2, 0x06A2, 0x06A2}, /* U+06A2 */ - {0x06A3, 0x06A3, 0x06A3, 0x06A3}, /* U+06A3 */ - {0xFB6C, 0xFB6D, 0xFB6B, 0xFB6A}, /* U+06A4 ARABIC LETTER VEH */ - {0x06A5, 0x06A5, 0x06A5, 0x06A5}, /* U+06A5 */ - {0xFB70, 0xFB71, 0xFB6F, 0xFB6E}, /* U+06A6 ARABIC LETTER PEHEH */ - {0x06A7, 0x06A7, 0x06A7, 0x06A7}, /* U+06A7 */ - {0x06A8, 0x06A8, 0x06A8, 0x06A8}, /* U+06A8 */ - {0xFB90, 0xFB91, 0xFB8F, 0xFB8E}, /* U+06A9 ARABIC LETTER KEHEH */ - {0x06AA, 0x06AA, 0x06AA, 0x06AA}, /* U+06AA */ - {0x06AB, 0x06AB, 0x06AB, 0x06AB}, /* U+06AB */ - {0x06AC, 0x06AC, 0x06AC, 0x06AC}, /* U+06AC */ - {0xFBD5, 0xFBD6, 0xFBD4, 0xFBD3}, /* U+06AD ARABIC LETTER NG */ - {0x06AE, 0x06AE, 0x06AE, 0x06AE}, /* U+06AE */ - {0xFB94, 0xFB95, 0xFB93, 0xFB92}, /* U+06AF ARABIC LETTER GAF */ - {0x06B0, 0x06B0, 0x06B0, 0x06B0}, /* U+06B0 */ - {0xFB9C, 0xFB9D, 0xFB9B, 0xFB9A}, /* U+06B1 ARABIC LETTER NGOEH */ - {0x06B2, 0x06B2, 0x06B2, 0x06B2}, /* U+06B2 */ - {0xFB98, 0xFB99, 0xFB97, 0xFB96}, /* U+06B3 ARABIC LETTER GUEH */ - {0x06B4, 0x06B4, 0x06B4, 0x06B4}, /* U+06B4 */ - {0x06B5, 0x06B5, 0x06B5, 0x06B5}, /* U+06B5 */ - {0x06B6, 0x06B6, 0x06B6, 0x06B6}, /* U+06B6 */ - {0x06B7, 0x06B7, 0x06B7, 0x06B7}, /* U+06B7 */ - {0x06B8, 0x06B8, 0x06B8, 0x06B8}, /* U+06B8 */ - {0x06B9, 0x06B9, 0x06B9, 0x06B9}, /* U+06B9 */ - {0x06BA, 0x06BA, 0xFB9F, 0xFB9E}, /* U+06BA ARABIC LETTER NOON GHUNNA */ - {0xFBA2, 0xFBA3, 0xFBA1, 0xFBA0}, /* U+06BB ARABIC LETTER RNOON */ - {0x06BC, 0x06BC, 0x06BC, 0x06BC}, /* U+06BC */ - {0x06BD, 0x06BD, 0x06BD, 0x06BD}, /* U+06BD */ - {0xFBAC, 0xFBAD, 0xFBAB, 0xFBAA}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ - {0x06BF, 0x06BF, 0x06BF, 0x06BF}, /* U+06BF */ - {0x06C0, 0x06C0, 0xFBA5, 0xFBA4}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */ - {0xFBA8, 0xFBA9, 0xFBA7, 0xFBA6}, /* U+06C1 ARABIC LETTER HEH GOAL */ - {0x06C2, 0x06C2, 0x06C2, 0x06C2}, /* U+06C2 */ - {0x06C3, 0x06C3, 0x06C3, 0x06C3}, /* U+06C3 */ - {0x06C4, 0x06C4, 0x06C4, 0x06C4}, /* U+06C4 */ - {0x06C5, 0x06C5, 0xFBE1, 0xFBE0}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */ - {0x06C6, 0x06C6, 0xFBDA, 0xFBD9}, /* U+06C6 ARABIC LETTER OE */ - {0x06C7, 0x06C7, 0xFBD8, 0xFBD7}, /* U+06C7 ARABIC LETTER U */ - {0x06C8, 0x06C8, 0xFBDC, 0xFBDB}, /* U+06C8 ARABIC LETTER YU */ - {0x06C9, 0x06C9, 0xFBE3, 0xFBE2}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */ - {0x06CA, 0x06CA, 0x06CA, 0x06CA}, /* U+06CA */ - {0x06CB, 0x06CB, 0xFBDF, 0xFBDE}, /* U+06CB ARABIC LETTER VE */ - {0xFBFE, 0xFBFF, 0xFBFD, 0xFBFC}, /* U+06CC ARABIC LETTER FARSI YEH */ - {0x06CD, 0x06CD, 0x06CD, 0x06CD}, /* U+06CD */ - {0x06CE, 0x06CE, 0x06CE, 0x06CE}, /* U+06CE */ - {0x06CF, 0x06CF, 0x06CF, 0x06CF}, /* U+06CF */ - {0xFBE6, 0xFBE7, 0xFBE5, 0xFBE4}, /* U+06D0 ARABIC LETTER E */ - {0x06D1, 0x06D1, 0x06D1, 0x06D1}, /* U+06D1 */ - {0x06D2, 0x06D2, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */ - {0x06D3, 0x06D3, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */ + {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 0x0621 -#define SHAPING_TABLE_LAST 0x06D3 +#define SHAPING_TABLE_FIRST 0x0621u +#define SHAPING_TABLE_LAST 0x06D3u -static const struct { +static const struct ligature_set_t { uint16_t first; - struct { + struct ligature_pairs_t { uint16_t second; uint16_t ligature; } ligatures[4]; } ligature_table[] = { - { 0xFEDF, { - { 0xFE88, 0xFEF9 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ - { 0xFE82, 0xFEF5 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ - { 0xFE8E, 0xFEFB }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ - { 0xFE84, 0xFEF7 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + { 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 */ }}, - { 0xFEE0, { - { 0xFE88, 0xFEFA }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ - { 0xFE82, 0xFEF6 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ - { 0xFE8E, 0xFEFC }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ - { 0xFE84, 0xFEF8 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + { 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 */ }}, }; diff --git a/src/hb-ot-shape-complex-arabic-win1256.hh b/src/hb-ot-shape-complex-arabic-win1256.hh new file mode 100644 index 0000000..8edd3ba --- /dev/null +++ b/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 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 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/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index 54460f0..ae90864 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2010 Google, Inc. + * Copyright © 2010,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -28,9 +28,8 @@ #include "hb-ot-shape-private.hh" - /* buffer var allocations */ -#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */ +#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ /* @@ -38,17 +37,16 @@ */ enum { JOINING_TYPE_U = 0, - JOINING_TYPE_R = 1, - JOINING_TYPE_D = 2, + JOINING_TYPE_L = 1, + JOINING_TYPE_R = 2, + JOINING_TYPE_D = 3, JOINING_TYPE_C = JOINING_TYPE_D, - JOINING_GROUP_ALAPH = 3, - JOINING_GROUP_DALATH_RISH = 4, - NUM_STATE_MACHINE_COLS = 5, - - /* We deliberately don't have a JOINING_TYPE_L since that's unused in Unicode. */ + JOINING_GROUP_ALAPH = 4, + JOINING_GROUP_DALATH_RISH = 5, + NUM_STATE_MACHINE_COLS = 6, - JOINING_TYPE_T = 6, - JOINING_TYPE_X = 7 /* means: use general-category to choose between U or T. */ + JOINING_TYPE_T = 7, + JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */ }; /* @@ -59,77 +57,45 @@ enum { static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) { - if (likely (hb_in_range<hb_codepoint_t> (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) { - unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST]; - if (likely (j_type != JOINING_TYPE_X)) - return j_type; - } - - /* Mongolian joining data is not in ArabicJoining.txt yet */ - if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1800, 0x18AF))) - { - /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */ - if (gen_cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A) - return JOINING_TYPE_D; - } - - if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) { - return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C; - } - - return (FLAG(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; -} - -static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape) -{ - if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4) - return shaping_table[u - SHAPING_TABLE_FIRST][shape]; - return u; + unsigned int j_type = joining_type(u); + if (likely (j_type != JOINING_TYPE_X)) + return j_type; + + return (FLAG(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; } -static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second) -{ - if (unlikely (!second)) return 0; - for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++) - if (ligature_table[i].first == first) - for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++) - if (ligature_table[i].ligatures[j].second == second) - return ligature_table[i].ligatures[j].ligature; - return 0; -} +#define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3') -static const hb_tag_t arabic_syriac_features[] = +static const hb_tag_t arabic_features[] = { - HB_TAG('i','n','i','t'), - HB_TAG('m','e','d','i'), - HB_TAG('f','i','n','a'), HB_TAG('i','s','o','l'), - /* Syriac */ - HB_TAG('m','e','d','2'), + HB_TAG('f','i','n','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 { - INIT, - MEDI, - FINA, ISOL, - - /* Syriac */ - MED2, + FINA, FIN2, FIN3, + MEDI, + MED2, + INIT, NONE, - COMMON_NUM_FEATURES = 4, - SYRIAC_NUM_FEATURES = 7, - TOTAL_NUM_FEATURES = NONE + ARABIC_NUM_FEATURES = NONE }; static const struct arabic_state_table_entry { @@ -138,162 +104,279 @@ static const struct arabic_state_table_entry { uint16_t next_state; } arabic_state_table[][NUM_STATE_MACHINE_COLS] = { - /* jt_U, jt_R, jt_D, jg_ALAPH, jg_DALATH_RISH */ + /* 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,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, }, + { {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,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, }, + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, }, - /* State 2: prev was D/ISOL, willing to join. */ - { {NONE,NONE,0}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, }, + /* State 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/FINA, willing to join. */ - { {NONE,NONE,0}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,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}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, }, + { {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}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, }, + { {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,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, } + { {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); -void -_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, - const hb_segment_properties_t *props) +static void +arabic_fallback_shape (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) { - /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together, - * then rlig and calt each in their own stage. This makes IranNastaliq's ALLAH - * ligature work correctly. It's unfortunate though... + hb_ot_map_builder_t *map = &plan->map; + + /* We apply features according to the Arabic spec, with pauses + * in between most. * - * This also makes Arial Bold in Windows7 work. See: + * The pause between init/medi/... and rlig is required. See eg: * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 * - * TODO: Add test cases for these two. + * 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_bool_feature (HB_TAG('c','c','m','p')); - map->add_bool_feature (HB_TAG('l','o','c','l')); - - map->add_gsub_pause (NULL, NULL); - - unsigned int num_features = props->script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES; - for (unsigned int i = 0; i < num_features; i++) - map->add_bool_feature (arabic_syriac_features[i], false); + map->add_gsub_pause (nuke_joiners); - map->add_gsub_pause (NULL, NULL); + map->add_global_bool_feature (HB_TAG('c','c','m','p')); + map->add_global_bool_feature (HB_TAG('l','o','c','l')); - map->add_bool_feature (HB_TAG('r','l','i','g')); - map->add_gsub_pause (NULL, NULL); + map->add_gsub_pause (NULL); - map->add_bool_feature (HB_TAG('c','a','l','t')); - map->add_gsub_pause (NULL, 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); + } - /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */ - map->add_bool_feature (HB_TAG('c','s','w','h')); + 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')); + map->add_gsub_pause (NULL); + + /* 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_global_bool_feature (HB_TAG('c','s','w','h')); + map->add_global_bool_feature (HB_TAG('m','s','e','t')); } -hb_ot_shape_normalization_mode_t -_hb_ot_shape_complex_normalization_preference_arabic (void) +#include "hb-ot-shape-complex-arabic-fallback.hh" + +struct arabic_shape_plan_t { - return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; -} + 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]; -static void -arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer) + bool do_fallback; + arabic_fallback_plan_t *fallback_plan; +}; + +static void * +data_create_arabic (const hb_ot_shape_plan_t *plan) { - unsigned int count = buffer->len; - hb_codepoint_t glyph; - - /* Shape to presentation forms */ - for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t u = buffer->info[i].codepoint; - hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action()); - if (shaped != u && hb_font_get_glyph (font, shaped, 0, &glyph)) - buffer->info[i].codepoint = shaped; + 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; + 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])); } - /* Mandatory ligatures */ - buffer->clear_output (); - for (buffer->idx = 0; buffer->idx + 1 < count;) { - hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint, - buffer->cur(+1).codepoint); - if (likely (!ligature) || !(hb_font_get_glyph (font, ligature, 0, &glyph))) { - buffer->next_glyph (); - continue; - } + return arabic_plan; +} + +static void +data_destroy_arabic (void *data) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data; - buffer->replace_glyphs (2, 1, &ligature); + arabic_fallback_plan_destroy (arabic_plan->fallback_plan); - /* Technically speaking we can skip marks and stuff, like the GSUB path does. - * But who cares, we're in fallback! */ - } - for (; buffer->idx < count;) - buffer->next_glyph (); - buffer->swap_buffers (); + free (data); } -void -_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, - hb_buffer_t *buffer, - hb_font_t *font) +static void +arabic_joining (hb_buffer_t *buffer) { unsigned int count = buffer->len; - unsigned int prev = 0, state = 0; + hb_glyph_info_t *info = buffer->info; + unsigned int prev = (unsigned int) -1, state = 0; - HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); + /* 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 (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[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)) { - buffer->info[i].arabic_shaping_action() = NONE; + info[i].arabic_shaping_action() = NONE; continue; } const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; - if (entry->prev_action != NONE) - buffer->info[prev].arabic_shaping_action() = entry->prev_action; + if (entry->prev_action != NONE && prev != (unsigned int) -1) + info[prev].arabic_shaping_action() = entry->prev_action; - buffer->info[i].arabic_shaping_action() = entry->curr_action; + info[i].arabic_shaping_action() = entry->curr_action; prev = i; state = entry->next_state; } - hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0}; - hb_mask_t total_masks = 0; - unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES; - for (unsigned int i = 0; i < num_masks; i++) { - mask_array[i] = map->get_1_mask (arabic_syriac_features[i]); - total_masks |= mask_array[i]; - } + 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; - if (total_masks) { - /* Has OpenType tables */ - for (unsigned int i = 0; i < count; i++) - buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()]; - } else if (buffer->props.script == HB_SCRIPT_ARABIC) { - /* Fallback Arabic shaping to Presentation Forms */ - /* Pitfalls: - * - This path fires if user force-set init/medi/fina/isol off, - * - If font does not declare script 'arab', well, what to do? - * Most probably it's safe to assume that init/medi/fina/isol - * still mean Arabic shaping, although they do not have to. - */ - arabic_fallback_shape (font, buffer); + 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(); +} + +static void +setup_masks_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); + + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + + arabic_joining (buffer); + if (plan->props.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()]; HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } +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); +} + + +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_arabic */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_arabic, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/src/hb-graphite2-private.hh b/src/hb-ot-shape-complex-default.cc index 644ea75..f7f097e 100644 --- a/src/hb-graphite2-private.hh +++ b/src/hb-ot-shape-complex-default.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Google, Inc. + * Copyright © 2010,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,19 +24,21 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_GRAPHITE2_PRIVATE_HH -#define HB_GRAPHITE2_PRIVATE_HH +#include "hb-ot-shape-complex-private.hh" -#include "hb-private.hh" -#include "hb-graphite2.h" - - -HB_INTERNAL hb_bool_t -_hb_graphite2_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); - - -#endif /* HB_GRAPHITE2_PRIVATE_HH */ +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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT, + true, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc new file mode 100644 index 0000000..6ac18b0 --- /dev/null +++ b/src/hb-ot-shape-complex-hangul.cc @@ -0,0 +1,426 @@ +/* + * 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;) + { + 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)) + { + 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; + } + /* Merge clusters across the (possibly reordered) syllable+tone. + * We want to merge even in the zero-width tone mark case here, + * so that clustering behavior isn't dependent on how the tone mark + * is handled by the font. + */ + buffer->merge_out_clusters (start, end + 1); + } + 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; + 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; + 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_create */ + data_destroy_hangul, /* data_destroy */ + preprocess_text_hangul, + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_hangul, /* setup_masks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-hebrew.cc b/src/hb-ot-shape-complex-hebrew.cc new file mode 100644 index 0000000..c7b7a5e --- /dev/null +++ b/src/hb-ot-shape-complex-hebrew.cc @@ -0,0 +1,172 @@ +/* + * 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 = 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; +} + + +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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + compose_hebrew, + NULL, /* setup_masks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh deleted file mode 100644 index db1396b..0000000 --- a/src/hb-ot-shape-complex-indic-machine.hh +++ /dev/null @@ -1,293 +0,0 @@ - -#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" -/* - * Copyright © 2011,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH -#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH - -#include "hb-private.hh" - -HB_BEGIN_DECLS - - -#line 38 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" -static const unsigned char _indic_syllable_machine_trans_keys[] = { - 5u, 5u, 1u, 2u, 1u, 2u, 5u, 5u, 1u, 5u, 1u, 2u, 5u, 5u, 1u, 13u, - 4u, 11u, 4u, 11u, 5u, 11u, 1u, 10u, 1u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, - 5u, 10u, 8u, 10u, 5u, 10u, 6u, 10u, 9u, 10u, 4u, 11u, 1u, 13u, 4u, 10u, - 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, - 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 0 -}; - -static const char _indic_syllable_machine_key_spans[] = { - 1, 2, 2, 1, 5, 2, 1, 13, - 8, 8, 7, 10, 10, 1, 1, 7, - 6, 3, 6, 5, 2, 8, 13, 7, - 7, 6, 7, 6, 3, 1, 1, 7, - 7, 6, 7, 6, 3, 1, 1 -}; - -static const unsigned char _indic_syllable_machine_index_offsets[] = { - 0, 2, 5, 8, 10, 16, 19, 21, - 35, 44, 53, 61, 72, 83, 85, 87, - 95, 102, 106, 113, 119, 122, 131, 145, - 153, 161, 168, 176, 183, 187, 189, 191, - 199, 207, 214, 222, 229, 233, 235 -}; - -static const char _indic_syllable_machine_indicies[] = { - 1, 0, 2, 2, 0, 4, 4, 3, - 5, 3, 4, 4, 3, 3, 5, 3, - 7, 7, 6, 8, 6, 2, 10, 11, - 9, 9, 9, 9, 9, 9, 9, 9, - 12, 12, 9, 14, 15, 16, 16, 17, - 18, 19, 20, 13, 21, 15, 16, 16, - 17, 18, 19, 20, 13, 15, 16, 16, - 17, 18, 19, 20, 13, 2, 2, 13, - 13, 13, 22, 22, 13, 18, 19, 13, - 2, 2, 13, 13, 13, 13, 13, 13, - 18, 19, 13, 19, 13, 23, 13, 24, - 25, 13, 13, 17, 18, 19, 13, 25, - 13, 13, 17, 18, 19, 13, 17, 18, - 19, 13, 26, 13, 13, 17, 18, 19, - 13, 27, 27, 13, 18, 19, 13, 18, - 19, 13, 14, 28, 16, 16, 17, 18, - 19, 20, 13, 2, 2, 11, 13, 13, - 22, 22, 13, 18, 19, 13, 12, 12, - 13, 30, 5, 31, 32, 33, 34, 35, - 29, 4, 5, 31, 32, 33, 34, 35, - 29, 5, 31, 32, 33, 34, 35, 29, - 36, 37, 29, 29, 33, 34, 35, 29, - 37, 29, 29, 33, 34, 35, 29, 33, - 34, 35, 29, 35, 29, 38, 29, 40, - 8, 41, 41, 42, 43, 44, 39, 7, - 8, 41, 41, 42, 43, 44, 39, 8, - 41, 41, 42, 43, 44, 39, 45, 46, - 39, 39, 42, 43, 44, 39, 46, 39, - 39, 42, 43, 44, 39, 42, 43, 44, - 39, 44, 39, 47, 39, 0 -}; - -static const char _indic_syllable_machine_trans_targs[] = { - 7, 1, 8, 7, 25, 2, 7, 33, - 5, 7, 21, 23, 31, 7, 9, 11, - 0, 15, 13, 14, 18, 10, 12, 7, - 16, 17, 19, 20, 22, 7, 24, 3, - 4, 26, 29, 30, 27, 28, 7, 7, - 32, 6, 34, 37, 38, 35, 36, 7 -}; - -static const char _indic_syllable_machine_trans_actions[] = { - 1, 0, 2, 3, 2, 0, 4, 2, - 0, 7, 2, 2, 2, 8, 2, 0, - 0, 0, 0, 0, 0, 2, 0, 9, - 0, 0, 0, 0, 0, 10, 2, 0, - 0, 0, 0, 0, 0, 0, 11, 12, - 2, 0, 0, 0, 0, 0, 0, 13 -}; - -static const char _indic_syllable_machine_to_state_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 5, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; - -static const char _indic_syllable_machine_from_state_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 6, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; - -static const unsigned char _indic_syllable_machine_eof_trans[] = { - 1, 1, 4, 4, 4, 7, 7, 0, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 30, - 30, 30, 30, 30, 30, 30, 30, 40, - 40, 40, 40, 40, 40, 40, 40 -}; - -static const int indic_syllable_machine_start = 7; -static const int indic_syllable_machine_first_final = 7; -static const int indic_syllable_machine_error = -1; - -static const int indic_syllable_machine_en_main = 7; - - -#line 38 "../../src/hb-ot-shape-complex-indic-machine.rl" - - - -#line 79 "../../src/hb-ot-shape-complex-indic-machine.rl" - - -#define process_syllable(func) \ - HB_STMT_START { \ - /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \ - for (unsigned int i = last; i < p+1; i++) \ - info[i].syllable() = syllable_serial; \ - PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \ - last = p+1; \ - syllable_serial++; \ - if (unlikely (!syllable_serial)) syllable_serial++; \ - } HB_STMT_END - -static void -find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array) -{ - unsigned int p, pe, eof, ts, te, act; - int cs; - hb_glyph_info_t *info = buffer->info; - -#line 170 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" - { - cs = indic_syllable_machine_start; - ts = 0; - te = 0; - act = 0; - } - -#line 101 "../../src/hb-ot-shape-complex-indic-machine.rl" - - - p = 0; - pe = eof = buffer->len; - - unsigned int last = 0; - uint8_t syllable_serial = 1; - -#line 187 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" - { - int _slen; - int _trans; - const unsigned char *_keys; - const char *_inds; - if ( p == pe ) - goto _test_eof; -_resume: - switch ( _indic_syllable_machine_from_state_actions[cs] ) { - case 6: -#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" - {ts = p;} - break; -#line 201 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" - } - - _keys = _indic_syllable_machine_trans_keys + (cs<<1); - _inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs]; - - _slen = _indic_syllable_machine_key_spans[cs]; - _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) && - ( info[p].indic_category()) <= _keys[1] ? - ( info[p].indic_category()) - _keys[0] : _slen ]; - -_eof_trans: - cs = _indic_syllable_machine_trans_targs[_trans]; - - if ( _indic_syllable_machine_trans_actions[_trans] == 0 ) - goto _again; - - switch ( _indic_syllable_machine_trans_actions[_trans] ) { - case 2: -#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p+1;} - break; - case 9: -#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ process_syllable (consonant_syllable); }} - break; - case 11: -#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ process_syllable (vowel_syllable); }} - break; - case 13: -#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ process_syllable (standalone_cluster); }} - break; - case 7: -#line 75 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ process_syllable (non_indic); }} - break; - case 8: -#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p;p--;{ process_syllable (consonant_syllable); }} - break; - case 10: -#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p;p--;{ process_syllable (vowel_syllable); }} - break; - case 12: -#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" - {te = p;p--;{ process_syllable (standalone_cluster); }} - break; - case 1: -#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" - {{p = ((te))-1;}{ process_syllable (consonant_syllable); }} - break; - case 3: -#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" - {{p = ((te))-1;}{ process_syllable (vowel_syllable); }} - break; - case 4: -#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" - {{p = ((te))-1;}{ process_syllable (standalone_cluster); }} - break; -#line 263 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" - } - -_again: - switch ( _indic_syllable_machine_to_state_actions[cs] ) { - case 5: -#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" - {ts = 0;} - break; -#line 272 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" - } - - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - if ( _indic_syllable_machine_eof_trans[cs] > 0 ) { - _trans = _indic_syllable_machine_eof_trans[cs] - 1; - goto _eof_trans; - } - } - - } - -#line 110 "../../src/hb-ot-shape-complex-indic-machine.rl" - -} - -HB_END_DECLS - -#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl index 93ca29a..694b235 100644 --- a/src/hb-ot-shape-complex-indic-machine.rl +++ b/src/hb-ot-shape-complex-indic-machine.rl @@ -29,8 +29,6 @@ #include "hb-private.hh" -HB_BEGIN_DECLS - %%{ machine indic_syllable_machine; alphtype unsigned char; @@ -42,57 +40,76 @@ HB_BEGIN_DECLS # Same order as enum indic_category_t. Not sure how to avoid duplication. X = 0; C = 1; -Ra = 2; -V = 3; -N = 4; -H = 5; -ZWNJ = 6; -ZWJ = 7; -M = 8; -SM = 9; -VD = 10; -A = 11; -NBSP = 12; -DOTTEDCIRCLE = 13; - -c = C | Ra; -n = N N?; -z = ZWJ|ZWNJ; -matra_group = M N? H?; -syllable_tail = SM? (VD VD?)?; -place_holder = NBSP | DOTTEDCIRCLE; - - -consonant_syllable = (c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail; -vowel_syllable = (Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail; -standalone_cluster = (Ra H)? place_holder n? (z? H c)* matra_group* syllable_tail; +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; +CM2 = 31; + +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?.CM2?; +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 => { process_syllable (consonant_syllable); }; - vowel_syllable => { process_syllable (vowel_syllable); }; - standalone_cluster => { process_syllable (standalone_cluster); }; - other => { process_syllable (non_indic); }; + 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 process_syllable(func) \ +#define found_syllable(syllable_type) \ HB_STMT_START { \ - /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \ + 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; \ - PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ last = p+1; \ syllable_serial++; \ - if (unlikely (!syllable_serial)) syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array) +find_syllables (hb_buffer_t *buffer) { - unsigned int p, pe, eof, ts, te, act; + unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED; int cs; hb_glyph_info_t *info = buffer->info; %%{ @@ -104,12 +121,10 @@ find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_arr pe = eof = buffer->len; unsigned int last = 0; - uint8_t syllable_serial = 1; + unsigned int syllable_serial = 1; %%{ write exec; }%% } -HB_END_DECLS - #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh index 64af0da..d8dfc65 100644 --- a/src/hb-ot-shape-complex-indic-private.hh +++ b/src/hb-ot-shape-complex-indic-private.hh @@ -31,13 +31,10 @@ #include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-private.hh" /* XXX Remove */ -/* buffer var allocations */ -#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */ -#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */ - -#define INDIC_TABLE_ELEMENT_TYPE uint8_t +#define INDIC_TABLE_ELEMENT_TYPE uint16_t /* Cateories used in the OpenType spec: * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx @@ -46,225 +43,140 @@ * Not sure how to avoid duplication. */ enum indic_category_t { OT_X = 0, - OT_C, - OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */ - OT_V, - OT_N, - OT_H, - OT_ZWNJ, - OT_ZWJ, - OT_M, - OT_SM, - OT_VD, - OT_A, - OT_NBSP, - OT_DOTTEDCIRCLE /* Not in the spec, but special in Uniscribe. /Very very/ special! */ + 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). */ + OT_CM2 = 31 /* Consonant-Medial, second slot. */ }; +#define MEDIAL_FLAGS (FLAG (OT_CM) | FLAG (OT_CM2)) + +/* 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_ABOVE_M, - POS_BELOW_M, + POS_AFTER_SUB, + + POS_BEFORE_POST, POS_POST_C, - POS_POST_M, - POS_SMVD + 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_X, - INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM, - INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_NBSP, - INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA = OT_C, - INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X, - INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N, - INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_X, - INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X, - INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_X, - INDIC_SYLLABIC_CATEGORY_VIRAMA = OT_H, - INDIC_SYLLABIC_CATEGORY_VISARGA = OT_SM, - INDIC_SYLLABIC_CATEGORY_VOWEL = OT_V, - INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT = OT_M, - INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT = OT_V + 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, /* TODO */ + 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_MEDIAL = OT_CM, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_PLACEHOLDER, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA = OT_Repha, + INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_CM, + INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA = OT_N, + INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK = OT_SM, + INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER = OT_H, /* TODO */ + 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, /* TODO */ + INDIC_SYLLABIC_CATEGORY_PURE_KILLER = OT_H, /* TODO */ + INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_RS, + 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_BASE_C, - - INDIC_MATRA_CATEGORY_LEFT = POS_PRE_M, - INDIC_MATRA_CATEGORY_TOP = POS_ABOVE_M, - INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW_M, - INDIC_MATRA_CATEGORY_RIGHT = POS_POST_M, - - /* We don't really care much about these since we decompose them - * in the generic pre-shaping layer. They will only be used if - * the font does not cover the decomposition. In which case, we - * define these as aliases to the place we want the split-matra - * glyph to show up. Quite arbitrary. - * - * TODO: There are some split matras without Unicode decompositions. - * We have to figure out what to do with them. - */ - INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = POS_POST_M, - INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = POS_PRE_M, - INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = POS_BELOW_M, - INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = POS_POST_M, - INDIC_MATRA_CATEGORY_TOP_AND_LEFT = POS_PRE_M, - INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = POS_PRE_M, - INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = POS_POST_M, - - INDIC_MATRA_CATEGORY_INVISIBLE = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, - INDIC_MATRA_CATEGORY_OVERSTRUCK = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, - INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = INDIC_MATRA_CATEGORY_NOT_APPLICABLE + 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 }; /* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation * because gcc fails to optimize the latter and fills the table in at runtime. */ #define INDIC_COMBINE_CATEGORIES(S,M) \ - (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \ - ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \ - ((M << 4) | S)) - - -#include "hb-ot-shape-complex-indic-table.hh" - -/* XXX - * This is a hack for now. We should: - * 1. Move this data into the main Indic table, - * and/or - * 2. Probe font lookups to determine consonant positions. - */ -static const struct consonant_position_t { - hb_codepoint_t u; - indic_position_t position; -} consonant_positions[] = { - {0x0930, POS_BELOW_C}, - {0x09AC, POS_BELOW_C}, - {0x09AF, POS_POST_C}, - {0x09B0, POS_BELOW_C}, - {0x09F0, POS_BELOW_C}, - {0x0A2F, POS_POST_C}, - {0x0A30, POS_BELOW_C}, - {0x0A35, POS_BELOW_C}, - {0x0A39, POS_BELOW_C}, - {0x0AB0, POS_BELOW_C}, - {0x0B24, POS_BELOW_C}, - {0x0B28, POS_BELOW_C}, - {0x0B2C, POS_BELOW_C}, - {0x0B2D, POS_BELOW_C}, - {0x0B2E, POS_BELOW_C}, - {0x0B2F, POS_POST_C}, - {0x0B30, POS_BELOW_C}, - {0x0B32, POS_BELOW_C}, - {0x0B33, POS_BELOW_C}, - {0x0B5F, POS_POST_C}, - {0x0B71, POS_BELOW_C}, - {0x0C15, POS_BELOW_C}, - {0x0C16, POS_BELOW_C}, - {0x0C17, POS_BELOW_C}, - {0x0C18, POS_BELOW_C}, - {0x0C19, POS_BELOW_C}, - {0x0C1A, POS_BELOW_C}, - {0x0C1B, POS_BELOW_C}, - {0x0C1C, POS_BELOW_C}, - {0x0C1D, POS_BELOW_C}, - {0x0C1E, POS_BELOW_C}, - {0x0C1F, POS_BELOW_C}, - {0x0C20, POS_BELOW_C}, - {0x0C21, POS_BELOW_C}, - {0x0C22, POS_BELOW_C}, - {0x0C23, POS_BELOW_C}, - {0x0C24, POS_BELOW_C}, - {0x0C25, POS_BELOW_C}, - {0x0C26, POS_BELOW_C}, - {0x0C27, POS_BELOW_C}, - {0x0C28, POS_BELOW_C}, - {0x0C2A, POS_BELOW_C}, - {0x0C2B, POS_BELOW_C}, - {0x0C2C, POS_BELOW_C}, - {0x0C2D, POS_BELOW_C}, - {0x0C2E, POS_BELOW_C}, - {0x0C2F, POS_BELOW_C}, - {0x0C30, POS_BELOW_C}, - {0x0C32, POS_BELOW_C}, - {0x0C33, POS_BELOW_C}, - {0x0C35, POS_BELOW_C}, - {0x0C36, POS_BELOW_C}, - {0x0C37, POS_BELOW_C}, - {0x0C38, POS_BELOW_C}, - {0x0C39, POS_BELOW_C}, - {0x0C95, POS_BELOW_C}, - {0x0C96, POS_BELOW_C}, - {0x0C97, POS_BELOW_C}, - {0x0C98, POS_BELOW_C}, - {0x0C99, POS_BELOW_C}, - {0x0C9A, POS_BELOW_C}, - {0x0C9B, POS_BELOW_C}, - {0x0C9C, POS_BELOW_C}, - {0x0C9D, POS_BELOW_C}, - {0x0C9E, POS_BELOW_C}, - {0x0C9F, POS_BELOW_C}, - {0x0CA0, POS_BELOW_C}, - {0x0CA1, POS_BELOW_C}, - {0x0CA2, POS_BELOW_C}, - {0x0CA3, POS_BELOW_C}, - {0x0CA4, POS_BELOW_C}, - {0x0CA5, POS_BELOW_C}, - {0x0CA6, POS_BELOW_C}, - {0x0CA7, POS_BELOW_C}, - {0x0CA8, POS_BELOW_C}, - {0x0CAA, POS_BELOW_C}, - {0x0CAB, POS_BELOW_C}, - {0x0CAC, POS_BELOW_C}, - {0x0CAD, POS_BELOW_C}, - {0x0CAE, POS_BELOW_C}, - {0x0CAF, POS_BELOW_C}, - {0x0CB0, POS_BELOW_C}, - {0x0CB2, POS_BELOW_C}, - {0x0CB3, POS_BELOW_C}, - {0x0CB5, POS_BELOW_C}, - {0x0CB6, POS_BELOW_C}, - {0x0CB7, POS_BELOW_C}, - {0x0CB8, POS_BELOW_C}, - {0x0CB9, POS_BELOW_C}, - {0x0CDE, POS_BELOW_C}, - {0x0D2F, POS_POST_C}, - {0x0D30, POS_POST_C}, - {0x0D32, POS_BELOW_C}, - {0x0D35, POS_POST_C}, -}; - -/* XXX - * This is a hack for now. We should move this data into the main Indic table. - * Or completely remove it and just check in the tables. - */ -static const hb_codepoint_t ra_chars[] = { - 0x0930, /* Devanagari */ - 0x09B0, /* Bengali */ - 0x09F0, /* Bengali */ - 0x0A30, /* Gurmukhi */ /* No Reph */ - 0x0AB0, /* Gujarati */ - 0x0B30, /* Oriya */ - 0x0BB0, /* Tamil */ /* No Reph */ - 0x0C30, /* Telugu */ /* No Reph */ - 0x0CB0, /* Kannada */ - 0x0D30, /* Malayalam */ /* No Reph */ -}; - + (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || \ + ( \ + 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)) + \ + ASSERT_STATIC_EXPR_ZERO (S < 255 && M < 255) + \ + ((M << 8) | S)) + +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/src/hb-ot-shape-complex-indic-table.hh b/src/hb-ot-shape-complex-indic-table.cc index 5b4b344..2e159a1 100644 --- a/src/hb-ot-shape-complex-indic-table.hh +++ b/src/hb-ot-shape-complex-indic-table.cc @@ -6,55 +6,63 @@ * * on files with these headers: * - * # IndicSyllabicCategory-6.1.0.txt - * # Date: 2011-08-31, 23:54:00 GMT [KW] - * # IndicMatraCategory-6.1.0.txt - * # Date: 2011-08-31, 23:50:00 GMT [KW] - * # Blocks-6.1.0.txt - * # Date: 2011-06-14, 18:26:00 GMT [KW, LI] + * # IndicSyllabicCategory-7.0.0.txt + * # Date: 2014-06-03, 07:00:00 GMT [KW, LI, AG, RP] + * # IndicMatraCategory-7.0.0.txt + * # Date: 2014-06-03, 07:00:00 GMT [KW, LI, AG, RP] + * # Blocks-7.0.0.txt + * # Date: 2014-04-03, 23:23:00 GMT [RP, KW] */ -#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH -#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH - - -#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 11 chars; Avagraha */ -#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 34 chars; Bindu */ -#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 123 chars; Consonant */ -#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 2 chars; Consonant_Dead */ -#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 17 chars; Consonant_Final */ -#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 1 chars; Consonant_Head_Letter */ -#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 12 chars; Consonant_Medial */ -#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 4 chars; Consonant_Placeholder */ -#define ISC_CR INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA /* 5 chars; Consonant_Repha */ -#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 10 chars; Consonant_Subjoined */ +#include "hb-ot-shape-complex-indic-private.hh" + + +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 13 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 59 chars; Bindu */ +#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ +#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 30 chars; Cantillation_Mark */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 1744 chars; Consonant */ +#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 7 chars; Consonant_Dead */ +#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 61 chars; Consonant_Final */ +#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ +#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 19 chars; Consonant_Medial */ +#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 11 chars; Consonant_Placeholder */ +#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 1 chars; Consonant_Preceding_Repha */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 61 chars; Consonant_Subjoined */ +#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ +#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_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 12 chars; Nukta */ +#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ +#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 18 chars; Nukta */ +#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 408 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_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 1 chars; Register_Shifter */ -#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 3 chars; Tone_Letter */ -#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 16 chars; Tone_Mark */ -#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 34 chars; Virama */ -#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 25 chars; Visarga */ -#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 5 chars; Vowel */ -#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 165 chars; Vowel_Dependent */ -#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 59 chars; Vowel_Independent */ - -#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 65 chars; Bottom */ +#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 15 chars; Pure_Killer */ +#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 3 chars; Register_Shifter */ +#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ +#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 62 chars; Tone_Mark */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 22 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 29 chars; Visarga */ +#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 553 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 395 chars; Vowel_Independent */ + +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 142 chars; Bottom */ #define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ -#define IMC_I INDIC_MATRA_CATEGORY_INVISIBLE /* 6 chars; Invisible */ -#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 30 chars; Left */ -#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 8 chars; Left_And_Right */ +#define IMC_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 /* 2 chars; Overstruck */ -#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 75 chars; Right */ -#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 83 chars; Top */ -#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 6 chars; Top_And_Bottom */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 163 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 169 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 /* 4 chars; Top_And_Left */ -#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 2 chars; Top_And_Left_And_Right */ -#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 8 chars; Top_And_Right */ -#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 5 chars; Visual_Order_Left */ +#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 /* 15 chars; Visual_Order_Left */ #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M) @@ -62,10 +70,26 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { -#define indic_offset_0x0900 0 +#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_0x00d0u 24 + + /* Latin-1 Supplement */ - /* Devanagari (0900..097F) */ + /* 00D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(CP,x), + +#define indic_offset_0x0900u 32 + + + /* Devanagari */ /* 0900 */ _(Bi,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -77,14 +101,14 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0938 */ _(C,x), _(C,x), _(M,T), _(M,R), _(N,x), _(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), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,B), _(M,B), + /* 0950 */ _(x,x), _(TM,x), _(TM,x), _(x,x), _(x,x), _(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), _(x,x), _(x,x), - /* 0968 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,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 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 0978 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* Bengali (0980..09FF) */ + /* Bengali */ /* 0980 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -98,12 +122,12 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 09E8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,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..0A7F) */ + /* Gurmukhi */ /* 0A00 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -117,12 +141,12 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 0A68 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0A70 */ _(Bi,x), _(x,x), _(CP,x), _(CP,x), _(x,x), _(CM,x), _(x,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,x), _(GM,T), _(CP,x), _(CP,x), _(x,x), _(CM,x), _(x,x), _(x,x), /* 0A78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Gujarati (0A80..0AFF) */ + /* Gujarati */ /* 0A80 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -136,12 +160,12 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 0AE8 */ _(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), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Oriya (0B00..0B7F) */ + /* Oriya */ /* 0B00 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -155,12 +179,12 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 0B68 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,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..0BFF) */ + /* Tamil */ /* 0B80 */ _(x,x), _(x,x), _(Bi,x), _(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), @@ -174,33 +198,33 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 0BE8 */ _(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..0C7F) */ + /* Telugu */ - /* 0C00 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C00 */ _(Bi,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), _(x,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), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0C60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0C68 */ _(x,x), _(x,x), _(x,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..0CFF) */ + /* Kannada */ - /* 0C80 */ _(x,x), _(x,x), _(Bi,x), _(Vs,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C80 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -212,14 +236,14 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(x,x), _(x,x), - /* 0CE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,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), _(x,x), _(x,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..0D7F) */ + /* Malayalam */ - /* 0D00 */ _(x,x), _(x,x), _(Bi,x), _(Vs,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D00 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(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), @@ -228,15 +252,15 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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), _(CR,x), _(x,x), + /* 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), _(x,x), _(x,x), _(x,x), _(M,R), /* 0D58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0D60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0D68 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,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..0DFF) */ + /* Sinhala */ /* 0D80 */ _(x,x), _(x,x), _(Bi,x), _(Vs,x), _(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), @@ -249,86 +273,15 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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,LR), _(M,LR), _(M,R), - /* 0DE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0DE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(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), - /* 0DF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* Thai (0E00..0E7F) */ - - /* 0E00 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E08 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E10 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E28 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), - /* 0E30 */ _(M,R), _(M,T), _(M,R), _(M,R), _(M,T), _(M,T), _(M,T), _(M,T), - /* 0E38 */ _(M,B), _(M,B), _(V,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E40 */_(M,VOL),_(M,VOL),_(M,VOL),_(M,VOL),_(M,VOL), _(M,R), _(x,x), _(M,T), - /* 0E48 */ _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(x,x), _(Bi,x), _(V,T), _(x,x), - /* 0E50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E68 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E70 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0E78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* Lao (0E80..0EFF) */ - - /* 0E80 */ _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(x,x), _(C,x), - /* 0E88 */ _(C,x), _(x,x), _(C,x), _(x,x), _(x,x), _(C,x), _(x,x), _(x,x), - /* 0E90 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0E98 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0EA0 */ _(x,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(C,x), - /* 0EA8 */ _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), - /* 0EB0 */ _(M,R), _(M,T), _(M,R), _(M,R), _(M,T), _(M,T), _(M,T), _(M,T), - /* 0EB8 */ _(M,B), _(M,B), _(x,x), _(M,T), _(CM,x), _(CM,x), _(x,x), _(x,x), - /* 0EC0 */_(M,VOL),_(M,VOL),_(M,VOL),_(M,VOL),_(M,VOL), _(x,x), _(x,x), _(x,x), - /* 0EC8 */ _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(x,x), _(Bi,x), _(x,x), _(x,x), - /* 0ED0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0ED8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(x,x), - /* 0EE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0EE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0EF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0EF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* Tibetan (0F00..0FFF) */ - - /* 0F00 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F08 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F10 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F18 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F20 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F28 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F30 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F38 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0F40 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0F48 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0F50 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0F58 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0F60 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 0F68 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), - /* 0F70 */ _(x,x), _(M,B), _(M,T), _(M,TB), _(M,B), _(M,B), _(M,TB), _(M,TB), - /* 0F78 */ _(M,TB), _(M,TB), _(M,T), _(M,T), _(M,T), _(M,T), _(Bi,x), _(Vs,x), - /* 0F80 */ _(M,T), _(M,TB), _(Bi,x), _(Bi,x), _(V,B), _(A,x), _(x,x), _(x,x), - /* 0F88 */_(CHL,x),_(CHL,x),_(CHL,x),_(CHL,x),_(CHL,x), _(CS,x), _(CS,x), _(CS,x), - /* 0F90 */ _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), - /* 0F98 */ _(x,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), - /* 0FA0 */ _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), - /* 0FA8 */ _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), - /* 0FB0 */ _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), - /* 0FB8 */ _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(CS,x), _(x,x), _(x,x), _(x,x), - /* 0FC0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FC8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 0FF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* Myanmar (1000..109F) */ + +#define indic_offset_0x1000u 1304 + + + /* 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), @@ -337,9 +290,9 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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,x), _(TM,x), - /* 1038 */ _(Vs,x), _(V,I), _(V,T), _(CM,x), _(CM,x), _(CM,x), _(CM,x), _(C,x), - /* 1040 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1048 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1038 */ _(Vs,x), _(IS,x), _(PK,T), _(CM,x), _(CM,x), _(CM,x), _(CM,x), _(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,x), _(CM,x), /* 1060 */ _(CM,x), _(C,x), _(M,R), _(TM,x), _(TM,x), _(C,x), _(C,x), _(M,R), @@ -348,41 +301,41 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1078 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1080 */ _(C,x), _(C,x), _(CM,x), _(M,R), _(M,L), _(M,T), _(M,T), _(TM,x), /* 1088 */ _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(C,x), _(TM,x), - /* 1090 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1098 */ _(x,x), _(x,x), _(TM,x), _(TM,x), _(M,R), _(M,T), _(x,x), _(x,x), + /* 1090 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1098 */ _(Nd,x), _(Nd,x), _(TM,x), _(TM,x), _(M,R), _(M,T), _(x,x), _(x,x), -#define indic_offset_0x1700 1952 +#define indic_offset_0x1700u 1464 - /* Tagalog (1700..171F) */ + /* Tagalog */ /* 1700 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1708 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), - /* 1710 */ _(C,x), _(C,x), _(M,T), _(M,B), _(V,B), _(x,x), _(x,x), _(x,x), + /* 1710 */ _(C,x), _(C,x), _(M,T), _(M,B), _(PK,B), _(x,x), _(x,x), _(x,x), /* 1718 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Hanunoo (1720..173F) */ + /* Hanunoo */ /* 1720 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1728 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 1730 */ _(C,x), _(C,x), _(M,T), _(M,B), _(V,B), _(x,x), _(x,x), _(x,x), + /* 1730 */ _(C,x), _(C,x), _(M,T), _(M,B), _(PK,B), _(x,x), _(x,x), _(x,x), /* 1738 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Buhid (1740..175F) */ + /* Buhid */ /* 1740 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1748 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1750 */ _(C,x), _(C,x), _(M,T), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), /* 1758 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Tagbanwa (1760..177F) */ + /* Tagbanwa */ /* 1760 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1768 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), /* 1770 */ _(C,x), _(x,x), _(M,T), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), /* 1778 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Khmer (1780..17FF) */ + /* 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), @@ -393,31 +346,29 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 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,x), _(Vs,x), - /* 17C8 */ _(M,R), _(RS,x), _(RS,x), _(x,x), _(CR,x), _(x,x), _(x,x), _(x,x), - /* 17D0 */ _(x,x), _(V,T), _(V,I), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 17C8 */ _(M,R), _(RS,T), _(RS,T), _(RS,T),_(CSR,T), _(M,T), _(M,T), _(M,T), + /* 17D0 */ _(M,T), _(PK,T), _(IS,x), _(M,T), _(x,x), _(x,x), _(x,x), _(x,x), /* 17D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(A,x), _(x,x), _(x,x), _(x,x), - /* 17E0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 17E8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 17F0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 17F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(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_0x1900 2208 +#define indic_offset_0x1900u 1704 - /* Limbu (1900..194F) */ + /* Limbu */ /* 1900 */ _(CP,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1908 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1910 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 1918 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), + /* 1918 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), /* 1920 */ _(M,T), _(M,T), _(M,B), _(M,R), _(M,R), _(M,TR), _(M,TR), _(M,T), /* 1928 */ _(M,T), _(CS,x), _(CS,x), _(CS,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1930 */ _(CF,x), _(CF,x), _(Bi,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), /* 1938 */ _(CF,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1940 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1948 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1940 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), + /* 1948 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), - /* Tai Le (1950..197F) */ + /* Tai Le */ /* 1950 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1958 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -426,7 +377,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1970 */ _(TL,x), _(TL,x), _(TL,x), _(TL,x), _(TL,x), _(x,x), _(x,x), _(x,x), /* 1978 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* New Tai Lue (1980..19DF) */ + /* New Tai Lue */ /* 1980 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1988 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -438,24 +389,21 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 19B8 */ _(M,R), _(M,R), _(M,L), _(M,R), _(M,R), _(M,R), _(M,R), _(M,R), /* 19C0 */ _(M,R), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), /* 19C8 */ _(TM,x), _(TM,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 19D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 19D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* FILLER (19E0..19FF) */ - + /* 19D0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 19D8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 19E0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 19E8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 19F0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 19F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Buginese (1A00..1A1F) */ + /* Buginese */ /* 1A00 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1A08 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1A10 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,T), - /* 1A18 */ _(M,B), _(M,L), _(M,R), _(M,L), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1A18 */ _(M,B), _(M,L), _(M,R), _(M,T), _(x,x), _(x,x), _(x,x), _(x,x), - /* Tai Tham (1A20..1AAF) */ + /* Tai Tham */ /* 1A20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1A28 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -463,25 +411,23 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1A38 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1A40 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1A48 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), - /* 1A50 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(CM,x), _(CM,x), _(CF,x), + /* 1A50 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(CM,L), _(CM,x), _(CF,x), /* 1A58 */ _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(x,x), - /* 1A60 */ _(V,I), _(M,R), _(M,T), _(M,R), _(M,R), _(M,T), _(M,T), _(M,T), + /* 1A60 */ _(IS,x), _(M,R), _(M,T), _(M,R), _(M,R), _(M,T), _(M,T), _(M,T), /* 1A68 */ _(M,T), _(M,B), _(M,B), _(M,T), _(M,B), _(M,R), _(M,L), _(M,L), /* 1A70 */ _(M,L), _(M,L), _(M,L), _(M,T), _(M,T), _(TM,x), _(TM,x), _(TM,x), /* 1A78 */ _(TM,x), _(TM,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1A80 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1A88 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1A90 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1A98 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1AA0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1AA8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1A80 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1A88 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1A90 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1A98 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x1b00 2640 +#define indic_offset_0x1b00u 2120 - /* Balinese (1B00..1B7F) */ + /* Balinese */ - /* 1B00 */ _(Bi,x), _(Bi,x), _(Bi,x), _(CR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), + /* 1B00 */ _(Bi,x), _(Bi,x), _(Bi,x),_(CSR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), /* 1B08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 1B10 */ _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1B18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -491,36 +437,36 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1B38 */ _(M,B), _(M,B), _(M,B), _(M,BR), _(M,TB),_(M,TBR), _(M,L), _(M,L), /* 1B40 */ _(M,LR), _(M,LR), _(M,T), _(M,TR), _(V,R), _(C,x), _(C,x), _(C,x), /* 1B48 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1B58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1B50 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1B58 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1B60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1B68 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1B70 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1B78 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Sundanese (1B80..1BBF) */ + /* Sundanese */ - /* 1B80 */ _(Bi,x), _(CR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 1B80 */ _(Bi,x),_(CSR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 1B88 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1B90 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1B98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1BA0 */ _(C,x), _(CS,x), _(CS,x), _(CS,x), _(M,T), _(M,B), _(M,L), _(M,R), - /* 1BA8 */ _(M,T), _(M,T), _(V,R), _(V,x), _(CS,x), _(CS,x), _(C,x), _(C,x), - /* 1BB0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1BB8 */ _(x,x), _(x,x), _(A,x), _(C,x), _(C,x), _(C,x), _(CF,x), _(CF,x), + /* 1BA8 */ _(M,T), _(M,T), _(PK,R), _(IS,x), _(CS,x), _(CS,x), _(C,x), _(C,x), + /* 1BB0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1BB8 */ _(Nd,x), _(Nd,x), _(A,x), _(C,x), _(C,x), _(C,x), _(CF,x), _(CF,x), - /* Batak (1BC0..1BFF) */ + /* Batak */ /* 1BC0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1BC8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1BD0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1BD8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* 1BE0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(N,x), _(M,x), - /* 1BE8 */ _(M,x), _(M,x), _(M,x), _(M,x), _(M,x), _(M,x), _(M,x), _(M,x), - /* 1BF0 */ _(CF,x), _(CF,x), _(V,R), _(V,R), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1BE0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(N,x), _(M,R), + /* 1BE8 */ _(M,T), _(M,T), _(M,R), _(M,R), _(M,R), _(M,T), _(M,R), _(M,T), + /* 1BF0 */ _(CF,x), _(CF,x), _(PK,R), _(PK,R), _(x,x), _(x,x), _(x,x), _(x,x), /* 1BF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Lepcha (1C00..1C4F) */ + /* Lepcha */ /* 1C00 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1C08 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -528,41 +474,45 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1C18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1C20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(CS,x), _(CS,x), _(M,R), _(M,L), /* 1C28 */ _(M,L), _(M,TL), _(M,R), _(M,R), _(M,B), _(CF,x), _(CF,x), _(CF,x), - /* 1C30 */ _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(Bi,x), _(Bi,x), _(x,x), _(N,x), + /* 1C30 */ _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(Bi,L), _(Bi,L), _(x,x), _(N,x), /* 1C38 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1C40 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1C48 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), + /* 1C40 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 1C48 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), -#define indic_offset_0x1cd0 2976 +#define indic_offset_0x1cd0u 2456 - /* Vedic Extensions (1CD0..1CFF) */ + /* Vedic Extensions */ - /* 1CD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1CD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1CE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CD0 */ _(TM,x), _(TM,x), _(TM,x), _(x,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), + /* 1CD8 */ _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), _(TM,x), + /* 1CE0 */ _(TM,x), _(TM,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1CE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1CF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(TM,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x2008u 2496 + + + /* General Punctuation */ -#define indic_offset_0xa800 3024 + /* 2008 */ _(x,x), _(x,x), _(x,x), _(x,x),_(ZWNJ,x),_(ZWJ,x), _(x,x), _(x,x), + /* 2010 */ _(x,x), _(x,x), _(CP,x), _(CP,x), _(CP,x), _(x,x), _(x,x), _(x,x), +#define indic_offset_0xa800u 2512 - /* Syloti Nagri (A800..A82F) */ - /* A800 */ _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(V,T), _(C,x), + /* Syloti Nagri */ + + /* A800 */ _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(PK,T), _(C,x), /* A808 */ _(C,x), _(C,x), _(C,x), _(Bi,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A810 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A818 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A820 */ _(C,x), _(C,x), _(C,x), _(M,R), _(M,R), _(M,B), _(M,T), _(M,R), /* A828 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* FILLER (A830..A83F) */ - /* A830 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A838 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Phags-pa (A840..A87F) */ + /* Phags-pa */ /* A840 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A848 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -573,7 +523,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* A870 */ _(C,x), _(CS,x), _(C,x), _(Bi,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A878 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Saurashtra (A880..A8DF) */ + /* Saurashtra */ /* A880 */ _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* A888 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), @@ -585,44 +535,41 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* A8B8 */ _(M,R), _(M,R), _(M,R), _(M,R), _(M,R), _(M,R), _(M,R), _(M,R), /* A8C0 */ _(M,R), _(M,R), _(M,R), _(M,R), _(V,B), _(x,x), _(x,x), _(x,x), /* A8C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A8D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A8D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* A8D0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* A8D8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* FILLER (A8E0..A8FF) */ + /* Devanagari Extended */ - /* A8E0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A8E8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A8F0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* A8E0 */ _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), + /* A8E8 */ _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), + /* A8F0 */ _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A8F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Kayah Li (A900..A92F) */ + /* Kayah Li */ - /* A900 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A908 */ _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* A900 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* A908 */ _(Nd,x), _(Nd,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A910 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A918 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A920 */ _(C,x), _(C,x), _(Vo,x), _(Vo,x), _(Vo,x), _(Vo,x), _(Vo,x), _(Vo,x), /* A928 */ _(Vo,x), _(Vo,x), _(Vo,x), _(TM,x), _(TM,x), _(TM,x), _(x,x), _(x,x), - /* Rejang (A930..A95F) */ + /* Rejang */ /* A930 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A938 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A940 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,B), /* A948 */ _(M,B), _(M,B), _(M,T), _(M,B), _(M,B), _(M,B), _(M,B), _(CF,x), - /* A950 */ _(CF,x), _(CF,x), _(CF,x), _(V,R), _(x,x), _(x,x), _(x,x), _(x,x), + /* A950 */ _(CF,x), _(CF,x), _(CF,x), _(PK,R), _(x,x), _(x,x), _(x,x), _(x,x), /* A958 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - - /* FILLER (A960..A97F) */ - /* A960 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A968 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A970 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A978 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Javanese (A980..A9DF) */ + /* Javanese */ - /* A980 */ _(Bi,x), _(Bi,x), _(CR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* A980 */ _(Bi,x), _(Bi,x),_(CSR,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* A988 */ _(VI,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), /* A990 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* A998 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -632,17 +579,17 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* A9B8 */ _(M,B), _(M,B), _(M,L), _(M,L), _(M,T), _(CS,x), _(CM,x), _(CM,x), /* A9C0 */ _(V,BR), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* A9C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A9D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A9D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* A9D0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* A9D8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* FILLER (A9E0..A9FF) */ + /* Myanmar Extended-B */ - /* A9E0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A9E8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A9F0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* A9F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* A9E0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(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), - /* Cham (AA00..AA5F) */ + /* Cham */ /* AA00 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), /* AA08 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -650,21 +597,21 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* AA18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* AA20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* AA28 */ _(C,x), _(M,T), _(M,T), _(M,T), _(M,T), _(M,B), _(M,T), _(M,L), - /* AA30 */ _(M,L), _(M,T), _(M,B), _(CM,x), _(CM,x), _(CM,x), _(CM,x), _(x,x), + /* AA30 */ _(M,L), _(M,T), _(M,B), _(CM,x), _(CM,L), _(CM,x), _(CM,x), _(x,x), /* AA38 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* AA40 */ _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), /* AA48 */ _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(x,x), _(x,x), - /* AA50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* AA58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* AA50 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* AA58 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Myanmar Extended-A (AA60..AA7F) */ + /* 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), _(x,x), _(x,x), _(x,x), _(x,x), - /* AA78 */ _(x,x), _(x,x), _(C,x), _(TM,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* AA78 */ _(x,x), _(x,x), _(C,x), _(TM,x), _(TM,x), _(TM,x), _(C,x), _(C,x), - /* Tai Viet (AA80..AADF) */ + /* Tai Viet */ /* AA80 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* AA88 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -679,31 +626,30 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* AAD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* AAD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Meetei Mayek Extensions (AAE0..AAFF) */ + /* Meetei Mayek Extensions */ /* AAE0 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* AAE8 */ _(C,x), _(C,x), _(C,x), _(M,L), _(M,B), _(M,T), _(M,L), _(M,R), - /* AAF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Vs,x), _(V,I), _(x,x), - /* AAF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* AAF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Vs,x), _(IS,x), _(x,x), -#define indic_offset_0xabc0 3792 +#define indic_offset_0xabc0u 3272 - /* Meetei Mayek (ABC0..ABFF) */ + /* Meetei Mayek */ /* ABC0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* ABC8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), /* ABD0 */ _(C,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* ABD8 */ _(C,x), _(C,x), _(C,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), /* ABE0 */ _(CF,x), _(CF,x), _(CF,x), _(M,R), _(M,R), _(M,T), _(M,R), _(M,R), - /* ABE8 */ _(M,B), _(M,R), _(M,R), _(x,x), _(TM,x), _(V,B), _(x,x), _(x,x), - /* ABF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* ABF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* ABE8 */ _(M,B), _(M,R), _(M,R), _(x,x), _(TM,x), _(PK,B), _(x,x), _(x,x), + /* ABF0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* ABF8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x10a00 3856 +#define indic_offset_0x10a00u 3336 - /* Kharoshthi (10A00..10A5F) */ + /* Kharoshthi */ /* 10A00 */ _(C,x), _(M,O), _(M,B), _(M,B), _(x,x), _(M,T), _(M,O), _(x,x), /* 10A08 */ _(x,x), _(x,x), _(x,x), _(x,x), _(M,B), _(x,x), _(Bi,x), _(Vs,x), @@ -712,16 +658,13 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 10A20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 10A28 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 10A30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 10A38 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(V,I), - /* 10A40 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 10A48 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 10A50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 10A58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 10A38 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(IS,x), + /* 10A40 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), -#define indic_offset_0x11000 3952 +#define indic_offset_0x11000u 3408 - /* Brahmi (11000..1107F) */ + /* Brahmi */ /* 11000 */ _(Bi,x), _(Bi,x), _(Vs,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 11008 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), @@ -733,14 +676,14 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 11038 */ _(M,T), _(M,T), _(M,T), _(M,T), _(M,B), _(M,B), _(M,B), _(M,B), /* 11040 */ _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), _(M,T), _(V,T), _(x,x), /* 11048 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 11050 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 11058 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 11060 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 11068 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11050 */ _(x,x), _(x,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x), + /* 11058 */_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x), + /* 11060 */_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x),_(BJN,x), _(Nd,x), _(Nd,x), + /* 11068 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), /* 11070 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 11078 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11078 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(NJ,x), - /* Kaithi (11080..110CF) */ + /* Kaithi */ /* 11080 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 11088 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), @@ -750,13 +693,11 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 110A8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 110B0 */ _(M,R), _(M,L), _(M,R), _(M,B), _(M,B), _(M,T), _(M,T), _(M,R), /* 110B8 */ _(M,R), _(V,B), _(N,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 110C0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 110C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x11100 4160 +#define indic_offset_0x11100u 3600 - /* Chakma (11100..1114F) */ + /* Chakma */ /* 11100 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), /* 11108 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -764,15 +705,21 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 11118 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 11120 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,T), /* 11128 */ _(M,T), _(M,T), _(M,B), _(M,B), _(M,L), _(M,T), _(M,TB), _(M,TB), - /* 11130 */ _(M,T), _(M,B), _(M,B), _(V,I), _(V,T), _(x,x), _(x,x), _(x,x), - /* 11138 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11130 */ _(M,T), _(M,B), _(M,B), _(IS,x), _(PK,T), _(x,x), _(Nd,x), _(Nd,x), + /* 11138 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), /* 11140 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 11148 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x11180 4240 + /* Mahajani */ + /* 11150 */ _(Vo,x), _(Vo,x), _(Vo,x), _(Vo,x), _(Vo,x), _(C,x), _(C,x), _(C,x), + /* 11158 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11160 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11168 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11170 */ _(C,x), _(C,x), _(C,x), _(N,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11178 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* Sharada (11180..111DF) */ + /* Sharada */ /* 11180 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 11188 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), @@ -784,13 +731,116 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 111B8 */ _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), _(M,TR), /* 111C0 */ _(V,R), _(A,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 111C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 111D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 111D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - -#define indic_offset_0x11680 4336 - - - /* Takri (11680..116CF) */ + /* 111D0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 111D8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Sinhala Archaic Numbers */ + + /* 111E0 */ _(x,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 111E8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 111F0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), + /* 111F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Khojki */ + + /* 11200 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11208 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11210 */ _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11218 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11220 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11228 */ _(C,x), _(C,x), _(C,x), _(C,x), _(M,R), _(M,R), _(M,R), _(M,B), + /* 11230 */ _(M,T), _(M,T), _(M,TR), _(M,TR), _(Bi,x), _(V,R), _(N,x), _(GM,T), + +#define indic_offset_0x112b0u 3912 + + + /* Khudawadi */ + + /* 112B0 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 112B8 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 112C0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 112C8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 112D0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 112D8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(Bi,x), + /* 112E0 */ _(M,R), _(M,L), _(M,R), _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), + /* 112E8 */ _(M,T), _(N,x), _(PK,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 112F0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 112F8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Grantha */ + + /* 11300 */ _(x,x), _(Bi,x), _(Bi,x), _(Vs,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 11308 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(VI,x), + /* 11310 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), + /* 11318 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11320 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11328 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11330 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), + /* 11338 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,x), _(A,x), _(M,R), _(M,R), + /* 11340 */ _(M,T), _(M,R), _(M,R), _(M,R), _(M,R), _(x,x), _(x,x), _(M,L), + /* 11348 */ _(M,L), _(x,x), _(x,x), _(M,LR), _(M,LR), _(V,R), _(x,x), _(x,x), + /* 11350 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), + /* 11358 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11360 */ _(VI,x), _(VI,x), _(M,R), _(M,R), _(x,x), _(x,x), _(Ca,x), _(Ca,x), + /* 11368 */ _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), + /* 11370 */ _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x11480u 4112 + + + /* Tirhuta */ + + /* 11480 */ _(x,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11488 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), + /* 11490 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11498 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 114A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 114A8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 114B0 */ _(M,R), _(M,L), _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,B), + /* 114B8 */ _(M,B), _(M,L), _(M,T), _(M,TL), _(M,LR), _(M,R), _(M,LR), _(Bi,x), + /* 114C0 */ _(Bi,x), _(Vs,x), _(V,B), _(N,x), _(A,x), _(x,x), _(x,x), _(x,x), + /* 114C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 114D0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 114D8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x11580u 4208 + + + /* Siddham */ + + /* 11580 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11588 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), + /* 11590 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11598 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 115A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 115A8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,R), + /* 115B0 */ _(M,L), _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), + /* 115B8 */ _(M,L), _(M,TL), _(M,LR),_(M,TLR), _(Bi,x), _(Bi,x), _(Vs,x), _(V,B), + /* 115C0 */ _(N,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x11600u 4280 + + + /* Modi */ + + /* 11600 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11608 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), + /* 11610 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11618 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11620 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11628 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11630 */ _(M,R), _(M,R), _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,B), + /* 11638 */ _(M,B), _(M,T), _(M,T), _(M,R), _(M,R), _(Bi,x), _(Vs,x), _(V,B), + /* 11640 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11648 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11650 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 11658 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11660 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11668 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11670 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11678 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + + /* Takri */ /* 11680 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 11688 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -800,30 +850,57 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 116A8 */ _(C,x), _(C,x), _(C,x), _(Bi,x), _(Vs,x), _(M,T), _(M,L), _(M,R), /* 116B0 */ _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), _(M,T), _(V,T), _(N,x), /* 116B8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 116C0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 116C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - -#define indic_offset_total 4416 + /* 116C0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), + /* 116C8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -}; /* Table occupancy: 60% */ +}; /* Table items: 4488; occupancy: 73% */ -static INDIC_TABLE_ELEMENT_TYPE -get_indic_categories (hb_codepoint_t u) +INDIC_TABLE_ELEMENT_TYPE +hb_indic_get_categories (hb_codepoint_t u) { - if (0x0900 <= u && u <= 0x10A0) return indic_table[u - 0x0900 + indic_offset_0x0900]; - if (0x1700 <= u && u <= 0x1800) return indic_table[u - 0x1700 + indic_offset_0x1700]; - if (0x1900 <= u && u <= 0x1AB0) return indic_table[u - 0x1900 + indic_offset_0x1900]; - if (0x1B00 <= u && u <= 0x1C50) return indic_table[u - 0x1B00 + indic_offset_0x1b00]; - if (0x1CD0 <= u && u <= 0x1D00) return indic_table[u - 0x1CD0 + indic_offset_0x1cd0]; - if (0xA800 <= u && u <= 0xAB00) return indic_table[u - 0xA800 + indic_offset_0xa800]; - if (0xABC0 <= u && u <= 0xAC00) return indic_table[u - 0xABC0 + indic_offset_0xabc0]; - if (0x10A00 <= u && u <= 0x10A60) return indic_table[u - 0x10A00 + indic_offset_0x10a00]; - if (0x11000 <= u && u <= 0x110D0) return indic_table[u - 0x11000 + indic_offset_0x11000]; - if (0x11100 <= u && u <= 0x11150) return indic_table[u - 0x11100 + indic_offset_0x11100]; - if (0x11180 <= u && u <= 0x111E0) return indic_table[u - 0x11180 + indic_offset_0x11180]; - if (0x11680 <= u && u <= 0x116D0) return indic_table[u - 0x11680 + indic_offset_0x11680]; - if (unlikely (u == 0x00A0)) return _(CP,x); - if (unlikely (u == 0x25CC)) return _(CP,x); + 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, 0x00D0u, 0x00D7u)) return indic_table[u - 0x00D0u + indic_offset_0x00d0u]; + 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, 0x1700u, 0x17EFu)) return indic_table[u - 0x1700u + indic_offset_0x1700u]; + if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return indic_table[u - 0x1900u + indic_offset_0x1900u]; + if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return indic_table[u - 0x1B00u + indic_offset_0x1b00u]; + if (hb_in_range (u, 0x1CD0u, 0x1CF7u)) 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 (unlikely (u == 0x25CCu)) return _(CP,x); + break; + + case 0xAu: + if (hb_in_range (u, 0xA800u, 0xAAF7u)) return indic_table[u - 0xA800u + indic_offset_0xa800u]; + if (hb_in_range (u, 0xABC0u, 0xABFFu)) return indic_table[u - 0xABC0u + indic_offset_0xabc0u]; + break; + + case 0x10u: + if (hb_in_range (u, 0x10A00u, 0x10A47u)) return indic_table[u - 0x10A00u + indic_offset_0x10a00u]; + break; + + case 0x11u: + if (hb_in_range (u, 0x11000u, 0x110BFu)) return indic_table[u - 0x11000u + indic_offset_0x11000u]; + if (hb_in_range (u, 0x11100u, 0x11237u)) return indic_table[u - 0x11100u + indic_offset_0x11100u]; + if (hb_in_range (u, 0x112B0u, 0x11377u)) return indic_table[u - 0x112B0u + indic_offset_0x112b0u]; + if (hb_in_range (u, 0x11480u, 0x114DFu)) return indic_table[u - 0x11480u + indic_offset_0x11480u]; + if (hb_in_range (u, 0x11580u, 0x115C7u)) return indic_table[u - 0x11580u + indic_offset_0x11580u]; + if (hb_in_range (u, 0x11600u, 0x116CFu)) return indic_table[u - 0x11600u + indic_offset_0x11600u]; + break; + + default: + break; + } return _(x,x); } @@ -831,17 +908,27 @@ get_indic_categories (hb_codepoint_t u) #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_CM #undef ISC_CP -#undef ISC_CR +#undef ISC_CPR #undef ISC_CS +#undef ISC_CSR +#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_TL #undef ISC_TM @@ -853,7 +940,6 @@ get_indic_categories (hb_codepoint_t u) #undef IMC_B #undef IMC_BR -#undef IMC_I #undef IMC_L #undef IMC_LR #undef IMC_x @@ -867,6 +953,4 @@ get_indic_categories (hb_codepoint_t u) #undef IMC_TR #undef IMC_VOL -#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */ - /* == End of generated table == */ diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index f168fe1..7723600 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -25,198 +25,608 @@ */ #include "hb-ot-shape-complex-indic-private.hh" -#include "hb-ot-shape-private.hh" +#include "hb-ot-layout-private.hh" -struct indic_options_t +/* 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) { - int initialized : 1; - int uniscribe_bug_compatible : 1; -}; + 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; +} -union indic_options_union_t { - int i; - indic_options_t opts; +/* 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 */ }; -ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t)); -static indic_options_union_t -indic_options_init (void) +static inline bool +is_ra (hb_codepoint_t u) { - indic_options_union_t u; - u.i = 0; - u.opts.initialized = 1; + for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++) + if (u == ra_chars[i]) + return true; + return false; +} - char *c = getenv ("HB_OT_INDIC_OPTIONS"); - u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); +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 (info.indic_category()) & flags); +} - return u; +static inline bool +is_joiner (const hb_glyph_info_t &info) +{ + return is_one_of (info, JOINER_FLAGS); } -inline indic_options_t -indic_options (void) +static inline bool +is_consonant (const hb_glyph_info_t &info) { - static indic_options_union_t options; + 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 + */ + - if (unlikely (!options.i)) { - /* This is idempotent and threadsafe. */ - options = indic_options_init (); + /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe + * treats a whole bunch of characters similarly. + * TESTS: For example, for U+0951: + * U+092E,U+0947,U+0952 + * U+092E,U+0952,U+0947 + * U+092E,U+0947,U+0951 + * U+092E,U+0951,U+0947 + * U+092E,U+0951,U+0952 + * U+092E,U+0952,U+0951 + */ + if (unlikely (hb_in_ranges (u, 0x0951u, 0x0952u, + 0x1CD0u, 0x1CD2u, + 0x1CD4u, 0x1CE1u) || + u == 0x1CF4u)) + cat = OT_A; + /* The following act more like the Bindus. */ + else 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 (hb_in_range (u, 0x17CDu, 0x17D1u) || + u == 0x17CBu || u == 0x17D3u || u == 0x17DDu)) /* Khmer Various signs */ + { + /* These are like Top Matras. */ + cat = OT_M; + pos = POS_ABOVE_C; } + else if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */ + else if (unlikely (u == 0x17D2u)) cat = OT_Coeng; /* Khmer coeng */ + else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u))) + cat = OT_PLACEHOLDER; + else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE; + else if (unlikely (u == 0xA982u)) cat = OT_SM; /* Javanese repha. */ + else if (unlikely (u == 0xA9BEu)) cat = OT_CM2; /* Javanese medial ya. */ + else if (unlikely (u == 0xA9BDu)) { cat = OT_M; pos = POS_POST_C; } /* Javanese vocalic r. */ + + + /* + * Re-assign position. + */ - return options.opts; -} + if ((FLAG (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 (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. */ -static int -compare_codepoint (const void *pa, const void *pb) -{ - hb_codepoint_t a = * (hb_codepoint_t *) pa; - hb_codepoint_t b = * (hb_codepoint_t *) pb; - return a < b ? -1 : a == b ? 0 : +1; + + info.indic_category() = cat; + info.indic_position() = pos; } -static indic_position_t -consonant_position (hb_codepoint_t u) -{ - consonant_position_t *record; +/* + * Things above this line should ideally be moved to the Indic table itself. + */ - record = (consonant_position_t *) bsearch (&u, consonant_positions, - ARRAY_LENGTH (consonant_positions), - sizeof (consonant_positions[0]), - compare_codepoint); - return record ? record->position : POS_BASE_C; -} +/* + * 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. + */ -static bool -is_ra (hb_codepoint_t u) +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. */ +}; +enum pref_len_t { + PREF_LEN_1 = 1, + PREF_LEN_2 = 2, + PREF_LEN_DONT_CARE = PREF_LEN_2 +}; +struct indic_config_t { - return !!bsearch (&u, ra_chars, - ARRAY_LENGTH (ra_chars), - sizeof (ra_chars[0]), - compare_codepoint); -} + 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; + pref_len_t pref_len; +}; -static bool -is_joiner (const hb_glyph_info_t &info) +static const indic_config_t indic_configs[] = { - return !!(FLAG (info.indic_category()) & (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))); -} + /* Default. Should be first. */ + {HB_SCRIPT_INVALID, false, 0,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_1}, + {HB_SCRIPT_DEVANAGARI,true, 0x094Du,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_BENGALI, true, 0x09CDu,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_GUJARATI, true, 0x0ACDu,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_ORIYA, true, 0x0B4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_TAMIL, true, 0x0BCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, + {HB_SCRIPT_TELUGU, true, 0x0C4Du,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY, PREF_LEN_2}, + {HB_SCRIPT_KANNADA, true, 0x0CCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY, PREF_LEN_2}, + {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, + {HB_SCRIPT_SINHALA, false,0x0DCAu,BASE_POS_LAST_SINHALA, + REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, + {HB_SCRIPT_KHMER, false,0x17D2u,BASE_POS_FIRST,REPH_POS_DONT_CARE, REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, + {HB_SCRIPT_JAVANESE, false,0xA9C0u,BASE_POS_FIRST,REPH_POS_DONT_CARE, REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_1}, +}; -static bool -is_consonant (const hb_glyph_info_t &info) -{ - /* Note: - * - * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels - * cannot happen in a consonant syllable. The plus side however is, we can call the - * consonant syllable logic from the vowel syllable function and get it all right! */ - return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))); -} + + +/* + * Indic shaper. + */ struct feature_list_t { hb_tag_t tag; - hb_bool_t is_global; + hb_ot_map_feature_flags_t flags; }; static const feature_list_t -indic_basic_features[] = -{ - {HB_TAG('n','u','k','t'), true}, - {HB_TAG('a','k','h','n'), false}, - {HB_TAG('r','p','h','f'), false}, - {HB_TAG('r','k','r','f'), true}, - {HB_TAG('p','r','e','f'), false}, - {HB_TAG('b','l','w','f'), false}, - {HB_TAG('h','a','l','f'), false}, - {HB_TAG('p','s','t','f'), false}, - {HB_TAG('c','j','c','t'), false}, - {HB_TAG('v','a','t','u'), true}, +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}, }; -/* Same order as the indic_basic_features array */ +/* + * Must be in the same order as the indic_features array. + */ enum { _NUKT, - AKHN, + _AKHN, RPHF, _RKRF, PREF, BLWF, + ABVF, HALF, PSTF, - CJCT, - VATU -}; - -static const feature_list_t -indic_other_features[] = -{ - {HB_TAG('i','n','i','t'), false}, - {HB_TAG('p','r','e','s'), true}, - {HB_TAG('a','b','v','s'), true}, - {HB_TAG('b','l','w','s'), true}, - {HB_TAG('p','s','t','s'), true}, - {HB_TAG('h','a','l','n'), true}, - - {HB_TAG('d','i','s','t'), true}, - {HB_TAG('a','b','v','m'), true}, - {HB_TAG('b','l','w','m'), true}, -}; - -/* Same order as the indic_other_features array */ -enum { - INIT + _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 -initial_reordering (const hb_ot_map_t *map, - hb_face_t *face, - hb_buffer_t *buffer, - void *user_data HB_UNUSED); +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_map_t *map, - hb_face_t *face, - hb_buffer_t *buffer, - void *user_data HB_UNUSED); +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); -void -_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, - const hb_segment_properties_t *props HB_UNUSED) +static void +collect_features_indic (hb_ot_shape_planner_t *plan) { - map->add_bool_feature (HB_TAG('l','o','c','l')); + 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_bool_feature (HB_TAG('c','c','m','p')); + map->add_global_bool_feature (HB_TAG('c','c','m','p')); - map->add_gsub_pause (initial_reordering, NULL); - for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) { - map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global); - map->add_gsub_pause (NULL, NULL); + 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_gsub_pause (final_reordering, NULL); + 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); +} - for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) { - map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global); - map->add_gsub_pause (NULL, NULL); +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); } -hb_ot_shape_normalization_mode_t -_hb_ot_shape_complex_normalization_preference_indic (void) +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 { - /* We want split matras decomposed by the common shaping logic. */ - return HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; + 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_glyph (config->virama, 0, &glyph)) + glyph = 0; + /* Technically speaking, the spec says we should apply 'locl' to virama too. + * Maybe one day... */ + + /* Our get_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. */ + bool zero_context = !indic_plan->is_old_spec; + 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; + unsigned int pref_len = indic_plan->config->pref_len; + if ((pref_len == PREF_LEN_2 && + (indic_plan->pref.would_substitute (glyphs , 2, face) || + indic_plan->pref.would_substitute (glyphs+1, 2, face))) + || (pref_len == PREF_LEN_1 && + indic_plan->pref.would_substitute (glyphs+1, 1, face))) + return POS_POST_C; + return POS_BASE_C; } -void -_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED, - hb_buffer_t *buffer, - hb_font_t *font HB_UNUSED) +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); @@ -225,39 +635,17 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED, * 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++) - { - hb_glyph_info_t &info = buffer->info[i]; - unsigned int type = get_indic_categories (info.codepoint); - - info.indic_category() = type & 0x0F; - info.indic_position() = type >> 4; - - /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe - * treats U+0951..U+0952 all as OT_VD. - * TESTS: - * U+092E,U+0947,U+0952 - * U+092E,U+0952,U+0947 - * U+092E,U+0947,U+0951 - * U+092E,U+0951,U+0947 - * */ - if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954))) - info.indic_category() = OT_VD; - - if (info.indic_category() == OT_C) { - info.indic_position() = consonant_position (info.codepoint); - if (is_ra (info.codepoint)) - info.indic_category() = OT_Ra; - } else if (info.indic_category() == OT_SM || - info.indic_category() == OT_VD) { - info.indic_position() = POS_SMVD; - } else if (unlikely (info.codepoint == 0x200C)) - info.indic_category() = OT_ZWNJ; - else if (unlikely (info.codepoint == 0x200D)) - info.indic_category() = OT_ZWJ; - else if (unlikely (info.codepoint == 0x25CC)) - info.indic_category() = OT_DOTTEDCIRCLE; - } + 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 @@ -269,13 +657,44 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) 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_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, +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; @@ -301,54 +720,144 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff * and has more than one consonant, Ra is excluded from candidates for * base consonants. */ unsigned int limit = start; - if (mask_array[RPHF] && + if (indic_plan->config->reph_pos != REPH_POS_DONT_CARE && + indic_plan->mask_array[RPHF] && start + 3 <= end && - info[start].indic_category() == OT_Ra && - info[start + 1].indic_category() == OT_H && - !is_joiner (info[start + 2])) + ( + (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) + )) { - limit += 2; - base = start; - has_reph = true; - }; - - /* -> starting from the end of the syllable, move backwards */ - unsigned int i = end; - do { - i--; - /* -> until a consonant is found */ - if (is_consonant (info[i])) + /* 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))) { - /* -> that does not have a below-base or post-base form - * (post-base forms have to follow below-base forms), */ - if (info[i].indic_position() != POS_BELOW_C && - info[i].indic_position() != POS_POST_C) - { - base = i; - break; - } + 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; + } - /* -> or that is not a pre-base reordering Ra, - * - * TODO + switch (indic_plan->config->base_pos) + { + default: + assert (false); + /* fallthrough */ + + 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. */ - /* -> or arrive at the first consonant. The consonant stopped at will - * be the base. */ - base = i; + 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; } - else - if (is_joiner (info[i])) - break; - } while (i > limit); - if (base < start) - base = start; /* Just in case... */ + 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. */ - if (has_reph && base == start) { + * 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; } @@ -390,24 +899,56 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff /* Reorder characters */ for (unsigned int i = start; i < base; i++) - info[i].indic_position() = POS_PRE_C; - info[base].indic_position() = POS_BASE_C; + 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. */ - if ((map->get_chosen_script (0) & 0x000000FF) != '2') { - /* We should only do this for Indic scripts which have a version two I guess. */ + * 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) { + if (info[i].indic_category() == OT_H) + { unsigned int j; for (j = end - 1; j > i; j--) - if (is_consonant (info[j])) + if (is_consonant (info[j]) || + (disallow_double_halants && info[j].indic_category() == OT_H)) break; - if (j > i) { + 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])); @@ -417,43 +958,99 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff } } - /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */ - if (!indic_options ().uniscribe_bug_compatible) + /* Attach misc marks to previous char to move with them. */ { - /* Please update the Uniscribe branch when touching this! */ - for (unsigned int i = start + 1; i < end; i++) - if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) - info[i].indic_position() = info[i - 1].indic_position(); - } else { - /* - * Uniscribe doesn't move the Halant with Left Matra. - * TEST: U+092B,U+093F,U+094DE - */ - /* Please update the non-Uniscribe branch when touching this! */ - for (unsigned int i = start + 1; i < end; i++) - if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) { - info[i].indic_position() = info[i - 1].indic_position(); - if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M) + indic_position_t last_pos = POS_START; + for (unsigned int i = start; i < end; i++) + { + if ((FLAG (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; } - /* We do bubble-sort, skip malicious clusters attempts */ - if (end - start < 64) + { + /* 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_bubble_sort (info + start, end - start, compare_indic_order); /* Find base again */ base = end; for (unsigned int i = start; i < end; i++) - if (info[i].indic_position() == POS_BASE_C) { - base = i; + 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. */ + 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 */ @@ -463,21 +1060,86 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff /* Reph */ for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) - info[i].mask |= mask_array[RPHF]; + info[i].mask |= indic_plan->mask_array[RPHF]; /* Pre-base */ - mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT]; + 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 = mask_array[AKHN] | mask_array[CJCT]; - info[base].mask |= mask; + mask = 0; + if (base < end) + info[base].mask |= mask; /* Post-base */ - mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT]; + 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 = indic_plan->config->pref_len; + if (indic_plan->mask_array[PREF] && base + pref_len < end) + { + assert (1 <= pref_len && pref_len <= 2); + /* 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])) { @@ -487,9 +1149,13 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff do { j--; - info[j].mask &= ~mask_array[CJCT]; + /* 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 &= ~mask_array[HALF]; + info[j].mask &= ~indic_plan->mask_array[HALF]; } while (j > start && !is_consonant (info[j])); } @@ -497,25 +1163,25 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff static void -initial_reordering_vowel_syllable (const hb_ot_map_t *map, +initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, hb_buffer_t *buffer, - hb_mask_t *mask_array, unsigned int start, unsigned int end) { /* We made the vowels look like consonants. So let's call the consonant logic! */ - initial_reordering_consonant_syllable (map, buffer, mask_array, start, end); + initial_reordering_consonant_syllable (plan, face, buffer, start, end); } static void -initial_reordering_standalone_cluster (const hb_ot_map_t *map, +initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, hb_buffer_t *buffer, - hb_mask_t *mask_array, unsigned int start, unsigned int end) { - /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain. - * Only if not in compatibility mode that is... */ + /* We treat placeholder/dotted-circle as if they are consonants, so we + * should just chain. Only if not in compatibility mode that is... */ - if (indic_options ().uniscribe_bug_compatible) + 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. @@ -524,41 +1190,169 @@ initial_reordering_standalone_cluster (const hb_ot_map_t *map, return; } - initial_reordering_consonant_syllable (map, buffer, mask_array, start, end); + initial_reordering_consonant_syllable (plan, face, buffer, start, end); } static void -initial_reordering_non_indic (const hb_ot_map_t *map HB_UNUSED, - hb_buffer_t *buffer HB_UNUSED, - hb_mask_t *mask_array HB_UNUSED, - unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) +initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We already inserted dotted-circles, so just call the standalone_cluster. */ + initial_reordering_standalone_cluster (plan, face, buffer, start, end); +} + +static void +initial_reordering_symbol_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) +{ + /* Nothing to do right now. If we ever switch to using the output + * buffer in the reordering process, we'd need to next_glyph() here. */ +} + +static void +initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) { /* Nothing to do right now. If we ever switch to using the output * buffer in the reordering process, we'd need to next_glyph() here. */ } -#include "hb-ot-shape-complex-indic-machine.hh" static void -initial_reordering (const hb_ot_map_t *map, - hb_face_t *face HB_UNUSED, - hb_buffer_t *buffer, - void *user_data HB_UNUSED) +initial_reordering_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - hb_mask_t mask_array[ARRAY_LENGTH (indic_basic_features)] = {0}; - unsigned int num_masks = ARRAY_LENGTH (indic_basic_features); - for (unsigned int i = 0; i < num_masks; i++) - mask_array[i] = map->get_1_mask (indic_basic_features[i].tag); + syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) { + case consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return; + case vowel_syllable: initial_reordering_vowel_syllable (plan, face, buffer, start, end); return; + case standalone_cluster: initial_reordering_standalone_cluster (plan, face, buffer, start, end); return; + case symbol_cluster: initial_reordering_symbol_cluster (plan, face, buffer, start, end); return; + case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return; + case non_indic_cluster: initial_reordering_non_indic_cluster (plan, face, buffer, start, end); return; + } +} + +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_glyph (0x25CCu, 0, &dottedcircle_glyph)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + set_indic_properties (dottedcircle); + dottedcircle.codepoint = dottedcircle_glyph; - find_syllables (map, buffer, mask_array); + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len) + { + 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 info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + info.syllable() = buffer->cur().syllable(); + /* TODO Set glyph_props? */ + + /* Insert dottedcircle after possible Repha. */ + while (buffer->idx < buffer->len && + last_syllable == buffer->cur().syllable() && + buffer->cur().indic_category() == OT_Repha) + buffer->next_glyph (); + + buffer->output_info (info); + } + else + buffer->next_glyph (); + } + + buffer->swap_buffers (); } static void -final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, +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); + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + if (unlikely (!count)) return; + unsigned int last = 0; + unsigned int last_syllable = info[0].syllable(); + for (unsigned int i = 1; i < count; i++) + if (last_syllable != info[i].syllable()) { + initial_reordering_syllable (plan, font->face, buffer, last, i); + last = i; + last_syllable = info[last].syllable(); + } + initial_reordering_syllable (plan, font->face, buffer, last, count); +} + +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 @@ -567,22 +1361,46 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * cluster. */ + bool try_pref = !!indic_plan->mask_array[PREF]; + /* Find base again */ - unsigned int base = end; - for (unsigned int i = start; i < end; i++) - if (info[i].indic_position() == POS_BASE_C) { - base = i; + unsigned int base; + for (base = start; base < end; base++) + if (info[base].indic_position() >= POS_BASE_C) + { + if (try_pref && base + 1 < end && indic_plan->config->pref_len == 2) + { + 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; + } + } + + 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--; - if (base == start) { - /* There's no Reph, and no left Matra to reposition. Just merge the cluster - * and go home. */ - buffer->merge_clusters (start, end); - return; - } - - unsigned int start_of_last_cluster = base; /* o Reorder matras: * @@ -594,29 +1412,55 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * halant, position is moved after it. */ + if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */ { - unsigned int new_matra_pos = base - 1; - while (new_matra_pos > start && - !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H)))) - new_matra_pos--; - /* If we found no Halant we are done. Otherwise only proceed if the Halant does - * not belong to the Matra itself! */ - if (info[new_matra_pos].indic_category() == OT_H && - info[new_matra_pos].indic_position() != POS_PRE_M) { - /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ - if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1])) - new_matra_pos++; + /* 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_matra_pos; i > start; i--) + for (unsigned int i = new_pos; i > start; i--) if (info[i - 1].indic_position () == POS_PRE_M) { - unsigned int old_matra_pos = i - 1; - hb_glyph_info_t matra = info[old_matra_pos]; - memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0])); - info[new_matra_pos] = matra; - start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster); - new_matra_pos--; + unsigned int old_pos = i - 1; + 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; + if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ + base--; + 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; } } } @@ -631,56 +1475,29 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * before post-base consonant forms, and after post-base consonant forms. */ - /* If there's anything after the Ra that has the REPH pos, it ought to be halant. - * Which means that the font has failed to ligate the Reph. In which case, we - * shouldn't move. */ + /* 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 + 1].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; - - enum reph_position_t { - REPH_AFTER_MAIN, - REPH_BEFORE_SUBSCRIPT, - REPH_AFTER_SUBSCRIPT, - REPH_BEFORE_POSTSCRIPT, - REPH_AFTER_POSTSCRIPT - } reph_pos; - - /* XXX Figure out old behavior too */ - switch ((hb_tag_t) buffer->props.script) - { - case HB_SCRIPT_MALAYALAM: - case HB_SCRIPT_ORIYA: - reph_pos = REPH_AFTER_MAIN; - break; - - case HB_SCRIPT_GURMUKHI: - reph_pos = REPH_BEFORE_SUBSCRIPT; - break; - - case HB_SCRIPT_BENGALI: - reph_pos = REPH_AFTER_SUBSCRIPT; - break; - - default: - case HB_SCRIPT_DEVANAGARI: - case HB_SCRIPT_GUJARATI: - reph_pos = REPH_BEFORE_POSTSCRIPT; - break; - - case HB_SCRIPT_KANNADA: - case HB_SCRIPT_TAMIL: - case HB_SCRIPT_TELUGU: - reph_pos = REPH_AFTER_POSTSCRIPT; - break; - } + 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_AFTER_POSTSCRIPT) + if (reph_pos == REPH_POS_AFTER_POST) { goto reph_step_5; } @@ -698,10 +1515,11 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, */ { new_reph_pos = start + 1; - while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H) + while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) new_reph_pos++; - if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) { + 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++; @@ -713,9 +1531,13 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * first consonant not ligated with main, or find the first * consonant that is not a potential pre-base reordering Ra. */ - if (reph_pos == REPH_AFTER_MAIN) + if (reph_pos == REPH_POS_AFTER_MAIN) { - /* XXX */ + 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 @@ -724,11 +1546,11 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * first matra, syllable modifier sign or vedic sign. */ /* This is our take on what step 4 is trying to say (and failing, BADLY). */ - if (reph_pos == REPH_AFTER_SUBSCRIPT) + if (reph_pos == REPH_POS_AFTER_SUB) { new_reph_pos = base; while (new_reph_pos < end && - !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD)))) + !( FLAG (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; @@ -743,7 +1565,18 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, */ reph_step_5: { - /* XXX */ + /* 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. @@ -760,8 +1593,8 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * Uniscribe doesn't do this. * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ - if (!indic_options ().uniscribe_bug_compatible && - unlikely (info[new_reph_pos].indic_category() == OT_H)) { + 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. */ @@ -773,11 +1606,14 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, reph_move: { + buffer->merge_clusters (start, new_reph_pos + 1); + /* Move */ hb_glyph_info_t reph = info[start]; memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0])); info[new_reph_pos] = reph; - start_of_last_cluster = start; /* Yay, one big cluster! */ + if (start < base && base <= new_reph_pos) + base--; } } @@ -786,88 +1622,263 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, * * If a pre-base reordering consonant is found, reorder it according to * the following rules: - * - * 1. Only reorder a glyph produced by substitution during application - * of the feature. (Note that a font may shape a Ra consonant with - * the feature generally but block it in certain contexts.) - * - * 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. */ - /* TODO */ + if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base reordering Ra. */ + { + unsigned int pref_len = indic_plan->config->pref_len; + 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... + * + * If pref len is longer than one, then only reorder if it ligated. If + * pref len is one, only reorder if it didn't ligate with other things. */ + if (_hb_glyph_info_substituted (&info[i]) && + ((pref_len == 1) ^ _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 i = base + 1; i < old_pos; i++) + if (info[i].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 (_hb_glyph_info_get_general_category (&info[start - 1])) & - (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))) - info[start].mask |= mask_array[INIT]; - - + 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 (!indic_options ().uniscribe_bug_compatible) + /* + * Finish off the clusters and go home! + */ + if (hb_options ().uniscribe_bug_compatible) { - /* This is what Uniscribe does. Ie. add cluster boundaries after Halant,ZWNJ. - * This means, half forms are submerged into the main consonants cluster. - * This is unnecessary, and makes cursor positioning harder, but that's what - * Uniscribe does. */ - unsigned int cluster_start = start; - for (unsigned int i = start + 1; i < start_of_last_cluster; i++) - if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) { - i++; - buffer->merge_clusters (cluster_start, i); - cluster_start = i; - } - start_of_last_cluster = cluster_start; - } + switch ((hb_tag_t) plan->props.script) + { + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_SINHALA: + break; - buffer->merge_clusters (start_of_last_cluster, end); + 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_map_t *map, - hb_face_t *face HB_UNUSED, - hb_buffer_t *buffer, - void *user_data HB_UNUSED) +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 (!count) return; - - hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0}; - unsigned int num_masks = ARRAY_LENGTH (indic_other_features); - for (unsigned int i = 0; i < num_masks; i++) - mask_array[i] = map->get_1_mask (indic_other_features[i].tag); + if (unlikely (!count)) return; hb_glyph_info_t *info = buffer->info; unsigned int last = 0; unsigned int last_syllable = info[0].syllable(); for (unsigned int i = 1; i < count; i++) if (last_syllable != info[i].syllable()) { - final_reordering_syllable (buffer, mask_array, last, i); + final_reordering_syllable (plan, buffer, last, i); last = i; last_syllable = info[last].syllable(); } - final_reordering_syllable (buffer, mask_array, last, count); + final_reordering_syllable (plan, buffer, last, count); 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; + case 0x0B94u : return false; + + /* + * Decompose split matras that don't have Unicode decompositions. + */ + + case 0x0F77u : *a = 0x0FB2u; *b= 0x0F81u; return true; + case 0x0F79u : *a = 0x0FB3u; *b= 0x0F81u; return true; + 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; + case 0x1925u : *a = 0x1920u; *b= 0x1923u; return true; + case 0x1926u : *a = 0x1920u; *b= 0x1924u; return true; + case 0x1B3Cu : *a = 0x1B42u; *b= 0x1B3Cu; return true; + case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true; + case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true; +#if 0 + /* This one has no decomposition in Unicode, but needs no decomposition either. */ + /* case 0x0AC9u : return false; */ + case 0x0B57u : *a = no decomp, -> RIGHT; return true; + case 0x1C29u : *a = no decomp, -> LEFT; return true; + case 0xA9C0u : *a = no decomp, -> RIGHT; return true; + case 0x111BuF : *a = no decomp, -> ABOVE; 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_glyph (ab, 0, &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 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 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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + decompose_indic, + compose_indic, + setup_masks_indic, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-misc.cc b/src/hb-ot-shape-complex-misc.cc deleted file mode 100644 index d93d4c6..0000000 --- a/src/hb-ot-shape-complex-misc.cc +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright © 2010 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#include "hb-ot-shape-complex-private.hh" - - -/* TODO Add kana, and other small shapers here */ - -/* When adding trivial shapers, eg. kana, hangul, etc, we can either - * add a full shaper enum value for them, or switch on the script in - * the default complex shaper. The former is faster, so I think that's - * what we would do, and hence the default complex shaper shall remain - * empty. - */ - -void -_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSED, - const hb_segment_properties_t *props HB_UNUSED) -{ -} - -hb_ot_shape_normalization_mode_t -_hb_ot_shape_complex_normalization_preference_default (void) -{ - return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; -} - -void -_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED, - hb_buffer_t *buffer HB_UNUSED, - hb_font_t *font HB_UNUSED) -{ -} - - - -/* Hangul shaper */ - -static const hb_tag_t hangul_features[] = -{ - HB_TAG('l','j','m','o'), - HB_TAG('v','j','m','o'), - HB_TAG('t','j','m','o'), -}; - -void -_hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map, - const hb_segment_properties_t *props HB_UNUSED) -{ - for (unsigned int i = 0; i < ARRAY_LENGTH (hangul_features); i++) - map->add_bool_feature (hangul_features[i]); -} - -hb_ot_shape_normalization_mode_t -_hb_ot_shape_complex_normalization_preference_hangul (void) -{ - return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; -} - -void -_hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map HB_UNUSED, - hb_buffer_t *buffer HB_UNUSED, - hb_font_t *font HB_UNUSED) -{ -} - - - -/* Thai / Lao shaper */ - -void -_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED, - const hb_segment_properties_t *props HB_UNUSED) -{ -} - -hb_ot_shape_normalization_mode_t -_hb_ot_shape_complex_normalization_preference_thai (void) -{ - return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; -} - -void -_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED, - hb_buffer_t *buffer, - hb_font_t *font HB_UNUSED) -{ - /* The following is NOT specified in the MS OT Thai spec, however, it seems - * to be what Uniscribe and other engines implement. According to Eric Muller: - * - * When you have a sara am, decompose it in nikhahit + sara a, *and* mode the - * nihka hit backwards over any *tone* mark (0E48-0E4B). - * - * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32> - * - * This reordering is legit only when the nikhahit comes from a sara am, not - * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably - * not what a u↪ser wanted, but the rendering is nevertheless nikhahit above - * chattawa. - * - * Same for Lao. - */ - - /* - * Here are the characters of significance: - * - * Thai Lao - * SARA AM: U+0E33 U+0EB3 - * SARA AA: U+0E32 U+0EB2 - * Nikhahit: U+0E4D U+0ECD - * - * Tone marks: - * Thai: <0E48..0E4B> CCC=107 - * Lao: <0EC8..0ECB> CCC=122 - * - * Note how the Lao versions are the same as Thai + 0x80. - */ - - /* We only get one script at a time, so a script-agnostic implementation - * is adequate here. */ -#define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33) -#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D) -#define SARA_AA_FROM_SARA_AM(x) ((x) - 1) -#define IS_TONE_MARK(x) (((x) & ~0x0083) == 0x0E48) - - buffer->clear_output (); - unsigned int count = buffer->len; - for (buffer->idx = 0; buffer->idx < count;) - { - hb_codepoint_t u = buffer->cur().codepoint; - if (likely (!IS_SARA_AM (u))) { - buffer->next_glyph (); - continue; - } - - /* Is SARA AM. Decompose and reorder. */ - hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)), - hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))}; - buffer->replace_glyphs (1, 2, decomposed); - if (unlikely (buffer->in_error)) - return; - - /* Ok, let's see... */ - unsigned int end = buffer->out_len; - unsigned int start = end - 2; - while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint)) - start--; - - /* Move Nikhahit (end-2) to the beginning */ - hb_glyph_info_t t = buffer->out_info[end - 2]; - memmove (buffer->out_info + start + 1, - buffer->out_info + start, - sizeof (buffer->out_info[0]) * (end - start - 2)); - buffer->out_info[start] = t; - - /* XXX Make this easier! */ - /* Make cluster */ - for (; start > 0 && buffer->out_info[start - 1].cluster == buffer->out_info[start].cluster; start--) - ; - for (; buffer->idx < count;) - if (buffer->cur().cluster == buffer->prev().cluster) - buffer->next_glyph (); - else - break; - end = buffer->out_len; - - buffer->merge_out_clusters (start, end); - } - buffer->swap_buffers (); -} diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl new file mode 100644 index 0000000..9649a91 --- /dev/null +++ b/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/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc new file mode 100644 index 0000000..d016380 --- /dev/null +++ b/src/hb-ot-shape-complex-myanmar.cc @@ -0,0 +1,571 @@ +/* + * 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 (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 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; + } + + 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 (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + 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; + } + } + + buffer->merge_clusters (start, end); + /* Sit tight, rock 'n roll! */ + hb_bubble_sort (info + start, end - start, compare_myanmar_order); +} + +static void +initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We already inserted dotted-circles, so just call the consonant_syllable. */ + initial_reordering_consonant_syllable (plan, face, buffer, start, end); +} + +static void +initial_reordering_punctuation_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) +{ + /* Nothing to do right now. If we ever switch to using the output + * buffer in the reordering process, we'd need to next_glyph() here. */ +} + +static void +initial_reordering_non_myanmar_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) +{ + /* Nothing to do right now. If we ever switch to using the output + * buffer in the reordering process, we'd need to next_glyph() here. */ +} + + +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 consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return; + case punctuation_cluster: initial_reordering_punctuation_cluster (plan, face, buffer, start, end); return; + case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return; + case non_myanmar_cluster: initial_reordering_non_myanmar_cluster (plan, face, buffer, start, end); return; + } +} + +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_glyph (0x25CCu, 0, &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) + { + 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 info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + info.syllable() = buffer->cur().syllable(); + + buffer->output_info (info); + } + 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); + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + if (unlikely (!count)) return; + unsigned int last = 0; + unsigned int last_syllable = info[0].syllable(); + for (unsigned int i = 1; i < count; i++) + if (last_syllable != info[i].syllable()) { + initial_reordering_syllable (plan, font->face, buffer, last, i); + last = i; + last_syllable = info[last].syllable(); + } + initial_reordering_syllable (plan, font->face, buffer, last, count); +} + +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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + 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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_myanmar, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh index e0d93a6..e268933 100644 --- a/src/hb-ot-shape-complex-private.hh +++ b/src/hb-ot-shape-complex-private.hh @@ -29,78 +29,188 @@ #include "hb-private.hh" -#include "hb-ot-map-private.hh" +#include "hb-ot-shape-private.hh" #include "hb-ot-shape-normalize-private.hh" -/* buffer var allocations, used during the entire shaping process */ -#define unicode_props0() var1.u8[0] -#define unicode_props1() var1.u8[1] +/* buffer var allocations, used by complex shapers */ +#define complex_var_u8_0() var2.u8[2] +#define complex_var_u8_1() var2.u8[3] -/* buffer var allocations, used during the GSUB/GPOS processing */ -#define props_cache() var1.u16[1] /* GSUB/GPOS glyph_props cache */ -#define syllable() var2.u8[0] /* GSUB/GPOS shaping boundaries */ -#define lig_props() var2.u8[1] /* GSUB/GPOS ligature tracking */ -/* buffer var allocations, used by complex shapers */ -#define complex_var_persistent_u8_0() var2.u8[2] -#define complex_var_persistent_u8_1() var2.u8[3] -#define complex_var_temporary_u8() var2.u8[0] +enum hb_ot_shape_zero_width_marks_type_t { + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, +// HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + + HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_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 (sea) \ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ + HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \ /* ^--- Add new shapers here */ -enum hb_ot_complex_shaper_t { -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) hb_ot_complex_shaper_##name, - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS - /* Just here to avoid enum trailing comma: */ - hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default -#undef HB_COMPLEX_SHAPER_IMPLEMENT + +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); + + + 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); + + hb_ot_shape_zero_width_marks_type_t zero_width_marks; + + bool fallback_position; }; -static inline hb_ot_complex_shaper_t -hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) +#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) props->script) + switch ((hb_tag_t) planner->props.script) { default: - return hb_ot_complex_shaper_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: - return hb_ot_complex_shaper_arabic; + /* 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). */ + if (planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT || + planner->props.script == HB_SCRIPT_ARABIC) + return &_hb_ot_complex_shaper_arabic; + else + return &_hb_ot_complex_shaper_default; /* Unicode-1.1 additions */ - case HB_SCRIPT_HANGUL: + case HB_SCRIPT_THAI: + case HB_SCRIPT_LAO: - return hb_ot_complex_shaper_hangul; + return &_hb_ot_complex_shaper_thai; /* Unicode-1.1 additions */ - case HB_SCRIPT_THAI: - case HB_SCRIPT_LAO: + case HB_SCRIPT_HANGUL: - return hb_ot_complex_shaper_thai; + 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 */ @@ -127,9 +237,6 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) /* Unicode-5.1 additions */ case HB_SCRIPT_SAURASHTRA: - /* Unicode-5.2 additions */ - case HB_SCRIPT_MEETEI_MAYEK: - /* Unicode-6.0 additions */ case HB_SCRIPT_BATAK: case HB_SCRIPT_BRAHMI: @@ -142,9 +249,6 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) case HB_SCRIPT_LAO: case HB_SCRIPT_THAI: - /* Unicode-2.0 additions */ - case HB_SCRIPT_TIBETAN: - /* Unicode-3.2 additions */ case HB_SCRIPT_TAGALOG: case HB_SCRIPT_TAGBANWA: @@ -154,11 +258,10 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) case HB_SCRIPT_TAI_LE: /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + case HB_SCRIPT_NEW_TAI_LUE: case HB_SCRIPT_SYLOTI_NAGRI: - /* Unicode-5.0 additions */ - case HB_SCRIPT_PHAGS_PA: - /* Unicode-5.1 additions */ case HB_SCRIPT_KAYAH_LI: @@ -166,12 +269,6 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) case HB_SCRIPT_TAI_VIET: - /* May need Indic treatment in the future? */ - - /* Unicode-3.0 additions */ - case HB_SCRIPT_MYANMAR: - - #endif /* Unicode-1.1 additions */ @@ -186,19 +283,12 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) case HB_SCRIPT_TELUGU: /* Unicode-3.0 additions */ - case HB_SCRIPT_KHMER: case HB_SCRIPT_SINHALA: - /* Unicode-4.1 additions */ - case HB_SCRIPT_BUGINESE: - case HB_SCRIPT_KHAROSHTHI: - case HB_SCRIPT_NEW_TAI_LUE: - /* Unicode-5.0 additions */ case HB_SCRIPT_BALINESE: /* Unicode-5.1 additions */ - case HB_SCRIPT_CHAM: case HB_SCRIPT_LEPCHA: case HB_SCRIPT_REJANG: case HB_SCRIPT_SUNDANESE: @@ -206,105 +296,67 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) /* Unicode-5.2 additions */ case HB_SCRIPT_JAVANESE: case HB_SCRIPT_KAITHI: - case HB_SCRIPT_TAI_THAM: + case HB_SCRIPT_MEETEI_MAYEK: + + /* Unicode-6.0 additions */ /* Unicode-6.1 additions */ case HB_SCRIPT_CHAKMA: case HB_SCRIPT_SHARADA: case HB_SCRIPT_TAKRI: - return hb_ot_complex_shaper_indic; - - /* ^--- Add new shapers here */ - } -} - - - -/* - * collect_features() - * - * Called during shape_plan(). - * - * Shapers should use map to add their features and callbacks. - */ - -typedef void hb_ot_shape_complex_collect_features_func_t (hb_ot_map_builder_t *map, const hb_segment_properties_t *props); -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - HB_INTERNAL hb_ot_shape_complex_collect_features_func_t _hb_ot_shape_complex_collect_features_##name; - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT + /* If the designer designed the font for the 'DFLT' script, + * use the default shaper. Otherwise, use the Indic 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; -static inline void -hb_ot_shape_complex_collect_features (hb_ot_complex_shaper_t shaper, - hb_ot_map_builder_t *map, - const hb_segment_properties_t *props) -{ - switch (shaper) { - default: -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - case hb_ot_complex_shaper_##name: _hb_ot_shape_complex_collect_features_##name (map, props); return; - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT - } -} - - -/* - * normalization_preference() - * - * Called during shape_execute(). - * - * Shapers should return true if it prefers decomposed (NFD) input rather than precomposed (NFC). - */ - -typedef hb_ot_shape_normalization_mode_t hb_ot_shape_complex_normalization_preference_func_t (void); -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - HB_INTERNAL hb_ot_shape_complex_normalization_preference_func_t _hb_ot_shape_complex_normalization_preference_##name; - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT + 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; -static inline hb_ot_shape_normalization_mode_t -hb_ot_shape_complex_normalization_preference (hb_ot_complex_shaper_t shaper) -{ - switch (shaper) { - default: -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - case hb_ot_complex_shaper_##name: return _hb_ot_shape_complex_normalization_preference_##name (); - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT - } -} + 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-4.1 additions */ + case HB_SCRIPT_BUGINESE: -/* setup_masks() - * - * Called during shape_execute(). - * - * Shapers should use map to get feature masks and set on buffer. - */ + /* Unicode-5.1 additions */ + case HB_SCRIPT_CHAM: -typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font); -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - HB_INTERNAL hb_ot_shape_complex_setup_masks_func_t _hb_ot_shape_complex_setup_masks_##name; - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT + /* Unicode-5.2 additions */ + case HB_SCRIPT_TAI_THAM: -static inline void -hb_ot_shape_complex_setup_masks (hb_ot_complex_shaper_t shaper, - hb_ot_map_t *map, - hb_buffer_t *buffer, - hb_font_t *font) -{ - switch (shaper) { - default: -#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - case hb_ot_complex_shaper_##name: _hb_ot_shape_complex_setup_masks_##name (map, buffer, font); return; - HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS -#undef HB_COMPLEX_SHAPER_IMPLEMENT + /* If the designer designed the font for the 'DFLT' script, + * use the default shaper. Otherwise, use the Indic 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_sea; } } - #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */ diff --git a/src/hb-ot-shape-complex-sea-machine.rl b/src/hb-ot-shape-complex-sea-machine.rl new file mode 100644 index 0000000..46140fc --- /dev/null +++ b/src/hb-ot-shape-complex-sea-machine.rl @@ -0,0 +1,102 @@ +/* + * 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 + */ + +#ifndef HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH + +#include "hb-private.hh" + +%%{ + machine sea_syllable_machine; + alphtype unsigned char; + write data; +}%% + +%%{ + +# Same order as enum sea_category_t. Not sure how to avoid duplication. +C = 1; +GB = 12; # Generic Base +H = 4; # Halant +IV = 2; # Independent Vowel +MR = 22; # Medial Ra +CM = 17; # Consonant Medial +VAbv = 26; +VBlw = 27; +VPre = 28; +VPst = 29; +T = 3; # Tone Marks +A = 10; # Anusvara + +syllable_tail = (VPre|VAbv|VBlw|VPst|H.C|CM|MR|T|A)*; + +consonant_syllable = (C|IV|GB) syllable_tail; +broken_cluster = syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (consonant_syllable); }; + broken_cluster => { found_syllable (broken_cluster); }; + other => { found_syllable (non_sea_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].sea_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_SEA_MACHINE_HH */ diff --git a/src/hb-ot-shape-complex-sea.cc b/src/hb-ot-shape-complex-sea.cc new file mode 100644 index 0000000..f08b7cc --- /dev/null +++ b/src/hb-ot-shape-complex-sea.cc @@ -0,0 +1,380 @@ +/* + * 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 sea_category() complex_var_u8_0() /* indic_category_t */ +#define sea_position() complex_var_u8_1() /* indic_position_t */ + + +/* + * South-East Asian shaper. + * Loosely based on the Myanmar spec / shaper. + * There is no OpenType spec for this. + */ + +static const hb_tag_t +basic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after initial_reordering. + */ + HB_TAG('p','r','e','f'), + HB_TAG('a','b','v','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'), +}; + +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_sea (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_sea (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, + broken_cluster, + non_sea_cluster, +}; + +#include "hb-ot-shape-complex-sea-machine.hh" + + +/* Note: This enum is duplicated in the -machine.rl source file. + * Not sure how to avoid duplication. */ +enum sea_category_t { +// OT_C = 1, + OT_GB = 12, /* Generic Base XXX DOTTED CIRCLE only for now */ +// OT_H = 4, /* Halant */ + OT_IV = 2, /* Independent Vowel */ + OT_MR = 22, /* Medial Ra */ +// OT_CM = 17, /* Consonant Medial */ + OT_VAbv = 26, + OT_VBlw = 27, + OT_VPre = 28, + OT_VPst = 29, + OT_T = 3, /* Tone Marks */ +// OT_A = 10, /* Anusvara */ +}; + +static inline void +set_sea_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); + + /* Medial Ra */ + if (u == 0x1A55u || u == 0xAA34u) + cat = (indic_category_t) OT_MR; + + if (cat == OT_M) + { + switch ((int) pos) + { + case POS_PRE_C: cat = (indic_category_t) OT_VPre; 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.sea_category() = (sea_category_t) cat; + info.sea_position() = pos; +} + + +static void +setup_masks_sea (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, sea_category); + HB_BUFFER_ALLOCATE_VAR (buffer, sea_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_sea_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_sea_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->sea_position(); + int b = pb->sea_position(); + + return a < b ? -1 : a == b ? 0 : +1; +} + + +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) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int base = start; + + /* Reorder! */ + unsigned int i = start; + for (; i < base; i++) + info[i].sea_position() = POS_PRE_C; + if (i < end) + { + info[i].sea_position() = POS_BASE_C; + i++; + } + for (; i < end; i++) + { + if (info[i].sea_category() == OT_MR) /* Pre-base reordering */ + { + info[i].sea_position() = POS_PRE_C; + continue; + } + if (info[i].sea_category() == OT_VPre) /* Left matra */ + { + info[i].sea_position() = POS_PRE_M; + continue; + } + + info[i].sea_position() = POS_AFTER_MAIN; + } + + buffer->merge_clusters (start, end); + /* Sit tight, rock 'n roll! */ + hb_bubble_sort (info + start, end - start, compare_sea_order); +} + +static void +initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We already inserted dotted-circles, so just call the consonant_syllable. */ + initial_reordering_consonant_syllable (plan, face, buffer, start, end); +} + +static void +initial_reordering_non_sea_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) +{ + /* Nothing to do right now. If we ever switch to using the output + * buffer in the reordering process, we'd need to next_glyph() here. */ +} + + +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 consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return; + case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return; + case non_sea_cluster: initial_reordering_non_sea_cluster (plan, face, buffer, start, end); return; + } +} + +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_glyph (0x25CCu, 0, &dottedcircle_glyph)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + set_sea_properties (dottedcircle); + dottedcircle.codepoint = dottedcircle_glyph; + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len) + { + 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 info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + info.syllable() = buffer->cur().syllable(); + + buffer->output_info (info); + } + 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); + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + if (unlikely (!count)) return; + unsigned int last = 0; + unsigned int last_syllable = info[0].syllable(); + for (unsigned int i = 1; i < count; i++) + if (last_syllable != info[i].syllable()) { + initial_reordering_syllable (plan, font->face, buffer, last, i); + last = i; + last_syllable = info[last].syllable(); + } + initial_reordering_syllable (plan, font->face, buffer, last, count); +} + +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, sea_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, sea_position); +} + + +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea = +{ + "sea", + collect_features_sea, + override_features_sea, + NULL, /* data_create */ + NULL, /* data_destroy */ + NULL, /* preprocess_text */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + NULL, /* decompose */ + NULL, /* compose */ + setup_masks_sea, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc new file mode 100644 index 0000000..feb7fc7 --- /dev/null +++ b/src/hb-ot-shape-complex-thai.cc @@ -0,0 +1,381 @@ +/* + * 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) { + default: assert (false); /* Fallthrough */ + 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;) + { + 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 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->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, + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT, + false,/* fallback_position */ +}; diff --git a/src/hb-ot-shape-complex-tibetan.cc b/src/hb-ot-shape-complex-tibetan.cc new file mode 100644 index 0000000..01465a4 --- /dev/null +++ b/src/hb-ot-shape-complex-tibetan.cc @@ -0,0 +1,61 @@ +/* + * 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 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + NULL, /* decompose */ + NULL, /* compose */ + NULL, /* setup_masks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT, + true, /* fallback_position */ +}; diff --git a/src/hb-ot-shape-fallback-private.hh b/src/hb-ot-shape-fallback-private.hh new file mode 100644 index 0000000..ec65351 --- /dev/null +++ b/src/hb-ot-shape-fallback-private.hh @@ -0,0 +1,49 @@ +/* + * 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); + + +#endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */ diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc new file mode 100644 index 0000000..53274b5 --- /dev/null +++ b/src/hb-ot-shape-fallback.cc @@ -0,0 +1,485 @@ +/* + * 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; + } + /* Fall through */ + + 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; + + 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; + + 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 last_cluster = buffer->info[0].cluster; + unsigned int count = buffer->len; + for (unsigned int i = 1; i < count; i++) + if (buffer->info[i].cluster != last_cluster) { + position_cluster (plan, font, buffer, start, i); + start = i; + last_cluster = buffer->info[i].cluster; + } + 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; + } +} diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize-private.hh index bb81f00..c744e26 100644 --- a/src/hb-ot-shape-normalize-private.hh +++ b/src/hb-ot-shape-normalize-private.hh @@ -29,18 +29,41 @@ #include "hb-private.hh" -#include "hb-font.h" -#include "hb-buffer.h" +/* 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_FULL /* including base-to-base composition */ + 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 (hb_font_t *font, +HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, hb_buffer_t *buffer, - hb_ot_shape_normalization_mode_t mode); + 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/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 562ba88..8cc64af 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -25,6 +25,7 @@ */ #include "hb-ot-shape-normalize-private.hh" +#include "hb-ot-shape-complex-private.hh" #include "hb-ot-shape-private.hh" @@ -62,96 +63,218 @@ * knowledge too. We need to provide assistance to the itemizer. * * - When a font does not support a character but supports its decomposition, - * well, use the decomposition. + * well, use the decomposition (preferring the canonical decomposition, but + * falling back to the compatibility decomposition if necessary). The + * compatibility decomposition is really nice to have, for characters like + * ellipsis, or various-sized space characters. * - * - The Indic shaper requests decomposed output. This will handle splitting - * matra for the Indic shaper. + * - 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. + * + * - We try compatibility decomposition if decomposing through canonical + * decomposition alone failed to find a sequence that the font supports. + * We don't try compatibility decomposition recursively during the canonical + * decomposition phase. This has minimal impact. There are only a handful + * of Greek letter that have canonical decompositions that include characters + * with compatibility decomposition. Those can be found using this command: + * + * egrep "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt */ -static void -output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph) +static bool +decompose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) { - buffer->output_glyph (glyph); - _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode); + return c->unicode->decompose (ab, a, b); } static bool -decompose (hb_font_t *font, hb_buffer_t *buffer, - bool shortest, - hb_codepoint_t ab) +compose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return c->unicode->compose (a, b, ab); +} + +static inline void +set_glyph (hb_glyph_info_t &info, hb_font_t *font) { - hb_codepoint_t a, b, glyph; + font->get_glyph (info.codepoint, 0, &info.glyph_index()); +} - if (!hb_unicode_decompose (buffer->unicode, ab, &a, &b) || - (b && !hb_font_get_glyph (font, b, 0, &glyph))) - return false; +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); + _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode); +} - bool has_a = hb_font_get_glyph (font, a, 0, &glyph); +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_glyph (b, 0, &b_glyph))) + return 0; + + bool has_a = font->get_glyph (a, 0, &a_glyph); if (shortest && has_a) { /* Output a and b */ - output_glyph (buffer, a); - if (b) - output_glyph (buffer, b); - return true; + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; } - if (decompose (font, buffer, shortest, a)) { - if (b) - output_glyph (buffer, b); - return true; + 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_glyph (buffer, a); - if (b) - output_glyph (buffer, b); - return true; + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; } - return false; + return 0; } -static void -decompose_current_glyph (hb_font_t *font, hb_buffer_t *buffer, - bool shortest) +/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ +static inline unsigned int +decompose_compatibility (const hb_ot_shape_normalize_context_t *c, hb_codepoint_t u) { - if (decompose (font, buffer, shortest, buffer->cur().codepoint)) - buffer->skip_glyph (); - else - buffer->next_glyph (); + unsigned int len, i; + hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN]; + hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN]; + + len = c->buffer->unicode->decompose_compatibility (u, decomposed); + if (!len) + return 0; + + for (i = 0; i < len; i++) + if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i])) + return 0; + + for (i = 0; i < len; i++) + output_char (c->buffer, decomposed[i], glyphs[i]); + + return len; } -static void -decompose_single_char_cluster (hb_font_t *font, hb_buffer_t *buffer, - bool will_recompose) +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 recomposing and font supports this, we're good to go */ - if (will_recompose && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph)) { + /* Kind of a cute waterfall here... */ + if (shortest && c->font->get_glyph (u, 0, &glyph)) + next_char (buffer, glyph); + else if (decompose (c, shortest, u)) + skip_char (buffer); + else if (!shortest && c->font->get_glyph (u, 0, &glyph)) + next_char (buffer, glyph); + else if (decompose_compatibility (c, u)) + skip_char (buffer); + else + 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;) { + if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { + /* The next two lines are some ugly lines... But work. */ + if (font->get_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 (); - return; } - - decompose_current_glyph (font, buffer, will_recompose); } -static void -decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer, - unsigned int end) +static inline void +decompose_multi_char_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; for (unsigned int i = buffer->idx; i < end; i++) - if (unlikely (_hb_unicode_is_variation_selector (buffer->info[i].codepoint))) { - while (buffer->idx < end) - buffer->next_glyph (); + if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) { + handle_variation_selector_cluster (c, end, short_circuit); return; } while (buffer->idx < end) - decompose_current_glyph (font, buffer, false); + 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) { @@ -161,12 +284,28 @@ compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) return a < b ? -1 : a == b ? 0 : +1; } + void -_hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer, - hb_ot_shape_normalization_mode_t mode) +_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) { - bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; - bool has_multichar_clusters = false; + _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 @@ -187,20 +326,11 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer, if (buffer->cur().cluster != buffer->info[end].cluster) break; - if (buffer->idx + 1 == end) - decompose_single_char_cluster (font, buffer, recompose); - else { - decompose_multi_char_cluster (font, buffer, end); - has_multichar_clusters = true; - } + decompose_cluster (&c, end, might_short_circuit, always_short_circuit); } buffer->swap_buffers (); - if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !has_multichar_clusters) - return; /* Done! */ - - /* Second round, reorder (inplace) */ count = buffer->len; @@ -228,7 +358,8 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer, } - if (!recompose) + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED) return; /* Third round, recompose */ @@ -243,28 +374,34 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer, while (buffer->idx < count) { hb_codepoint_t composed, glyph; - if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to - * compose a CCC=0 character with it's preceding starter. */ - (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL || - _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) && + if (/* 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. */ - hb_unicode_compose (buffer->unicode, - buffer->out_info[starter].codepoint, - buffer->cur().codepoint, - &composed) && + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && /* And the font has glyph for the composite. */ - hb_font_get_glyph (font, composed, 0, &glyph)) + font->get_glyph (composed, 0, &glyph)) { - /* Composes. Modify starter and carry on. */ + /* 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; - /* XXX update cluster */ + buffer->out_info[starter].glyph_index() = glyph; _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode); - buffer->skip_glyph (); continue; } diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh index df0c705..54ac2c3 100644 --- a/src/hb-ot-shape-private.hh +++ b/src/hb-ot-shape-private.hh @@ -30,54 +30,75 @@ #include "hb-private.hh" #include "hb-ot-map-private.hh" -#include "hb-ot-shape-complex-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; - hb_ot_complex_shaper_t shaper; - - hb_ot_shape_plan_t (void) : map () {} - ~hb_ot_shape_plan_t (void) { map.finish (); } - - private: - NO_COPY (hb_ot_shape_plan_t); + 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 (); } }; - - -HB_INTERNAL hb_bool_t -_hb_ot_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); - - -inline void -_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode) -{ - info->unicode_props0() = ((unsigned int) hb_unicode_general_category (unicode, info->codepoint)) | - (_hb_unicode_is_zero_width (info->codepoint) ? 0x80 : 0); - info->unicode_props1() = _hb_unicode_modified_combining_class (unicode, info->codepoint); -} - -inline hb_unicode_general_category_t -_hb_glyph_info_get_general_category (const hb_glyph_info_t *info) +struct hb_ot_shape_planner_t { - return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F); -} + /* 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) + { + plan.props = props; + plan.shaper = shaper; + map.compile (plan.map); + + 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')); + } -inline unsigned int -_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) -{ - return info->unicode_props1(); -} + private: + NO_COPY (hb_ot_shape_planner_t); +}; -inline hb_bool_t -_hb_glyph_info_is_zero_width (const hb_glyph_info_t *info) -{ - return !!(info->unicode_props0() & 0x80); -} #endif /* HB_OT_SHAPE_PRIVATE_HH */ diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 19cf680..07adb04 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -1,6 +1,6 @@ /* * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -26,17 +26,23 @@ * 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-font-private.hh" +#include "hb-ot-layout-private.hh" +#include "hb-unicode-private.hh" #include "hb-set-private.hh" - -hb_tag_t common_features[] = { +static hb_tag_t common_features[] = { HB_TAG('c','c','m','p'), - HB_TAG('l','i','g','a'), HB_TAG('l','o','c','l'), HB_TAG('m','a','r','k'), HB_TAG('m','k','m','k'), @@ -44,62 +50,37 @@ hb_tag_t common_features[] = { }; -hb_tag_t horizontal_features[] = { +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'), }; -/* Note: - * Technically speaking, vrt2 and vert are mutually exclusive. - * According to the spec, valt and vpal are also mutually exclusive. - * But we apply them all for now. - */ -hb_tag_t vertical_features[] = { - HB_TAG('v','a','l','t'), +static hb_tag_t vertical_features[] = { HB_TAG('v','e','r','t'), - HB_TAG('v','k','r','n'), - HB_TAG('v','p','a','l'), - HB_TAG('v','r','t','2'), }; -struct hb_ot_shape_planner_t -{ - hb_ot_map_builder_t map; - hb_ot_complex_shaper_t shaper; - - hb_ot_shape_planner_t (void) : map () {} - ~hb_ot_shape_planner_t (void) { map.finish (); } - - inline void compile (hb_face_t *face, - const hb_segment_properties_t *props, - struct hb_ot_shape_plan_t &plan) - { - plan.shaper = shaper; - map.compile (face, props, plan.map); - } - - private: - NO_COPY (hb_ot_shape_planner_t); -}; - static void hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, const hb_segment_properties_t *props, const hb_feature_t *user_features, unsigned int num_user_features) { + hb_ot_map_builder_t *map = &planner->map; + switch (props->direction) { case HB_DIRECTION_LTR: - planner->map.add_bool_feature (HB_TAG ('l','t','r','a')); - planner->map.add_bool_feature (HB_TAG ('l','t','r','m')); + 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: - planner->map.add_bool_feature (HB_TAG ('r','t','l','a')); - planner->map.add_bool_feature (HB_TAG ('r','t','l','m'), false); + 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: @@ -108,33 +89,122 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, break; } -#define ADD_FEATURES(array) \ - HB_STMT_START { \ - for (unsigned int i = 0; i < ARRAY_LENGTH (array); i++) \ - planner->map.add_bool_feature (array[i]); \ - } HB_STMT_END + 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); - hb_ot_shape_complex_collect_features (planner->shaper, &planner->map, props); + if (planner->shaper->collect_features) + planner->shaper->collect_features (planner); - ADD_FEATURES (common_features); + 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)) - ADD_FEATURES (horizontal_features); + 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 - ADD_FEATURES (vertical_features); + for (unsigned int i = 0; i < ARRAY_LENGTH (vertical_features); i++) + map->add_feature (vertical_features[i], 1, F_GLOBAL | + (vertical_features[i] == HB_TAG('v','k','r','n') ? + F_HAS_FALLBACK : F_NONE)); -#undef ADD_FEATURES + 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]; - planner->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1)); + 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) +{ + 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) +{ + 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); + + 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 { - /* Input to hb_ot_shape_execute() */ hb_ot_shape_plan_t *plan; hb_font_t *font; hb_face_t *face; @@ -144,51 +214,61 @@ struct hb_ot_shape_context_t /* Transient stuff */ hb_direction_t target_direction; - hb_bool_t applied_position_complex; }; -static void -hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) -{ - hb_mask_t global_mask = c->plan->map.get_global_mask (); - c->buffer->reset_masks (global_mask); - - hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer, 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 = c->plan->map.get_mask (feature->tag, &shift); - c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); - } - } -} /* 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 (&buffer->info[i], buffer->unicode); + _hb_glyph_info_set_unicode_props (&info[i], buffer->unicode); +} + +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->unicode); + + 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->next_glyph (); + + buffer->swap_buffers (); } static void hb_form_clusters (hb_buffer_t *buffer) { unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) - if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) & - (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | - FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) - buffer->info[i].cluster = buffer->info[i - 1].cluster; /* XXX do the min() here */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))) + buffer->merge_clusters (i - 1, i + 1); } static void @@ -211,286 +291,478 @@ hb_ensure_native_direction (hb_buffer_t *buffer) /* Substitute */ -static void -hb_mirror_chars (hb_ot_shape_context_t *c) +static inline void +hb_ot_mirror_chars (hb_ot_shape_context_t *c) { - hb_unicode_funcs_t *unicode = c->buffer->unicode; - if (HB_DIRECTION_IS_FORWARD (c->target_direction)) return; - hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m')); + 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 = c->buffer->len; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t codepoint = hb_unicode_mirroring (unicode, c->buffer->info[i].codepoint); - if (likely (codepoint == c->buffer->info[i].codepoint)) - c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */ + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (likely (codepoint == info[i].codepoint)) + info[i].mask |= rtlm_mask; else - c->buffer->info[i].codepoint = codepoint; + info[i].codepoint = codepoint; } } -static void -hb_map_glyphs (hb_font_t *font, - hb_buffer_t *buffer) +static inline void +hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) { - hb_codepoint_t glyph; - - if (unlikely (!buffer->len)) + if (!c->plan->has_frac) return; - buffer->clear_output (); + hb_buffer_t *buffer = c->buffer; - unsigned int count = buffer->len - 1; - for (buffer->idx = 0; buffer->idx < count;) { - if (unlikely (_hb_unicode_is_variation_selector (buffer->cur(+1).codepoint))) { - hb_font_get_glyph (font, buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph); - buffer->replace_glyphs (2, 1, &glyph); - } else { - hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph); - buffer->replace_glyph (glyph); + /* 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; } } - if (likely (buffer->idx < buffer->len)) { - hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph); - buffer->replace_glyph (glyph); +} + +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); + } } - buffer->swap_buffers (); } -static void -hb_substitute_default (hb_ot_shape_context_t *c) +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(); +} + +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_class_mask_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_ot_layout_substitute_start (c->buffer); + hb_buffer_t *buffer = c->buffer; + + if (c->plan->shaper->preprocess_text) + c->plan->shaper->preprocess_text (c->plan, buffer, c->font); + + 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_mirror_chars (c); + hb_ot_shape_setup_masks (c); + + /* This is unfortunate to go here, but necessary... */ + if (!hb_ot_layout_has_positioning (c->face)) + _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer); - hb_map_glyphs (c->font, c->buffer); + hb_ot_map_glyphs_fast (buffer); + + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); } -static void +static inline void hb_ot_substitute_complex (hb_ot_shape_context_t *c) { - if (hb_ot_layout_has_substitution (c->face)) { - c->plan->map.substitute (c->face, c->buffer); - } + hb_buffer_t *buffer = c->buffer; + + _hb_buffer_allocate_gsubgpos_vars (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); - hb_ot_layout_substitute_finish (c->buffer); + hb_ot_layout_substitute_finish (c->font, buffer); return; } +static inline void +hb_ot_substitute (hb_ot_shape_context_t *c) +{ + hb_ot_substitute_default (c); + hb_ot_substitute_complex (c); +} /* Position */ -static void -hb_position_default (hb_ot_shape_context_t *c) +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_unicode (hb_buffer_t *buffer, bool adjust_offsets) { - hb_ot_layout_position_start (c->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) + { + if (adjust_offsets) + adjust_mark_offsets (&buffer->pos[i]); + zero_mark_width (&buffer->pos[i]); + } +} +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; - for (unsigned int i = 0; i < count; i++) { - hb_font_get_glyph_advance_for_direction (c->font, c->buffer->info[i].codepoint, - c->buffer->props.direction, - &c->buffer->pos[i].x_advance, - &c->buffer->pos[i].y_advance); - hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, - c->buffer->props.direction, - &c->buffer->pos[i].x_offset, - &c->buffer->pos[i].y_offset); + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + for (unsigned int i = 0; i < count; i++) + { + c->font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + c->font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } } -static void +static inline bool hb_ot_position_complex (hb_ot_shape_context_t *c) { + bool ret = false; + unsigned int count = c->buffer->len; + bool has_positioning = hb_ot_layout_has_positioning (c->face); + /* 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 = !(has_positioning || c->plan->shaper->fallback_position || + HB_DIRECTION_IS_BACKWARD (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; - if (hb_ot_layout_has_positioning (c->face)) + /* Not currently used for any shaper: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: + zero_mark_widths_by_unicode (c->buffer, adjust_offsets_when_zeroing); + break; + */ + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + break; + } + + if (has_positioning) { + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */ - unsigned int count = c->buffer->len; for (unsigned int i = 0; i < count; i++) { - hb_font_add_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, - HB_DIRECTION_LTR, - &c->buffer->pos[i].x_offset, - &c->buffer->pos[i].y_offset); + c->font->add_glyph_origin_for_direction (info[i].codepoint, + HB_DIRECTION_LTR, + &pos[i].x_offset, + &pos[i].y_offset); } - c->plan->map.position (c->font, c->buffer); + c->plan->position (c->font, c->buffer); for (unsigned int i = 0; i < count; i++) { - hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, - HB_DIRECTION_LTR, - &c->buffer->pos[i].x_offset, - &c->buffer->pos[i].y_offset); + c->font->subtract_glyph_origin_for_direction (info[i].codepoint, + HB_DIRECTION_LTR, + &pos[i].x_offset, + &pos[i].y_offset); } - c->applied_position_complex = true; + ret = true; } - hb_ot_layout_position_finish (c->buffer); - - return; -} + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: + zero_mark_widths_by_unicode (c->buffer, adjust_offsets_when_zeroing); + break; -static void -hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) -{ - /* TODO Mark pos */ -} + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; -static void -hb_truetype_kern (hb_ot_shape_context_t *c) -{ - /* TODO Check for kern=0 */ - unsigned int count = c->buffer->len; - for (unsigned int i = 1; i < count; i++) { - hb_position_t x_kern, y_kern, kern1, kern2; - hb_font_get_glyph_kerning_for_direction (c->font, - c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint, - c->buffer->props.direction, - &x_kern, &y_kern); - - kern1 = x_kern >> 1; - kern2 = x_kern - kern1; - c->buffer->pos[i - 1].x_advance += kern1; - c->buffer->pos[i].x_advance += kern2; - c->buffer->pos[i].x_offset += kern2; - - kern1 = y_kern >> 1; - kern2 = y_kern - kern1; - c->buffer->pos[i - 1].y_advance += kern1; - c->buffer->pos[i].y_advance += kern2; - c->buffer->pos[i].y_offset += kern2; + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + //case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + break; } + + return ret; } -static void -hb_position_complex_fallback_visual (hb_ot_shape_context_t *c) +static inline void +hb_ot_position (hb_ot_shape_context_t *c) { - hb_truetype_kern (c); + hb_ot_layout_position_start (c->font, c->buffer); + + hb_ot_position_default (c); + + hb_bool_t fallback = !hb_ot_position_complex (c); + + hb_ot_layout_position_finish (c->font, c->buffer); + + if (fallback && 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 (fallback) + _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer); + + _hb_buffer_deallocate_gsubgpos_vars (c->buffer); } + +/* Post-process */ + static void -hb_hide_zerowidth (hb_ot_shape_context_t *c) +hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) { - /* TODO Save the space character in the font? */ + if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) + return; + hb_codepoint_t space; - if (!hb_font_get_glyph (c->font, ' ', 0, &space)) - return; /* No point! */ + enum { + SPACE_DONT_KNOW, + SPACE_AVAILABLE, + SPACE_UNAVAILABLE + } space_status = SPACE_DONT_KNOW; unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + unsigned int j = 0; for (unsigned int i = 0; i < count; i++) - if (unlikely (_hb_glyph_info_is_zero_width (&c->buffer->info[i]))) { - c->buffer->info[i].codepoint = space; - c->buffer->pos[i].x_advance = 0; - c->buffer->pos[i].y_advance = 0; + { + if (unlikely (!_hb_glyph_info_ligated (&info[i]) && + _hb_glyph_info_is_default_ignorable (&info[i]))) + { + if (space_status == SPACE_DONT_KNOW) + space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE; + + if (space_status == SPACE_AVAILABLE) + { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + else + continue; /* Delete it. XXX Merge clusters? */ + } + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; } + j++; + } + c->buffer->len = j; } -/* Do it! */ +/* Pull it all together! */ static void -hb_ot_shape_execute_internal (hb_ot_shape_context_t *c) +hb_ot_shape_internal (hb_ot_shape_context_t *c) { c->buffer->deallocate_var_all (); /* Save the original direction, we use it later. */ c->target_direction = c->buffer->props.direction; - HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0); - HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1); + _hb_buffer_allocate_unicode_vars (c->buffer); - hb_set_unicode_props (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); - _hb_ot_shape_normalize (c->font, c->buffer, hb_ot_shape_complex_normalization_preference (c->plan->shaper)); - - hb_ot_shape_setup_masks (c); - - /* SUBSTITUTE */ - { - hb_substitute_default (c); - - hb_ot_substitute_complex (c); - } - - /* POSITION */ - { - hb_position_default (c); - - hb_ot_position_complex (c); + hb_ot_substitute (c); + hb_ot_position (c); - hb_bool_t position_fallback = !c->applied_position_complex; - if (position_fallback) - hb_position_complex_fallback (c); + hb_ot_hide_default_ignorables (c); - if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) - hb_buffer_reverse (c->buffer); - - if (position_fallback) - hb_position_complex_fallback_visual (c); - } - - hb_hide_zerowidth (c); - - HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1); - HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0); + _hb_buffer_deallocate_unicode_vars (c->buffer); c->buffer->props.direction = c->target_direction; c->buffer->deallocate_var_all (); } -static void -hb_ot_shape_plan_internal (hb_ot_shape_plan_t *plan, - hb_face_t *face, - const hb_segment_properties_t *props, - const hb_feature_t *user_features, - unsigned int num_user_features) -{ - hb_ot_shape_planner_t planner; - - assert (HB_DIRECTION_IS_VALID (props->direction)); - - planner.shaper = hb_ot_shape_complex_categorize (props); - - hb_ot_shape_collect_features (&planner, props, user_features, num_user_features); - - planner.compile (face, props, *plan); -} - -static void -hb_ot_shape_execute (hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *user_features, - unsigned int num_user_features) -{ - hb_ot_shape_context_t c = {plan, font, font->face, buffer, user_features, num_user_features}; - hb_ot_shape_execute_internal (&c); -} hb_bool_t -_hb_ot_shape (hb_font_t *font, +_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_plan_t plan; + hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; + hb_ot_shape_internal (&c); - buffer->guess_properties (); + return true; +} - hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); - hb_ot_shape_execute (&plan, font, buffer, features, num_features); - return true; +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_glyph (u, 0, &glyph)) + glyphs->add (glyph); + if (mirror) + { + hb_codepoint_t m = unicode->mirroring (u); + if (m != u && font->get_glyph (m, 0, &glyph)) + glyphs->add (glyph); + } } @@ -503,26 +775,29 @@ hb_ot_shape_glyphs_closure (hb_font_t *font, { hb_ot_shape_plan_t plan; - buffer->guess_properties (); - - hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); + const char *shapers[] = {"ot", NULL}; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, + features, num_features, shapers); - /* TODO: normalization? have shapers do closure()? */ - /* TODO: Deal with mirrored chars? */ - hb_map_glyphs (font, buffer); + bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; - /* Seed it. It's user's responsibility to have cleard glyphs - * if that's what they desire. */ unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - hb_set_add (glyphs, buffer->info[i].codepoint); + 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); - plan.map.substitute_closure (font->face, glyphs); - } while (!copy.equal (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/src/hb-ot-shape.h b/src/hb-ot-shape.h new file mode 100644 index 0000000..1402f54 --- /dev/null +++ b/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. */ +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); + +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/src/hb-ot-tag.cc b/src/hb-ot-tag.cc index ac60e96..878dd79 100644 --- a/src/hb-ot-tag.cc +++ b/src/hb-ot-tag.cc @@ -23,11 +23,10 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ #include "hb-private.hh" -#include "hb-ot.h" #include <string.h> @@ -38,6 +37,8 @@ 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; @@ -56,7 +57,7 @@ hb_ot_old_tag_from_script (hb_script_t script) } /* Else, just change first char to lowercase and return */ - return ((hb_tag_t) script) | 0x20000000; + return ((hb_tag_t) script) | 0x20000000u; } static hb_script_t @@ -69,13 +70,13 @@ hb_ot_old_tag_to_script (hb_tag_t tag) /* Any spaces at the end of the tag are replaced by repeating the last * letter. Eg 'nko ' -> 'Nkoo' */ - if (unlikely ((tag & 0x0000FF00) == 0x00002000)) - tag |= (tag >> 8) & 0x0000FF00; /* Copy second letter to third */ - if (unlikely ((tag & 0x000000FF) == 0x00000020)) - tag |= (tag >> 8) & 0x000000FF; /* Copy third letter to fourth */ + 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 & ~0x20000000); + return (hb_script_t) (tag & ~0x20000000u); } static hb_tag_t @@ -91,6 +92,7 @@ hb_ot_new_tag_from_script (hb_script_t script) 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; @@ -109,6 +111,7 @@ hb_ot_new_tag_to_script (hb_tag_t tag) 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; @@ -143,7 +146,7 @@ hb_ot_tags_from_script (hb_script_t script, hb_script_t hb_ot_tag_to_script (hb_tag_t tag) { - if (unlikely ((tag & 0x000000FF) == '2')) + if (unlikely ((tag & 0x000000FFu) == '2')) return hb_ot_new_tag_to_script (tag); return hb_ot_old_tag_to_script (tag); @@ -153,7 +156,7 @@ hb_ot_tag_to_script (hb_tag_t tag) /* hb_language_t */ typedef struct { - char language[6]; + char language[4]; hb_tag_t tag; } LangTag; @@ -163,9 +166,14 @@ typedef struct { * * 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. + * 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. * - * Many items still missing. Those are commented out at the end. + * Some items still missing. Those are commented out at the end. * Keep sorted for bsearch. */ @@ -173,46 +181,102 @@ 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 */ + {"ach", HB_TAG('A','C','H',' ')}, /* Acoli */ + {"ada", HB_TAG('D','N','G',' ')}, /* Dangme */ {"ady", HB_TAG('A','D','Y',' ')}, /* Adyghe */ {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {"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] */ + {"alt", HB_TAG('A','L','T',' ')}, /* [Southern] Altai */ {"am", HB_TAG('A','M','H',' ')}, /* Amharic */ - {"ar", HB_TAG('A','R','A',' ')}, /* Arabic */ + {"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] */ + {"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 */ - {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani */ + {"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 */ - {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi */ + {"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 */ + {"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 */ + {"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 */ - {"bik", HB_TAG('B','I','K',' ')}, /* Bikol */ + {"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 */ + {"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 */ + {"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 */ {"cr", HB_TAG('C','R','E',' ')}, /* Cree */ {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {"crj", HB_TAG('E','C','R',' ')}, /* [Southern] East 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 */ {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */ {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */ @@ -221,123 +285,210 @@ static const LangTag ot_languages[] = { {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) */ {"dar", HB_TAG('D','A','R',' ')}, /* Dargwa */ {"de", HB_TAG('D','E','U',' ')}, /* German */ - {"din", HB_TAG('D','N','K',' ')}, /* Dinka */ + {"dgo", HB_TAG('D','G','O',' ')}, /* Dogri */ + {"dhd", HB_TAG('M','A','W',' ')}, /* Dhundari */ + {"din", HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {"diq", HB_TAG('D','I','Q',' ')}, /* Dimli */ + {"dje", HB_TAG('D','J','R',' ')}, /* Zarma */ {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */ - {"doi", HB_TAG('D','G','R',' ')}, /* Dogri */ + {"doi", HB_TAG('D','G','R',' ')}, /* Dogri [macrolanguage] */ {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ - {"dv", HB_TAG('D','I','V',' ')}, /* Dhivehi */ + {"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 */ {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */ {"eot", HB_TAG('B','T','I',' ')}, /* Beti (Côte d'Ivoire) */ {"es", HB_TAG('E','S','P',' ')}, /* Spanish */ - {"et", HB_TAG('E','T','I',' ')}, /* Estonian */ + {"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 */ - {"ff", HB_TAG('F','U','L',' ')}, /* Fulah */ + {"fa", HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {"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 */ {"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 */ {"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 */ {"gl", HB_TAG('G','A','L',' ')}, /* Galician */ {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */ - {"gn", HB_TAG('G','U','A',' ')}, /* Guarani */ - {"gon", HB_TAG('G','O','N',' ')}, /* Gondi */ + {"glk", HB_TAG('G','L','K',' ')}, /* Gilaki */ + {"gn", HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ + {"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 */ {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */ + {"guc", HB_TAG('G','U','C',' ')}, /* Wayuu */ {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */ - {"gv", HB_TAG('M','N','X',' ')}, /* Manx Gaelic */ +/*{"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 */ + {"hz", HB_TAG('H','E','R',' ')}, /* Herero */ {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */ {"hil", HB_TAG('H','I','L',' ')}, /* Hiligaynon */ + {"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 */ + {"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) */ + {"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 */ + {"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 */ + {"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',' ')}, /* Kabyle */ {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */ {"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 */ + {"kex", HB_TAG('K','K','N',' ')}, /* Kokni */ {"kfr", HB_TAG('K','A','C',' ')}, /* Kachchi */ {"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',' ')}, /* Kikuyu */ + {"ki", HB_TAG('K','I','K',' ')}, /* Gikuyu/Kikuyu */ + {"kj", HB_TAG('K','U','A',' ')}, /* Kuanyama/Kwanyama */ + {"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 */ - {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle */ + {"kok", HB_TAG('K','O','K',' ')}, /* Konkani [macrolanguage] */ + {"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 */ + {"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 */ - {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish */ + {"ksh", HB_TAG('K','S','H',' ')}, /* 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) */ + {"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 */ + {"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 */ + {"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) */ - {"luw", HB_TAG('L','U','O',' ')}, /* Luo (Cameroon) */ + {"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 */ + {"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) */ - {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy */ + {"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 */ - {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian */ + {"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 */ @@ -345,57 +496,119 @@ static const LangTag ot_languages[] = { {"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 */ - {"ms", HB_TAG('M','L','Y',' ')}, /* Malay */ + {"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 */ - {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari */ + {"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 */ + {"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 */ {"niu", HB_TAG('N','I','U',' ')}, /* Niuean */ {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */ {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */ {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk */ - {"no", HB_TAG('N','O','R',' ')}, /* Norwegian (deprecated) */ + {"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 */ - {"ny", HB_TAG('C','H','I',' ')}, /* Nyanja */ + {"nso", HB_TAG('S','O','T',' ')}, /* [Northern] Sotho */ + {"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 */ - {"om", HB_TAG('O','R','O',' ')}, /* Oromo */ + {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] */ + {"ojs", HB_TAG('O','C','R',' ')}, /* Oji-Cree */ + {"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',' ')}, /* Papiamento */ + {"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 */ - {"prs", HB_TAG('D','R','I',' ')}, /* Dari */ - {"ps", HB_TAG('P','A','S',' ')}, /* Pushto */ + {"pms", HB_TAG('P','M','S',' ')}, /* Piemontese */ + {"pnb", HB_TAG('P','N','B',' ')}, /* Western Panjabi */ + {"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 */ - {"raj", HB_TAG('R','A','J',' ')}, /* Rajasthani */ + {"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é */ + {"quz", HB_TAG('Q','U','Z',' ')}, /* Cusco Quechua */ + {"raj", HB_TAG('R','A','J',' ')}, /* Rajasthani [macrolanguage] */ + {"rbb", HB_TAG('P','L','G',' ')}, /* Rumai Palaung */ + {"rej", HB_TAG('R','E','J',' ')}, /* Rejang */ {"ria", HB_TAG('R','I','A',' ')}, /* Riang (India) */ {"ril", HB_TAG('R','I','A',' ')}, /* Riang (Myanmar) */ + {"rki", HB_TAG('A','R','K',' ')}, /* Rakhine */ + {"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 */ + {"rom", HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ {"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 */ + {"sas", HB_TAG('S','A','S',' ')}, /* Sasak */ {"sat", HB_TAG('S','A','T',' ')}, /* Santali */ {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */ + {"sc", HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {"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) */ {"shn", HB_TAG('S','H','N',' ')}, /* Shan */ {"si", HB_TAG('S','N','H',' ')}, /* Sinhala */ {"sid", HB_TAG('S','I','D',' ')}, /* Sidamo */ @@ -408,174 +621,160 @@ static const LangTag ot_languages[] = { {"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',' ')}, /* Shona */ {"snk", HB_TAG('S','N','K',' ')}, /* Soninke */ {"so", HB_TAG('S','M','L',' ')}, /* Somali */ - {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian */ + {"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 */ + {"sw", HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ {"swb", HB_TAG('C','M','R',' ')}, /* Comorian */ - {"syr", HB_TAG('S','Y','R',' ')}, /* Syriac */ + {"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 [macrolanguage] */ {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */ - {"tnz", HB_TAG('T','N','G',' ')}, /* Tonga (Thailand) */ - {"to", HB_TAG('T','N','G',' ')}, /* Tonga (Tonga Islands) */ - {"tog", HB_TAG('T','N','G',' ')}, /* Tonga (Nyasa) */ - {"toi", HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ + {"to", HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) */ + {"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 */ {"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 */ {"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 */ + {"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 */ + {"vls", HB_TAG('F','L','E',' ')}, /* Vlaams */ {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {"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 */ + {"wry", HB_TAG('M','A','W',' ')}, /* Merwari */ + {"wtm", HB_TAG('W','T','M',' ')}, /* Mewati */ {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */ {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */ {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */ + {"xog", HB_TAG('X','O','G',' ')}, /* Soga */ {"xom", HB_TAG('K','M','O',' ')}, /* Komo (Sudan) */ {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */ - {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish */ + {"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 */ + {"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 */ {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */ - {"zu", HB_TAG('Z','U','L',' ')} /* Zulu */ - - /* I couldn't find the language id for these */ - -/*{"??", HB_TAG('A','G','W',' ')},*/ /* Agaw */ -/*{"??", HB_TAG('A','L','S',' ')},*/ /* Alsatian */ -/*{"??", HB_TAG('A','L','T',' ')},*/ /* Altai */ -/*{"??", HB_TAG('A','R','K',' ')},*/ /* Arakanese */ -/*{"??", HB_TAG('A','T','H',' ')},*/ /* Athapaskan */ -/*{"??", HB_TAG('B','A','G',' ')},*/ /* Baghelkhandi */ -/*{"??", HB_TAG('B','A','L',' ')},*/ /* Balkar */ -/*{"??", HB_TAG('B','A','U',' ')},*/ /* Baule */ -/*{"??", HB_TAG('B','B','R',' ')},*/ /* Berber */ + {"zu", HB_TAG('Z','U','L',' ')}, /* Zulu */ + {"zum", HB_TAG('L','R','C',' ')} /* Kumzari */ + + /* The corresponding languages IDs for the following IDs are unclear, + * overlap, or are architecturally weird. Needs more research. */ + +/*{"ahg/awn/xan?", HB_TAG('A','G','W',' ')},*/ /* Agaw */ +/*{"gsw?/gsw-FR?", HB_TAG('A','L','S',' ')},*/ /* Alsatian */ +/*{"krc", HB_TAG('B','A','L',' ')},*/ /* Balkar */ /*{"??", HB_TAG('B','C','R',' ')},*/ /* Bible Cree */ -/*{"??", HB_TAG('B','E','L',' ')},*/ /* Belarussian */ -/*{"??", HB_TAG('B','I','L',' ')},*/ /* Bilen */ -/*{"??", HB_TAG('B','K','F',' ')},*/ /* Blackfoot */ -/*{"??", HB_TAG('B','L','N',' ')},*/ /* Balante */ -/*{"??", HB_TAG('B','M','L',' ')},*/ /* Bamileke */ -/*{"??", HB_TAG('B','R','I',' ')},*/ /* Braj Bhasha */ -/*{"??", HB_TAG('C','H','G',' ')},*/ /* Chaha Gurage */ -/*{"??", HB_TAG('C','H','H',' ')},*/ /* Chattisgarhi */ -/*{"??", HB_TAG('C','H','K',' ')},*/ /* Chukchi */ -/*{"??", HB_TAG('D','J','R',' ')},*/ /* Djerma */ -/*{"??", HB_TAG('D','N','G',' ')},*/ /* Dangme */ -/*{"??", HB_TAG('E','C','R',' ')},*/ /* Eastern Cree */ -/*{"??", HB_TAG('F','A','N',' ')},*/ /* French Antillean */ -/*{"??", HB_TAG('F','L','E',' ')},*/ /* Flemish */ -/*{"??", HB_TAG('F','N','E',' ')},*/ /* Forest Nenets */ -/*{"??", HB_TAG('F','T','A',' ')},*/ /* Futa */ -/*{"??", HB_TAG('G','A','R',' ')},*/ /* Garshuni */ -/*{"??", HB_TAG('G','E','Z',' ')},*/ /* Ge'ez */ -/*{"??", HB_TAG('H','A','L',' ')},*/ /* Halam */ -/*{"??", HB_TAG('H','A','R',' ')},*/ /* Harauti */ -/*{"??", HB_TAG('H','A','W',' ')},*/ /* Hawaiin */ -/*{"??", HB_TAG('H','B','N',' ')},*/ /* Hammer-Banna */ -/*{"??", HB_TAG('H','M','A',' ')},*/ /* High Mari */ -/*{"??", HB_TAG('H','N','D',' ')},*/ /* Hindko */ -/*{"??", HB_TAG('I','J','O',' ')},*/ /* Ijo */ -/*{"??", HB_TAG('I','L','O',' ')},*/ /* Ilokano */ -/*{"??", HB_TAG('I','R','T',' ')},*/ /* Irish Traditional */ -/*{"??", HB_TAG('J','U','L',' ')},*/ /* Jula */ -/*{"??", HB_TAG('K','A','R',' ')},*/ /* Karachay */ -/*{"??", HB_TAG('K','E','B',' ')},*/ /* Kebena */ -/*{"??", HB_TAG('K','G','E',' ')},*/ /* Khutsuri Georgian */ -/*{"??", HB_TAG('K','H','A',' ')},*/ /* Khakass */ -/*{"??", HB_TAG('K','H','K',' ')},*/ /* Khanty-Kazim */ -/*{"??", HB_TAG('K','H','S',' ')},*/ /* Khanty-Shurishkar */ -/*{"??", HB_TAG('K','H','V',' ')},*/ /* Khanty-Vakhi */ -/*{"??", HB_TAG('K','I','S',' ')},*/ /* Kisii */ -/*{"??", HB_TAG('K','K','N',' ')},*/ /* Kokni */ -/*{"??", HB_TAG('K','M','S',' ')},*/ /* Komso */ -/*{"??", HB_TAG('K','O','D',' ')},*/ /* Kodagu */ -/*{"??", HB_TAG('K','O','H',' ')},*/ /* Korean Old Hangul */ -/*{"??", HB_TAG('K','O','N',' ')},*/ /* Kikongo */ -/*{"??", HB_TAG('K','R','K',' ')},*/ /* Karakalpak */ -/*{"??", HB_TAG('K','R','N',' ')},*/ /* Karen */ -/*{"??", HB_TAG('K','U','L',' ')},*/ /* Kulvi */ +/*{"zh?", HB_TAG('C','H','N',' ')},*/ /* Chinese (seen in Microsoft fonts) */ +/*{"acf/gcf?", HB_TAG('F','A','N',' ')},*/ /* French Antillean */ +/*{"enf?/yrk?", HB_TAG('F','N','E',' ')},*/ /* Forest Nenets */ +/*{"fuf?", HB_TAG('F','T','A',' ')},*/ /* Futa */ +/*{"ar-Syrc?", HB_TAG('G','A','R',' ')},*/ /* Garshuni */ +/*{"cfm/rnl?", HB_TAG('H','A','L',' ')},*/ /* Halam */ +/*{"fonipa", HB_TAG('I','P','P','H')},*/ /* Phonetic transcription—IPA conventions */ +/*{"ga-Latg?/Latg?", HB_TAG('I','R','T',' ')},*/ /* Irish Traditional */ +/*{"krc", HB_TAG('K','A','R',' ')},*/ /* Karachay */ +/*{"alw?/ktb?", HB_TAG('K','E','B',' ')},*/ /* Kebena */ +/*{"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 */ +/*{"guz?/kqs?/kss?", HB_TAG('K','I','S',' ')},*/ /* Kisii */ +/*{"kfa/kfi?/kpb?/xua?/xuj?", HB_TAG('K','O','D',' ')},*/ /* Kodagu */ +/*{"okm?/oko?", HB_TAG('K','O','H',' ')},*/ /* Korean Old Hangul */ +/*{"kon?/ktu?/...", HB_TAG('K','O','N',' ')},*/ /* Kikongo */ +/*{"kfx?", HB_TAG('K','U','L',' ')},*/ /* Kulvi */ /*{"??", HB_TAG('L','A','H',' ')},*/ /* Lahuli */ -/*{"??", HB_TAG('L','A','M',' ')},*/ /* Lambani */ /*{"??", HB_TAG('L','C','R',' ')},*/ /* L-Cree */ -/*{"??", HB_TAG('L','E','Z',' ')},*/ /* Lezgi */ -/*{"??", HB_TAG('L','M','A',' ')},*/ /* Low Mari */ -/*{"??", HB_TAG('L','U','B',' ')},*/ /* Luba */ -/*{"??", HB_TAG('L','U','G',' ')},*/ /* Luganda */ -/*{"??", HB_TAG('L','U','H',' ')},*/ /* Luhya */ -/*{"??", HB_TAG('M','A','K',' ')},*/ /* Makua */ /*{"??", HB_TAG('M','A','L',' ')},*/ /* Malayalam Traditional */ -/*{"??", HB_TAG('M','B','N',' ')},*/ /* Mbundu */ -/*{"??", HB_TAG('M','I','Z',' ')},*/ /* Mizo */ -/*{"??", HB_TAG('M','L','N',' ')},*/ /* Malinke */ -/*{"??", HB_TAG('M','N','K',' ')},*/ /* Maninka */ -/*{"??", HB_TAG('M','O','R',' ')},*/ /* Moroccan */ -/*{"??", HB_TAG('N','A','G',' ')},*/ /* Naga-Assamese */ +/*{"mnk?/mlq?/...", HB_TAG('M','L','N',' ')},*/ /* Malinke */ /*{"??", HB_TAG('N','C','R',' ')},*/ /* N-Cree */ -/*{"??", HB_TAG('N','D','B',' ')},*/ /* Ndebele */ -/*{"??", HB_TAG('N','G','R',' ')},*/ /* Nagari */ /*{"??", HB_TAG('N','H','C',' ')},*/ /* Norway House Cree */ -/*{"??", HB_TAG('N','K','L',' ')},*/ /* Nkole */ -/*{"??", HB_TAG('N','T','A',' ')},*/ /* Northern Tai */ -/*{"??", HB_TAG('O','C','R',' ')},*/ /* Oji-Cree */ -/*{"??", HB_TAG('P','A','A',' ')},*/ /* Palestinian Aramaic */ -/*{"??", HB_TAG('P','G','R',' ')},*/ /* Polytonic Greek */ -/*{"??", HB_TAG('P','L','G',' ')},*/ /* Palaung */ -/*{"??", HB_TAG('Q','I','N',' ')},*/ /* Chin */ -/*{"??", HB_TAG('R','B','U',' ')},*/ /* Russian Buriat */ +/*{"jpa?/sam?", HB_TAG('P','A','A',' ')},*/ /* Palestinian Aramaic */ +/*{"polyton", HB_TAG('P','G','R',' ')},*/ /* Polytonic Greek */ +/*{"??", HB_TAG('Q','I','N',' ')},*/ /* Asho Chin */ /*{"??", HB_TAG('R','C','R',' ')},*/ /* R-Cree */ -/*{"??", HB_TAG('R','M','S',' ')},*/ /* Rhaeto-Romanic */ -/*{"??", HB_TAG('R','U','A',' ')},*/ /* Ruanda */ -/*{"??", HB_TAG('S','A','Y',' ')},*/ /* Sayisi */ -/*{"??", HB_TAG('S','E','K',' ')},*/ /* Sekota */ -/*{"??", HB_TAG('S','I','G',' ')},*/ /* Silte Gurage */ -/*{"??", HB_TAG('S','L','A',' ')},*/ /* Slavey */ -/*{"??", HB_TAG('S','O','G',' ')},*/ /* Sodo Gurage */ -/*{"??", HB_TAG('S','O','T',' ')},*/ /* Sotho */ -/*{"??", HB_TAG('S','W','A',' ')},*/ /* Swadaya Aramaic */ -/*{"??", HB_TAG('S','W','Z',' ')},*/ /* Swazi */ -/*{"??", HB_TAG('S','X','T',' ')},*/ /* Sutu */ -/*{"??", HB_TAG('T','A','B',' ')},*/ /* Tabasaran */ +/*{"chp?", HB_TAG('S','A','Y',' ')},*/ /* Sayisi */ +/*{"xan?", HB_TAG('S','E','K',' ')},*/ /* Sekota */ +/*{"ngo?", HB_TAG('S','X','T',' ')},*/ /* Sutu */ /*{"??", HB_TAG('T','C','R',' ')},*/ /* TH-Cree */ -/*{"??", HB_TAG('T','G','N',' ')},*/ /* Tongan */ -/*{"??", HB_TAG('T','M','N',' ')},*/ /* Temne */ -/*{"??", HB_TAG('T','N','E',' ')},*/ /* Tundra Nenets */ -/*{"??", HB_TAG('T','O','D',' ')},*/ /* Todo */ -/*{"??", HB_TAG('T','U','A',' ')},*/ /* Turoyo Aramaic */ -/*{"??", HB_TAG('T','U','V',' ')},*/ /* Tuvin */ +/*{"tnz?/tog?/toi?", HB_TAG('T','N','G',' ')},*/ /* Tonga */ +/*{"enh?/yrk?", HB_TAG('T','N','E',' ')},*/ /* Tundra Nenets */ /*{"??", HB_TAG('W','C','R',' ')},*/ /* West-Cree */ -/*{"??", HB_TAG('X','B','D',' ')},*/ /* Tai Lue */ -/*{"??", HB_TAG('Y','C','R',' ')},*/ /* Y-Cree */ +/*{"cre?", HB_TAG('Y','C','R',' ')},*/ /* Y-Cree */ /*{"??", HB_TAG('Y','I','C',' ')},*/ /* Yi Classic */ -/*{"??", HB_TAG('Y','I','M',' ')},*/ /* Yi Modern */ +/*{"ii?/Yiii?", HB_TAG('Y','I','M',' ')},*/ /* Yi Modern */ /*{"??", HB_TAG('Z','H','P',' ')},*/ /* Chinese Phonetic */ }; -static const LangTag ot_languages_zh[] = { +typedef struct { + char language[8]; + hb_tag_t tag; +} LangTagLong; +static const LangTagLong ot_languages_zh[] = { {"zh-cn", HB_TAG('Z','H','S',' ')}, /* Chinese (China) */ {"zh-hk", HB_TAG('Z','H','H',' ')}, /* Chinese (Hong Kong) */ {"zh-mo", HB_TAG('Z','H','T',' ')}, /* Chinese (Macao) */ {"zh-sg", HB_TAG('Z','H','S',' ')}, /* Chinese (Singapore) */ - {"zh-tw", HB_TAG('Z','H','T',' ')} /* Chinese (Taiwan) */ + {"zh-tw", HB_TAG('Z','H','T',' ')}, /* Chinese (Taiwan) */ + {"zh-hans", HB_TAG('Z','H','S',' ')}, /* Chinese (Simplified) */ + {"zh-hant", HB_TAG('Z','H','T',' ')}, /* Chinese (Traditional) */ }; static int @@ -607,7 +806,6 @@ hb_tag_t hb_ot_tag_from_language (hb_language_t language) { const char *lang_str, *s; - const LangTag *lang_tag; if (language == HB_LANGUAGE_INVALID) return HB_OT_TAG_DEFAULT_LANGUAGE; @@ -629,11 +827,14 @@ hb_ot_tag_from_language (hb_language_t language) } /* Find a language matching in the first component */ - 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; + { + 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")) @@ -642,8 +843,9 @@ hb_ot_tag_from_language (hb_language_t language) for (i = 0; i < ARRAY_LENGTH (ot_languages_zh); i++) { + const LangTagLong *lang_tag; lang_tag = &ot_languages_zh[i]; - if (lang_matches (lang_tag->language, lang_str)) + if (lang_matches (lang_str, lang_tag->language)) return lang_tag->tag; } @@ -656,7 +858,7 @@ hb_ot_tag_from_language (hb_language_t language) s = lang_str + strlen (lang_str); if (s - lang_str == 3) { /* Assume it's ISO-639-3 and upper-case and use it. */ - return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000; + return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u; } return HB_OT_TAG_DEFAULT_LANGUAGE; @@ -675,21 +877,12 @@ hb_ot_tag_to_language (hb_tag_t tag) return hb_language_from_string (ot_languages[i].language, -1); /* If tag starts with ZH, it's Chinese */ - if ((tag & 0xFFFF0000) == 0x5A480000) { + if ((tag & 0xFFFF0000u) == 0x5A480000u) { switch (tag) { case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */ - default: { - /* Encode the tag... */ - unsigned char buf[14] = "zh-x-hbot"; - buf[9] = tag >> 24; - buf[10] = (tag >> 16) & 0xFF; - buf[11] = (tag >> 8) & 0xFF; - buf[12] = tag & 0xFF; - if (buf[12] == 0x20) - buf[12] = '\0'; - buf[13] = '\0'; - return hb_language_from_string ((char *) buf, -1); - } + 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 */ } } diff --git a/src/hb-ot.h b/src/hb-ot.h index 2d750c3..47c92a5 100644 --- a/src/hb-ot.h +++ b/src/hb-ot.h @@ -30,18 +30,13 @@ #include "hb.h" +#include "hb-ot-font.h" #include "hb-ot-layout.h" #include "hb-ot-tag.h" +#include "hb-ot-shape.h" HB_BEGIN_DECLS -void -hb_ot_shape_glyphs_closure (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features, - hb_set_t *glyphs); - HB_END_DECLS #undef HB_OT_H_IN diff --git a/src/hb-private.hh b/src/hb-private.hh index 0cb049e..6505238 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -54,31 +54,140 @@ #include <stdarg.h> +/* Compiler attributes */ -/* Essentials */ -#ifndef NULL -# define NULL ((void *) 0) +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0) +#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1)) +#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +#ifndef __GNUC__ +#undef __attribute__ +#define __attribute__(x) +#endif + +#if __GNUC__ >= 3 +#define HB_PURE_FUNC __attribute__((pure)) +#define HB_CONST_FUNC __attribute__((const)) +#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 + +#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 getenv(Name) NULL +# define setlocale(Category, Locale) "C" +static int errno = 0; /* Use something better? */ +# 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 +# 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; } +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; } +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 -#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) +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) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1] +#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)) @@ -125,7 +234,7 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4); /* Check _assertion in a method environment */ #define _ASSERT_POD1(_line) \ - inline void _static_assertion_on_line_##_line (void) const \ + 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__) @@ -134,68 +243,10 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4); /* Misc */ - -#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0) -#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1)) -#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0)) -#else -#define likely(expr) (expr) -#define unlikely(expr) (expr) -#endif - -#ifndef __GNUC__ -#undef __attribute__ -#define __attribute__(x) -#endif - -#if __GNUC__ >= 3 -#define HB_PURE_FUNC __attribute__((pure)) -#define HB_CONST_FUNC __attribute__((const)) -#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 -# ifndef __MINGW32__ -# define HB_INTERNAL __attribute__((__visibility__("hidden"))) -# else -# define HB_INTERNAL -# endif -#endif - - -#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) -#define snprintf _snprintf -#endif - -#ifdef _MSC_VER -#undef inline -#define inline __inline -#endif - -#ifdef __STRICT_ANSI__ -#undef inline -#define inline __inline__ -#endif - - -#if __GNUC__ >= 3 -#define HB_FUNC __PRETTY_FUNCTION__ -#elif defined(_MSC_VER) -#define HB_FUNC __FUNCSIG__ -#else -#define HB_FUNC __func__ -#endif - +/* 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 @@ -205,7 +256,7 @@ _hb_popcount32 (uint32_t mask) return __builtin_popcount (mask); #else /* "HACKMEM 169" */ - register uint32_t y; + uint32_t y; y = (mask >> 1) &033333333333; y = mask - y - ((y >>1) & 033333333333); return (((y + (y >> 3)) & 030707070707) % 077); @@ -219,7 +270,7 @@ _hb_bit_storage (unsigned int number) #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0; #else - register unsigned int n_bits = 0; + unsigned int n_bits = 0; while (number) { n_bits++; number >>= 1; @@ -235,7 +286,7 @@ _hb_ctz (unsigned int number) #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) return likely (number) ? __builtin_ctz (number) : 0; #else - register unsigned int n_bits = 0; + unsigned int n_bits = 0; if (unlikely (!number)) return 0; while (!(number & 1)) { n_bits++; @@ -261,8 +312,8 @@ typedef int (*hb_compare_func_t) (const void *, const void *); /* arrays and maps */ -#define HB_PREALLOCED_ARRAY_INIT {0} -template <typename Type, unsigned int StaticSize> +#define HB_PREALLOCED_ARRAY_INIT {0, 0, NULL} +template <typename Type, unsigned int StaticSize=16> struct hb_prealloced_array_t { unsigned int len; @@ -310,14 +361,22 @@ struct hb_prealloced_array_t inline void pop (void) { len--; - /* TODO: shrink array if needed */ + } + + 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; - /* TODO: shrink array if needed */ } template <typename T> @@ -335,14 +394,14 @@ struct hb_prealloced_array_t return NULL; } - inline void sort (void) + inline void qsort (void) { - qsort (array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); + ::qsort (array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); } - inline void sort (unsigned int start, unsigned int end) + inline void qsort (unsigned int start, unsigned int end) { - qsort (array + start, end - start, sizeof (Type), (hb_compare_func_t) Type::cmp); + ::qsort (array + start, end - start, sizeof (Type), (hb_compare_func_t) Type::cmp); } template <typename T> @@ -365,6 +424,13 @@ struct hb_prealloced_array_t } }; +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> @@ -442,6 +508,11 @@ struct hb_lockable_set_t 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]; @@ -457,39 +528,14 @@ struct hb_lockable_set_t }; - - -/* Big-endian handling */ - -static inline uint16_t hb_be_uint16 (const uint16_t v) -{ - const uint8_t *V = (const uint8_t *) &v; - return (uint16_t) (V[0] << 8) + V[1]; -} - -/* Note, of the following macros, uint16_get is the one called many many times. - * If there is any optimizations to be done, it's in that macro. However, I - * already confirmed that on my T400 ThinkPad at least, using bswap_16(), which - * results in a single ror instruction, does NOT speed this up. In fact, it - * resulted in a minor slowdown. At any rate, note that v may not be correctly - * aligned, so I think the current implementation is optimal. - */ - -#define hb_be_uint16_put(v,V) HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END -#define hb_be_uint16_get(v) (uint16_t) ((v[0] << 8) + v[1]) -#define hb_be_uint16_eq(a,b) (a[0] == b[0] && a[1] == b[1]) - -#define hb_be_uint32_put(v,V) HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END -#define hb_be_uint32_get(v) (uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3]) -#define hb_be_uint32_eq(a,b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]) - - /* ASCII tag/character handling */ -static inline unsigned char ISALPHA (unsigned char c) +static inline bool ISALPHA (unsigned char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -static inline unsigned char ISALNUM (unsigned char c) +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) @@ -523,10 +569,43 @@ _hb_debug (unsigned int level, return level < max_level; } -#define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) -#define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0)) +#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> inline void +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, @@ -547,26 +626,28 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); if (indented) { - static const char bars[] = "││││││││││││││││││││││││││││││││││││││││"; - fprintf (stderr, "%2d %s├%s", +/* One may want to add ASCII version of these. See: + * https://bugs.freedesktop.org/show_bug.cgi?id=50970 */ +#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; + fprintf (stderr, "%2u %s" VRBAR "%s", level, - bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level), - level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴"); + bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), (unsigned int) (sizeof (VBAR) - 1) * level), + level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); } else - fprintf (stderr, " ├╴"); + fprintf (stderr, " " VRBAR LBAR); - if (func) { - /* If there's a class name, just write that. */ - const char *dotdot = strstr (func, "::"); - const char *space = strchr (func, ' '); - if (space && dotdot && space < dotdot) - func = space + 1; - unsigned int func_len = dotdot ? dotdot - func : strlen (func); - fprintf (stderr, "%.*s: ", func_len, func); - } + _hb_print_func (func); if (message) + { + fprintf (stderr, ": "); vfprintf (stderr, message, ap); + } fprintf (stderr, "\n"); } @@ -580,7 +661,7 @@ _hb_debug_msg_va<0> (const char *what HB_UNUSED, const char *message HB_UNUSED, va_list ap HB_UNUSED) {} -template <int max_level> inline void +template <int max_level> static inline void _hb_debug_msg (const char *what, const void *obj, const char *func, @@ -589,7 +670,7 @@ _hb_debug_msg (const char *what, int level_dir, const char *message, ...) HB_PRINTF_FUNC(7, 8); -template <int max_level> inline void +template <int max_level> static inline void _hb_debug_msg (const char *what, const void *obj, const char *func, @@ -629,10 +710,41 @@ _hb_debug_msg<0> (const char *what HB_UNUSED, /* + * 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 <int max_level> +template <typename T> +static inline void _hb_warn_no_return (bool returned) +{ + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN. 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_, @@ -650,23 +762,23 @@ struct hb_auto_trace_t { } inline ~hb_auto_trace_t (void) { - if (unlikely (!returned)) { - fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN. This is a bug, please report. Level was %d.\n", plevel ? *plevel : -1); + _hb_warn_no_return<ret_t> (returned); + if (!returned) { _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " "); - return; } - if (plevel) --*plevel; } - inline bool ret (bool v) + inline ret_t ret (ret_t v, unsigned int line = 0) { if (unlikely (returned)) { fprintf (stderr, "OUCH, double calls to TRACE_RETURN. This is a bug, please report.\n"); return v; } - _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false"); + _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; @@ -675,12 +787,12 @@ struct hb_auto_trace_t { private: unsigned int *plevel; - bool returned; const char *what; const void *obj; + bool returned; }; -template <> /* Optimize when tracing is disabled */ -struct hb_auto_trace_t<0> { +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, @@ -688,28 +800,44 @@ struct hb_auto_trace_t<0> { const char *message HB_UNUSED, ...) {} - template <typename T> - inline T ret (T v) { return v; } + inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } }; -#define TRACE_RETURN(RET) trace.ret (RET) +#define TRACE_RETURN(RET) 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> {}; -/* Pre-mature optimization: - * Checks for lo <= u <= hi but with an optimization if lo and hi - * are only different in a contiguous set of lower-most bits. - */ -template <typename T> inline bool +template <typename T> static inline bool hb_in_range (T u, T lo, T hi) { - if ( ((lo^hi) & lo) == 0 && - ((lo^hi) & hi) == (lo^hi) && - ((lo^hi) & ((lo^hi) + 1)) == 0 ) - return (u & ~(lo^hi)) == lo; - else - return lo <= u && u <= 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); } @@ -718,10 +846,11 @@ hb_in_range (T u, T lo, T hi) * (FLAG(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) */ #define FLAG(x) (1<<(x)) +#define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x)) -template <typename T> inline void -hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +template <typename T, typename T2> static inline void +hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) { if (unlikely (!len)) return; @@ -731,11 +860,21 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) unsigned int new_k = 0; for (unsigned int j = 0; j < k; j++) - if (compar (&array[j], &array[j+1]) > 0) { - T t; - t = array[j]; - array[j] = array[j + 1]; - array[j + 1] = t; + if (compar (&array[j], &array[j+1]) > 0) + { + { + T t; + t = array[j]; + array[j] = array[j + 1]; + array[j + 1] = t; + } + if (array2) + { + T2 t; + t = array2[j]; + array2[j] = array2[j + 1]; + array2[j + 1] = t; + } new_k = j; } @@ -743,7 +882,58 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) } while (k); } +template <typename T> static inline void +hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +{ + hb_bubble_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; +} #endif /* HB_PRIVATE_HH */ diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh index 5cdf8a0..acba4e9 100644 --- a/src/hb-set-private.hh +++ b/src/hb-set-private.hh @@ -28,27 +28,142 @@ #define HB_SET_PRIVATE_HH #include "hb-private.hh" -#include "hb-set.h" #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. As a result, our filters have much higher. + */ + +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 +struct hb_set_t { + friend struct hb_frozen_set_t; + hb_object_header_t header; ASSERT_POD (); + bool in_error; inline void init (void) { - header.init (); + 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 empty (void) const { + inline bool is_empty (void) const { for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++) if (elts[i]) return false; @@ -56,15 +171,31 @@ struct _hb_set_t } inline void add (hb_codepoint_t g) { - if (unlikely (g == SENTINEL)) return; + 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; @@ -81,7 +212,7 @@ struct _hb_set_t return true; return false; } - inline bool equal (const hb_set_t *other) const + inline bool is_equal (const hb_set_t *other) const { for (unsigned int i = 0; i < ELTS; i++) if (elts[i] != other->elts[i]) @@ -90,54 +221,93 @@ struct _hb_set_t } 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 bool next (hb_codepoint_t *codepoint) + 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 == SENTINEL)) { + if (unlikely (*codepoint == INVALID)) { hb_codepoint_t i = get_min (); - if (i != SENTINEL) { + if (i != INVALID) { *codepoint = i; return true; - } else + } 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; i < BITS; j++) + for (unsigned int j = 0; j < BITS; j++) if (elts[i] & (1 << j)) return i * BITS + j; - return SENTINEL; + return INVALID; } inline hb_codepoint_t get_max (void) const { @@ -146,7 +316,7 @@ struct _hb_set_t for (unsigned int j = BITS; j; j--) if (elts[i - 1] & (1 << (j - 1))) return (i - 1) * BITS + (j - 1); - return SENTINEL; + return INVALID; } typedef uint32_t elt_t; @@ -155,10 +325,10 @@ struct _hb_set_t static const unsigned int BITS = (1 << SHIFT); static const unsigned int MASK = BITS - 1; static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS; - static const hb_codepoint_t SENTINEL = (hb_codepoint_t) -1; + static const hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; } - elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; } + elt_t 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 */ @@ -167,6 +337,59 @@ struct _hb_set_t 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); + const elt_t &max_elt = set.elt (max); + + 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/src/hb-set.cc b/src/hb-set.cc index 4225e3c..59a0af4 100644 --- a/src/hb-set.cc +++ b/src/hb-set.cc @@ -27,12 +27,18 @@ #include "hb-set-private.hh" - /* Public API */ +/** + * hb_set_create: (Xconstructor) + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_set_t * -hb_set_create () +hb_set_create (void) { hb_set_t *set; @@ -44,11 +50,19 @@ hb_set_create () return set; } +/** + * hb_set_get_empty: + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ 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 */ }; @@ -56,12 +70,26 @@ hb_set_get_empty (void) return const_cast<hb_set_t *> (&_hb_set_nil); } +/** + * hb_set_reference: (skip) + * @set: a set. + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_set_t * hb_set_reference (hb_set_t *set) { return hb_object_reference (set); } +/** + * hb_set_destroy: (skip) + * @set: a set. + * + * Since: 1.0 + **/ void hb_set_destroy (hb_set_t *set) { @@ -72,49 +100,118 @@ hb_set_destroy (hb_set_t *set) free (set); } +/** + * hb_set_set_user_data: (skip) + * @set: a set. + * @key: + * @data: + * @destroy (closure data): + * @replace: + * + * Return value: + * + * Since: 1.0 + **/ 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_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: 1.0 + **/ void * -hb_set_get_user_data (hb_set_t *set, - hb_user_data_key_t *key) +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: 1.0 + **/ hb_bool_t -hb_set_allocation_successful (hb_set_t *set HB_UNUSED) +hb_set_allocation_successful (const hb_set_t *set HB_UNUSED) { - return true; + return !set->in_error; } +/** + * hb_set_clear: + * @set: a set. + * + * + * + * Since: 1.0 + **/ void hb_set_clear (hb_set_t *set) { set->clear (); } +/** + * hb_set_is_empty: + * @set: a set. + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t -hb_set_empty (hb_set_t *set) +hb_set_is_empty (const hb_set_t *set) { - return set->empty (); + return set->is_empty (); } +/** + * hb_set_has: + * @set: a set. + * @codepoint: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t -hb_set_has (hb_set_t *set, +hb_set_has (const hb_set_t *set, hb_codepoint_t codepoint) { return set->has (codepoint); } +/** + * hb_set_add: + * @set: a set. + * @codepoint: + * + * + * + * Since: 1.0 + **/ void hb_set_add (hb_set_t *set, hb_codepoint_t codepoint) @@ -122,6 +219,33 @@ hb_set_add (hb_set_t *set, set->add (codepoint); } +/** + * hb_set_add_range: + * @set: a set. + * @first: + * @last: + * + * + * + * Since: 1.0 + **/ +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: 1.0 + **/ void hb_set_del (hb_set_t *set, hb_codepoint_t codepoint) @@ -129,63 +253,219 @@ hb_set_del (hb_set_t *set, set->del (codepoint); } +/** + * hb_set_del_range: + * @set: a set. + * @first: + * @last: + * + * + * + * Since: 1.0 + **/ +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: 1.0 + **/ hb_bool_t -hb_set_equal (hb_set_t *set, - hb_set_t *other) +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other) { - return set->equal (other); + return set->is_equal (other); } +/** + * hb_set_set: + * @set: a set. + * @other: + * + * + * + * Since: 1.0 + **/ void -hb_set_set (hb_set_t *set, - hb_set_t *other) +hb_set_set (hb_set_t *set, + const hb_set_t *other) { set->set (other); } +/** + * hb_set_union: + * @set: a set. + * @other: + * + * + * + * Since: 1.0 + **/ void -hb_set_union (hb_set_t *set, - hb_set_t *other) +hb_set_union (hb_set_t *set, + const hb_set_t *other) { set->union_ (other); } +/** + * hb_set_intersect: + * @set: a set. + * @other: + * + * + * + * Since: 1.0 + **/ void -hb_set_intersect (hb_set_t *set, - hb_set_t *other) +hb_set_intersect (hb_set_t *set, + const hb_set_t *other) { set->intersect (other); } +/** + * hb_set_subtract: + * @set: a set. + * @other: + * + * + * + * Since: 1.0 + **/ void -hb_set_subtract (hb_set_t *set, - hb_set_t *other) +hb_set_subtract (hb_set_t *set, + const hb_set_t *other) { set->subtract (other); } +/** + * hb_set_symmetric_difference: + * @set: a set. + * @other: + * + * + * + * Since: 1.0 + **/ void -hb_set_symmetric_difference (hb_set_t *set, - hb_set_t *other) +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other) { set->symmetric_difference (other); } +/** + * hb_set_invert: + * @set: a set. + * + * + * + * Since: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ hb_codepoint_t -hb_set_min (hb_set_t *set) +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: 1.0 + **/ hb_codepoint_t -hb_set_max (hb_set_t *set) +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: 1.0 + **/ hb_bool_t -hb_set_next (hb_set_t *set, +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: 1.0 + **/ +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/src/hb-set.h b/src/hb-set.h index 9f849cf..bafdae9 100644 --- a/src/hb-set.h +++ b/src/hb-set.h @@ -36,7 +36,9 @@ HB_BEGIN_DECLS -typedef struct _hb_set_t hb_set_t; +#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1) + +typedef struct hb_set_t hb_set_t; hb_set_t * @@ -65,16 +67,16 @@ hb_set_get_user_data (hb_set_t *set, /* Returns false if allocation has failed before */ hb_bool_t -hb_set_allocation_successful (hb_set_t *set); +hb_set_allocation_successful (const hb_set_t *set); void hb_set_clear (hb_set_t *set); hb_bool_t -hb_set_empty (hb_set_t *set); +hb_set_is_empty (const hb_set_t *set); hb_bool_t -hb_set_has (hb_set_t *set, +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 @@ -84,47 +86,67 @@ hb_set_add (hb_set_t *set, hb_codepoint_t codepoint); void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +void hb_set_del (hb_set_t *set, hb_codepoint_t codepoint); +void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + hb_bool_t -hb_set_equal (hb_set_t *set, - hb_set_t *other); +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other); void -hb_set_set (hb_set_t *set, - hb_set_t *other); +hb_set_set (hb_set_t *set, + const hb_set_t *other); void -hb_set_union (hb_set_t *set, - hb_set_t *other); +hb_set_union (hb_set_t *set, + const hb_set_t *other); void -hb_set_intersect (hb_set_t *set, - hb_set_t *other); +hb_set_intersect (hb_set_t *set, + const hb_set_t *other); void -hb_set_subtract (hb_set_t *set, - hb_set_t *other); +hb_set_subtract (hb_set_t *set, + const hb_set_t *other); void -hb_set_symmetric_difference (hb_set_t *set, - hb_set_t *other); +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other); + +void +hb_set_invert (hb_set_t *set); + +unsigned int +hb_set_get_population (const hb_set_t *set); /* Returns -1 if set empty. */ hb_codepoint_t -hb_set_min (hb_set_t *set); +hb_set_get_min (const hb_set_t *set); /* Returns -1 if set empty. */ hb_codepoint_t -hb_set_max (hb_set_t *set); +hb_set_get_max (const hb_set_t *set); /* Pass -1 in to get started. */ hb_bool_t -hb_set_next (hb_set_t *set, +hb_set_next (const hb_set_t *set, hb_codepoint_t *codepoint); -/* TODO: Add faster iteration API? */ +/* Pass -1 for first and last to get started. */ +hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); HB_END_DECLS diff --git a/src/hb-shape-plan-private.hh b/src/hb-shape-plan-private.hh new file mode 100644 index 0000000..607da5e --- /dev/null +++ b/src/hb-shape-plan-private.hh @@ -0,0 +1,62 @@ +/* + * 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; + + 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 +#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/src/hb-shape-plan.cc b/src/hb-shape-plan.cc new file mode 100644 index 0000000..2166173 --- /dev/null +++ b/src/hb-shape-plan.cc @@ -0,0 +1,492 @@ +/* + * 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 char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, + "num_features=%d shaper_list=%p", + num_user_features, + 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); \ + 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: 1.0 + **/ +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) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, NULL, + "face=%p num_features=%d shaper_list=%p", + face, + num_user_features, + shaper_list); + + hb_shape_plan_t *shape_plan; + hb_feature_t *features = NULL; + + if (unlikely (!face)) + face = hb_face_get_empty (); + if (unlikely (!props || hb_object_is_inert (face))) + return hb_shape_plan_get_empty (); + if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t)))) + return hb_shape_plan_get_empty (); + if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) { + 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)); + + hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list); + + return shape_plan; +} + +/** + * hb_shape_plan_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ +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 */ + + { +#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: 1.0 + **/ +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: 1.0 + **/ +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); +} + +/** + * hb_shape_plan_set_user_data: (skip) + * @shape_plan: a shape plan. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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: 1.0 + **/ +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: 1.0 + **/ +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", + num_features, + shape_plan->shaper_func); + + if (unlikely (hb_object_is_inert (shape_plan) || + hb_object_is_inert (font) || + hb_object_is_inert (buffer))) + 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; + 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 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) && + ((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; +} + +/** + * 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: 1.0 + **/ +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) +{ + 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_create (face, props, user_features, num_user_features, shaper_list); + + /* 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; + + 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: 1.0 + **/ +const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) +{ + return shape_plan->shaper_name; +} diff --git a/src/hb-shape-plan.h b/src/hb-shape-plan.h new file mode 100644 index 0000000..8f54552 --- /dev/null +++ b/src/hb-shape-plan.h @@ -0,0 +1,89 @@ +/* + * 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_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_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_shape_plan_t * +hb_shape_plan_get_empty (void); + +hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan); + +void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan); + +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); + +void * +hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key); + + +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); + +const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan); + + +HB_END_DECLS + +#endif /* HB_SHAPE_PLAN_H */ diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 163a5bf..4e22c61 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,128 +23,279 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #include "hb-private.hh" -#include "hb-shape.h" - +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" #include "hb-buffer-private.hh" +#include "hb-font-private.hh" -#ifdef HAVE_GRAPHITE -#include "hb-graphite2-private.hh" -#endif -#ifdef HAVE_UNISCRIBE -# include "hb-uniscribe-private.hh" -#endif -#ifdef HAVE_OT -# include "hb-ot-shape-private.hh" -#endif -#include "hb-fallback-shape-private.hh" - -typedef hb_bool_t (*hb_shape_func_t) (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); - -#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape} -static const struct hb_shaper_pair_t { - char name[16]; - hb_shape_func_t func; -} all_shapers[] = { - /* v--- Add new shapers in the right place here */ -#ifdef HAVE_GRAPHITE - HB_SHAPER_IMPLEMENT (graphite2), -#endif -#ifdef HAVE_UNISCRIBE - HB_SHAPER_IMPLEMENT (uniscribe), -#endif -#ifdef HAVE_OT - HB_SHAPER_IMPLEMENT (ot), -#endif - HB_SHAPER_IMPLEMENT (fallback) /* should be last */ -}; -#undef HB_SHAPER_IMPLEMENT +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} -/* Thread-safe, lock-free, shapers */ +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); -static hb_shaper_pair_t *static_shapers; + if (*pp == end || **pp != c) + return false; -static -void free_static_shapers (void) + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) { - if (unlikely (static_shapers != all_shapers)) - free (static_shapers); + 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 const hb_shaper_pair_t * -get_shapers (void) +static bool +parse_bool (const char **pp, const char *end, unsigned int *pv) { -retry: - hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers); + 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; +} - if (unlikely (!shapers)) +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 == '"')) { - char *env = getenv ("HB_SHAPER_LIST"); - if (!env || !*env) { - hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair_t *) all_shapers); - return (const hb_shaper_pair_t *) all_shapers; - } + quote = **pp; + (*pp)++; + } - /* Not found; allocate one. */ - shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers)); - if (unlikely (!shapers)) - return (const hb_shaper_pair_t *) all_shapers; - memcpy (shapers, all_shapers, sizeof (all_shapers)); - - /* Reorder shaper list to prefer requested shapers. */ - unsigned int i = 0; - char *end, *p = env; - for (;;) { - end = strchr (p, ','); - if (!end) - end = p + strlen (p); - - for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++) - if (end - p == (int) strlen (shapers[j].name) && - 0 == strncmp (shapers[j].name, p, end - p)) - { - /* Reorder this shaper to position i */ - struct hb_shaper_pair_t t = shapers[j]; - memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); - shapers[i] = t; - i++; - } - - if (!*end) - break; - else - p = end + 1; - } + const char *p = *pp; + while (*pp < end && ISALNUM(**pp)) + (*pp)++; - if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) { - free (shapers); - goto retry; - } + if (p == *pp || *pp - p > 4) + return false; -#ifdef HAVE_ATEXIT - atexit (free_static_shapers); /* First person registers atexit() callback. */ -#endif + 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 shapers; + 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): + * @len: + * @feature: (out) (optional): + * + * + * + * Return value: + * + * Since: 1.0 + **/ +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: + * @buf: (array length=size): + * @size: + * + * + * + * Since: 1.0 + **/ +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: + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ const char ** hb_shape_list_shapers (void) { @@ -153,15 +305,15 @@ retry: if (unlikely (!shaper_list)) { /* Not found; allocate one. */ - shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof (const char *)); + 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 = get_shapers (); + const hb_shaper_pair_t *shapers = _hb_shapers_get (); unsigned int i; - for (i = 0; i < ARRAY_LENGTH (all_shapers); i++) + for (i = 0; i < HB_SHAPERS_COUNT; i++) shaper_list[i] = shapers[i].name; shaper_list[i] = NULL; @@ -170,7 +322,7 @@ retry: goto retry; } -#ifdef HAVE_ATEXIT +#ifdef HB_USE_ATEXIT atexit (free_static_shaper_list); /* First person registers atexit() callback. */ #endif } @@ -179,6 +331,20 @@ retry: } +/** + * hb_shape_full: + * @font: a font. + * @buffer: a buffer. + * @features: (array length=num_features): + * @num_features: + * @shaper_list: (array zero-terminated=1): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_shape_full (hb_font_t *font, hb_buffer_t *buffer, @@ -186,27 +352,31 @@ hb_shape_full (hb_font_t *font, unsigned int num_features, const char * const *shaper_list) { - hb_font_make_immutable (font); /* So we can safely cache stuff on it */ + if (unlikely (!buffer->len)) + return true; - if (likely (!shaper_list)) { - const hb_shaper_pair_t *shapers = get_shapers (); - for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) - if (likely (shapers[i].func (font, buffer, features, num_features))) - return true; - } else { - while (*shaper_list) { - for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) - if (0 == strcmp (*shaper_list, all_shapers[i].name)) { - if (likely (all_shapers[i].func (font, buffer, features, num_features))) - return true; - break; - } - shaper_list++; - } - } - return false; + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); + + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, 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: a font. + * @buffer: a buffer. + * @features: (array length=num_features): + * @num_features: + * + * + * + * Since: 1.0 + **/ void hb_shape (hb_font_t *font, hb_buffer_t *buffer, diff --git a/src/hb-shape.h b/src/hb-shape.h index 1a0d6cf..10a35cb 100644 --- a/src/hb-shape.h +++ b/src/hb-shape.h @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #ifndef HB_H_IN @@ -38,13 +40,24 @@ HB_BEGIN_DECLS -typedef struct _hb_feature_t { +typedef struct hb_feature_t { hb_tag_t tag; uint32_t value; unsigned int start; unsigned int end; } hb_feature_t; +/* len=-1 means str is NUL-terminated */ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +/* Something like 128 bytes is more than enough. + * nul-terminates. */ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + void hb_shape (hb_font_t *font, diff --git a/src/hb-uniscribe-private.hh b/src/hb-shaper-impl-private.hh index 239ab0c..7844081 100644 --- a/src/hb-uniscribe-private.hh +++ b/src/hb-shaper-impl-private.hh @@ -24,19 +24,20 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_UNISCRIBE_PRIVATE_HH -#define HB_UNISCRIBE_PRIVATE_HH +#ifndef HB_SHAPER_IMPL_PRIVATE_HH +#define HB_SHAPER_IMPL_PRIVATE_HH #include "hb-private.hh" -#include "hb-uniscribe.h" +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" -HB_INTERNAL hb_bool_t -_hb_uniscribe_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); +#ifdef HB_SHAPER +#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object) +#endif -#endif /* HB_UNISCRIBE_PRIVATE_HH */ +#endif /* HB_SHAPER_IMPL_PRIVATE_HH */ diff --git a/src/hb-shaper-list.hh b/src/hb-shaper-list.hh new file mode 100644 index 0000000..6c537d4 --- /dev/null +++ b/src/hb-shaper-list.hh @@ -0,0 +1,55 @@ +/* + * 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_CORETEXT +HB_SHAPER_IMPLEMENT (coretext) +#endif + +#ifdef HAVE_FALLBACK +HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ +#endif diff --git a/src/hb-shaper-private.hh b/src/hb-shaper-private.hh new file mode 100644 index 0000000..d1d1146 --- /dev/null +++ b/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/src/hb-shaper.cc b/src/hb-shaper.cc new file mode 100644 index 0000000..580b95c --- /dev/null +++ b/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 *) malloc (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/src/hb-tt-font.cc b/src/hb-tt-font.cc deleted file mode 100644 index 0055ae9..0000000 --- a/src/hb-tt-font.cc +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#include "hb-font-private.hh" /* Shall be first since may include windows.h */ - -#include "hb-open-type-private.hh" - -#include "hb-ot-hhea-table.hh" -#include "hb-ot-hmtx-table.hh" - -#include "hb-blob.h" - -#include <string.h> - - - -#if 0 -struct hb_tt_font_t -{ - const struct hhea *hhea; - hb_blob_t *hhea_blob; -}; - - -static hb_tt_font_t * -_hb_tt_font_create (hb_font_t *font) -{ - /* TODO Remove this object altogether */ - hb_tt_font_t *tt = (hb_tt_font_t *) calloc (1, sizeof (hb_tt_font_t)); - - tt->hhea_blob = Sanitizer<hhea>::sanitize (hb_face_reference_table (font->face, HB_OT_TAG_hhea)); - tt->hhea = Sanitizer<hhea>::lock_instance (tt->hhea_blob); - - return tt; -} - -static void -_hb_tt_font_destroy (hb_tt_font_t *tt) -{ - hb_blob_destroy (tt->hhea_blob); - - free (tt); -} - -static inline const hhea& -_get_hhea (hb_face_t *face) -{ -// return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea); -} - - -/* - * hb_tt_font_funcs_t - */ - -static hb_bool_t -hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) -{ - if (font->parent) - return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph); - - *glyph = 0; - return false; -} - -static hb_position_t -hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) -{ - if (font->parent) - return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph)); - - return font->x_scale; -} - -static hb_position_t -hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) -{ - if (font->parent) - return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph)); - - return font->y_scale; -} - -static hb_bool_t -hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) -{ - if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, - glyph, - x, y); - if (ret) - font->parent_scale_position (x, y); - return ret; - } - - *x = *y = 0; - return false; -} - -static hb_bool_t -hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) -{ - if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, - glyph, - x, y); - if (ret) - font->parent_scale_position (x, y); - return ret; - } - - *x = *y = 0; - return false; -} - -static hb_position_t -hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) -{ - if (font->parent) - return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph)); - - return 0; -} - -static hb_position_t -hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t top_glyph, - hb_codepoint_t bottom_glyph, - void *user_data HB_UNUSED) -{ - if (font->parent) - return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph)); - - return 0; -} - -static hb_bool_t -hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_glyph_extents_t *extents, - void *user_data HB_UNUSED) -{ - if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_extents (font->parent, - glyph, - extents); - if (ret) { - font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); - font->parent_scale_distance (&extents->width, &extents->height); - } - return ret; - } - - memset (extents, 0, sizeof (*extents)); - return false; -} - -static hb_bool_t -hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - unsigned int point_index, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) -{ - if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, - glyph, point_index, - x, y); - if (ret) - font->parent_scale_position (x, y); - return ret; - } - - *x = *y = 0; - return false; -} - - -static hb_font_funcs_t _hb_font_funcs_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } -}; -#endif - diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc new file mode 100644 index 0000000..5b53821 --- /dev/null +++ b/src/hb-ucdn.cc @@ -0,0 +1,231 @@ +/* + * 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, +}; + +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/src/hb-ucdn/COPYING b/src/hb-ucdn/COPYING new file mode 100644 index 0000000..be5205c --- /dev/null +++ b/src/hb-ucdn/COPYING @@ -0,0 +1,13 @@ +The contents of this directory are licensed under the following terms: + +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. diff --git a/src/hb-ucdn/Makefile.am b/src/hb-ucdn/Makefile.am new file mode 100644 index 0000000..0670b5c --- /dev/null +++ b/src/hb-ucdn/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in + +noinst_LTLIBRARIES = libhb-ucdn.la + + +libhb_ucdn_la_SOURCES = \ + ucdn.h \ + ucdn.c \ + unicodedata_db.h +libhb_ucdn_la_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src +libhb_ucdn_la_LIBADD = + +EXTRA_DIST = README COPYING + +-include $(top_srcdir)/git.mk diff --git a/src/hb-ucdn/Makefile.in b/src/hb-ucdn/Makefile.in new file mode 100644 index 0000000..64e16d2 --- /dev/null +++ b/src/hb-ucdn/Makefile.in @@ -0,0 +1,543 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/hb-ucdn +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + COPYING +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libhb_ucdn_la_DEPENDENCIES = +am_libhb_ucdn_la_OBJECTS = ucdn.lo +libhb_ucdn_la_OBJECTS = $(am_libhb_ucdn_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libhb_ucdn_la_SOURCES) +DIST_SOURCES = $(libhb_ucdn_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_FT_CFLAGS = @CAIRO_FT_CFLAGS@ +CAIRO_FT_LIBS = @CAIRO_FT_LIBS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CORETEXT_CFLAGS = @CORETEXT_CFLAGS@ +CORETEXT_LIBS = @CORETEXT_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ +FREETYPE_LIBS = @FREETYPE_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_CFLAGS = @GOBJECT_CFLAGS@ +GOBJECT_LIBS = @GOBJECT_LIBS@ +GRAPHITE2_CFLAGS = @GRAPHITE2_CFLAGS@ +GRAPHITE2_LIBS = @GRAPHITE2_LIBS@ +GREP = @GREP@ +GTHREAD_CFLAGS = @GTHREAD_CFLAGS@ +GTHREAD_LIBS = @GTHREAD_LIBS@ +HB_LIBTOOL_VERSION_INFO = @HB_LIBTOOL_VERSION_INFO@ +HB_VERSION = @HB_VERSION@ +HB_VERSION_MAJOR = @HB_VERSION_MAJOR@ +HB_VERSION_MICRO = @HB_VERSION_MICRO@ +HB_VERSION_MINOR = @HB_VERSION_MINOR@ +ICU_CFLAGS = @ICU_CFLAGS@ +ICU_LE_CFLAGS = @ICU_LE_CFLAGS@ +ICU_LE_LIBS = @ICU_LE_LIBS@ +ICU_LIBS = @ICU_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +UNISCRIBE_CFLAGS = @UNISCRIBE_CFLAGS@ +UNISCRIBE_LIBS = @UNISCRIBE_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libhb-ucdn.la +libhb_ucdn_la_SOURCES = \ + ucdn.h \ + ucdn.c \ + unicodedata_db.h + +libhb_ucdn_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src + +libhb_ucdn_la_LIBADD = +EXTRA_DIST = README COPYING +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/hb-ucdn/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnits src/hb-ucdn/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libhb-ucdn.la: $(libhb_ucdn_la_OBJECTS) $(libhb_ucdn_la_DEPENDENCIES) $(EXTRA_libhb_ucdn_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libhb_ucdn_la_OBJECTS) $(libhb_ucdn_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucdn.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +-include $(top_srcdir)/git.mk + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/hb-ucdn/README b/src/hb-ucdn/README new file mode 100644 index 0000000..2f3c552 --- /dev/null +++ b/src/hb-ucdn/README @@ -0,0 +1,41 @@ +Contents of this directory are derived from UCDN: + + https://github.com/grigorig/ucdn + https://github.com/behdad/ucdn + +The original README follows: + + +UCDN - Unicode Database and Normalization + +UCDN is a Unicode support library. Currently, it provides access +to basic character properties contained in the Unicode Character +Database and low-level normalization functions (pairwise canonical +composition/decomposition and compatibility decomposition). More +functionality might be provided in the future, such as additional +properties, string normalization and encoding conversion. + +UCDN uses standard C89 with no particular dependencies or requirements +except for stdint.h, and can be easily integrated into existing +projects. However, it can also be used as a standalone library, +and a CMake build script is provided for this. The first motivation +behind UCDN development was to provide a standalone set of Unicode +functions for the HarfBuzz OpenType shaping library. For this purpose, +a HarfBuzz-specific wrapper is shipped along with it (hb-ucdn.h). + +UCDN is published under the ISC license, please see the license header +in the C source code for more information. The makeunicodata.py script +required for parsing Unicode database files is licensed under the +PSF license, please see PYTHON-LICENSE for more information. + +UCDN was written by Grigori Goronzy <greg@kinoho.net>. + +How to Use + +Include ucdn.c, ucdn.h and unicodedata_db.h in your project. Now, +just use the functions as documented in ucdn.h. + +In some cases, it might be necessary to regenerate the Unicode +database file. The script makeunicodedata.py (Python 3.x required) +fetches the appropriate files and dumps the compressed database into +unicodedata_db.h. diff --git a/src/hb-ucdn/ucdn.c b/src/hb-ucdn/ucdn.c new file mode 100644 index 0000000..d1a4195 --- /dev/null +++ b/src/hb-ucdn/ucdn.c @@ -0,0 +1,281 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include "ucdn.h" + +typedef struct { + unsigned char category; + unsigned char combining; + unsigned char bidi_class; + unsigned char mirrored; + unsigned char east_asian_width; + unsigned char normalization_check; + unsigned char script; +} UCDRecord; + +typedef struct { + unsigned short from, to; +} MirrorPair; + +typedef struct { + unsigned int start; + short count, index; +} Reindex; + +#include "unicodedata_db.h" + +/* constants required for Hangul (de)composition */ +#define SBASE 0xAC00 +#define LBASE 0x1100 +#define VBASE 0x1161 +#define TBASE 0x11A7 +#define SCOUNT 11172 +#define LCOUNT 19 +#define VCOUNT 21 +#define TCOUNT 28 +#define NCOUNT (VCOUNT * TCOUNT) + +static const UCDRecord *get_ucd_record(uint32_t code) +{ + int index, offset; + + if (code >= 0x110000) + index = 0; + else { + index = index0[code >> (SHIFT1+SHIFT2)] << SHIFT1; + offset = (code >> SHIFT2) & ((1<<SHIFT1) - 1); + index = index1[index + offset] << SHIFT2; + offset = code & ((1<<SHIFT2) - 1); + index = index2[index + offset]; + } + + return &ucd_records[index]; +} + +static const unsigned short *get_decomp_record(uint32_t code) +{ + int index, offset; + + if (code >= 0x110000) + index = 0; + else { + index = decomp_index0[code >> (DECOMP_SHIFT1+DECOMP_SHIFT2)] + << DECOMP_SHIFT1; + offset = (code >> DECOMP_SHIFT2) & ((1<<DECOMP_SHIFT1) - 1); + index = decomp_index1[index + offset] << DECOMP_SHIFT2; + offset = code & ((1<<DECOMP_SHIFT2) - 1); + index = decomp_index2[index + offset]; + } + + return &decomp_data[index]; +} + +static int get_comp_index(uint32_t code, const Reindex *idx) +{ + int i; + + for (i = 0; idx[i].start; i++) { + const Reindex *cur = &idx[i]; + if (code < cur->start) + return -1; + if (code <= cur->start + cur->count) { + return cur->index + (code - cur->start); + } + } + + return -1; +} + +static int compare_mp(const void *a, const void *b) +{ + MirrorPair *mpa = (MirrorPair *)a; + MirrorPair *mpb = (MirrorPair *)b; + return mpa->from - mpb->from; +} + +static int hangul_pair_decompose(uint32_t code, uint32_t *a, uint32_t *b) +{ + int si = code - SBASE; + + if (si < 0 || si >= SCOUNT) + return 0; + + if (si % TCOUNT) { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return 3; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return 2; + } +} + +static int hangul_pair_compose(uint32_t *code, uint32_t a, uint32_t b) +{ + if (b < VBASE || b >= (TBASE + TCOUNT)) + return 0; + + if ((a < LBASE || a >= (LBASE + LCOUNT)) + && (a < SBASE || a >= (SBASE + SCOUNT))) + return 0; + + if (a >= SBASE) { + /* LV,T */ + *code = a + (b - TBASE); + return 3; + } else { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *code = SBASE + li * NCOUNT + vi * TCOUNT; + return 2; + } +} + +static uint32_t decode_utf16(const unsigned short **code_ptr) +{ + const unsigned short *code = *code_ptr; + + if ((code[0] & 0xd800) != 0xd800) { + *code_ptr += 1; + return (uint32_t)code[0]; + } else { + *code_ptr += 2; + return 0x10000 + ((uint32_t)code[1] - 0xdc00) + + (((uint32_t)code[0] - 0xd800) << 10); + } +} + +const char *ucdn_get_unicode_version(void) +{ + return UNIDATA_VERSION; +} + +int ucdn_get_combining_class(uint32_t code) +{ + return get_ucd_record(code)->combining; +} + +int ucdn_get_east_asian_width(uint32_t code) +{ + return get_ucd_record(code)->east_asian_width; +} + +int ucdn_get_general_category(uint32_t code) +{ + return get_ucd_record(code)->category; +} + +int ucdn_get_bidi_class(uint32_t code) +{ + return get_ucd_record(code)->bidi_class; +} + +int ucdn_get_mirrored(uint32_t code) +{ + return get_ucd_record(code)->mirrored; +} + +int ucdn_get_script(uint32_t code) +{ + return get_ucd_record(code)->script; +} + +uint32_t ucdn_mirror(uint32_t code) +{ + MirrorPair mp = {0}; + MirrorPair *res; + + if (get_ucd_record(code)->mirrored == 0) + return code; + + mp.from = code; + res = bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN, sizeof(MirrorPair), + compare_mp); + + if (res == NULL) + return code; + else + return res->to; +} + +int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b) +{ + const unsigned short *rec; + int len; + + if (hangul_pair_decompose(code, a, b)) + return 1; + + rec = get_decomp_record(code); + len = rec[0] >> 8; + + if ((rec[0] & 0xff) != 0 || len == 0) + return 0; + + rec++; + *a = decode_utf16(&rec); + if (len > 1) + *b = decode_utf16(&rec); + else + *b = 0; + + return 1; +} + +int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b) +{ + int l, r, index, indexi, offset; + + if (hangul_pair_compose(code, a, b)) + return 1; + + l = get_comp_index(a, nfc_first); + r = get_comp_index(b, nfc_last); + + if (l < 0 || r < 0) + return 0; + + indexi = l * TOTAL_LAST + r; + index = comp_index0[indexi >> (COMP_SHIFT1+COMP_SHIFT2)] << COMP_SHIFT1; + offset = (indexi >> COMP_SHIFT2) & ((1<<COMP_SHIFT1) - 1); + index = comp_index1[index + offset] << COMP_SHIFT2; + offset = indexi & ((1<<COMP_SHIFT2) - 1); + *code = comp_data[index + offset]; + + return *code != 0; +} + +int ucdn_compat_decompose(uint32_t code, uint32_t *decomposed) +{ + int i, len; + const unsigned short *rec = get_decomp_record(code); + len = rec[0] >> 8; + + if (len == 0) + return 0; + + rec++; + for (i = 0; i < len; i++) + decomposed[i] = decode_utf16(&rec); + + return len; +} diff --git a/src/hb-ucdn/ucdn.h b/src/hb-ucdn/ucdn.h new file mode 100644 index 0000000..ec8085b --- /dev/null +++ b/src/hb-ucdn/ucdn.h @@ -0,0 +1,358 @@ +/* + * 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. + */ + +#ifndef UCDN_H +#define UCDN_H + +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define HB_BEGIN_VISIBILITY _Pragma ("GCC visibility push(hidden)") +# define HB_END_VISIBILITY _Pragma ("GCC visibility pop") +#else +# define HB_BEGIN_VISIBILITY +# define HB_END_VISIBILITY +#endif +#ifdef __cplusplus +# define HB_BEGIN_HEADER extern "C" { HB_BEGIN_VISIBILITY +# define HB_END_HEADER HB_END_VISIBILITY } +#else +# define HB_BEGIN_HEADER HB_BEGIN_VISIBILITY +# define HB_END_HEADER HB_END_VISIBILITY +#endif + +HB_BEGIN_HEADER + +#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 + +#define UCDN_EAST_ASIAN_F 0 +#define UCDN_EAST_ASIAN_H 1 +#define UCDN_EAST_ASIAN_W 2 +#define UCDN_EAST_ASIAN_NA 3 +#define UCDN_EAST_ASIAN_A 4 +#define UCDN_EAST_ASIAN_N 5 + +#define UCDN_SCRIPT_COMMON 0 +#define UCDN_SCRIPT_LATIN 1 +#define UCDN_SCRIPT_GREEK 2 +#define UCDN_SCRIPT_CYRILLIC 3 +#define UCDN_SCRIPT_ARMENIAN 4 +#define UCDN_SCRIPT_HEBREW 5 +#define UCDN_SCRIPT_ARABIC 6 +#define UCDN_SCRIPT_SYRIAC 7 +#define UCDN_SCRIPT_THAANA 8 +#define UCDN_SCRIPT_DEVANAGARI 9 +#define UCDN_SCRIPT_BENGALI 10 +#define UCDN_SCRIPT_GURMUKHI 11 +#define UCDN_SCRIPT_GUJARATI 12 +#define UCDN_SCRIPT_ORIYA 13 +#define UCDN_SCRIPT_TAMIL 14 +#define UCDN_SCRIPT_TELUGU 15 +#define UCDN_SCRIPT_KANNADA 16 +#define UCDN_SCRIPT_MALAYALAM 17 +#define UCDN_SCRIPT_SINHALA 18 +#define UCDN_SCRIPT_THAI 19 +#define UCDN_SCRIPT_LAO 20 +#define UCDN_SCRIPT_TIBETAN 21 +#define UCDN_SCRIPT_MYANMAR 22 +#define UCDN_SCRIPT_GEORGIAN 23 +#define UCDN_SCRIPT_HANGUL 24 +#define UCDN_SCRIPT_ETHIOPIC 25 +#define UCDN_SCRIPT_CHEROKEE 26 +#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 +#define UCDN_SCRIPT_OGHAM 28 +#define UCDN_SCRIPT_RUNIC 29 +#define UCDN_SCRIPT_KHMER 30 +#define UCDN_SCRIPT_MONGOLIAN 31 +#define UCDN_SCRIPT_HIRAGANA 32 +#define UCDN_SCRIPT_KATAKANA 33 +#define UCDN_SCRIPT_BOPOMOFO 34 +#define UCDN_SCRIPT_HAN 35 +#define UCDN_SCRIPT_YI 36 +#define UCDN_SCRIPT_OLD_ITALIC 37 +#define UCDN_SCRIPT_GOTHIC 38 +#define UCDN_SCRIPT_DESERET 39 +#define UCDN_SCRIPT_INHERITED 40 +#define UCDN_SCRIPT_TAGALOG 41 +#define UCDN_SCRIPT_HANUNOO 42 +#define UCDN_SCRIPT_BUHID 43 +#define UCDN_SCRIPT_TAGBANWA 44 +#define UCDN_SCRIPT_LIMBU 45 +#define UCDN_SCRIPT_TAI_LE 46 +#define UCDN_SCRIPT_LINEAR_B 47 +#define UCDN_SCRIPT_UGARITIC 48 +#define UCDN_SCRIPT_SHAVIAN 49 +#define UCDN_SCRIPT_OSMANYA 50 +#define UCDN_SCRIPT_CYPRIOT 51 +#define UCDN_SCRIPT_BRAILLE 52 +#define UCDN_SCRIPT_BUGINESE 53 +#define UCDN_SCRIPT_COPTIC 54 +#define UCDN_SCRIPT_NEW_TAI_LUE 55 +#define UCDN_SCRIPT_GLAGOLITIC 56 +#define UCDN_SCRIPT_TIFINAGH 57 +#define UCDN_SCRIPT_SYLOTI_NAGRI 58 +#define UCDN_SCRIPT_OLD_PERSIAN 59 +#define UCDN_SCRIPT_KHAROSHTHI 60 +#define UCDN_SCRIPT_BALINESE 61 +#define UCDN_SCRIPT_CUNEIFORM 62 +#define UCDN_SCRIPT_PHOENICIAN 63 +#define UCDN_SCRIPT_PHAGS_PA 64 +#define UCDN_SCRIPT_NKO 65 +#define UCDN_SCRIPT_SUNDANESE 66 +#define UCDN_SCRIPT_LEPCHA 67 +#define UCDN_SCRIPT_OL_CHIKI 68 +#define UCDN_SCRIPT_VAI 69 +#define UCDN_SCRIPT_SAURASHTRA 70 +#define UCDN_SCRIPT_KAYAH_LI 71 +#define UCDN_SCRIPT_REJANG 72 +#define UCDN_SCRIPT_LYCIAN 73 +#define UCDN_SCRIPT_CARIAN 74 +#define UCDN_SCRIPT_LYDIAN 75 +#define UCDN_SCRIPT_CHAM 76 +#define UCDN_SCRIPT_TAI_THAM 77 +#define UCDN_SCRIPT_TAI_VIET 78 +#define UCDN_SCRIPT_AVESTAN 79 +#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 +#define UCDN_SCRIPT_SAMARITAN 81 +#define UCDN_SCRIPT_LISU 82 +#define UCDN_SCRIPT_BAMUM 83 +#define UCDN_SCRIPT_JAVANESE 84 +#define UCDN_SCRIPT_MEETEI_MAYEK 85 +#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 +#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 +#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 +#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 +#define UCDN_SCRIPT_OLD_TURKIC 90 +#define UCDN_SCRIPT_KAITHI 91 +#define UCDN_SCRIPT_BATAK 92 +#define UCDN_SCRIPT_BRAHMI 93 +#define UCDN_SCRIPT_MANDAIC 94 +#define UCDN_SCRIPT_CHAKMA 95 +#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 +#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 +#define UCDN_SCRIPT_MIAO 98 +#define UCDN_SCRIPT_SHARADA 99 +#define UCDN_SCRIPT_SORA_SOMPENG 100 +#define UCDN_SCRIPT_TAKRI 101 +#define UCDN_SCRIPT_UNKNOWN 102 +#define UCDN_SCRIPT_BASSA_VAH 103 +#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 +#define UCDN_SCRIPT_DUPLOYAN 105 +#define UCDN_SCRIPT_ELBASAN 106 +#define UCDN_SCRIPT_GRANTHA 107 +#define UCDN_SCRIPT_KHOJKI 108 +#define UCDN_SCRIPT_KHUDAWADI 109 +#define UCDN_SCRIPT_LINEAR_A 110 +#define UCDN_SCRIPT_MAHAJANI 111 +#define UCDN_SCRIPT_MANICHAEAN 112 +#define UCDN_SCRIPT_MENDE_KIKAKUI 113 +#define UCDN_SCRIPT_MODI 114 +#define UCDN_SCRIPT_MRO 115 +#define UCDN_SCRIPT_NABATAEAN 116 +#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 +#define UCDN_SCRIPT_OLD_PERMIC 118 +#define UCDN_SCRIPT_PAHAWH_HMONG 119 +#define UCDN_SCRIPT_PALMYRENE 120 +#define UCDN_SCRIPT_PAU_CIN_HAU 121 +#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 +#define UCDN_SCRIPT_SIDDHAM 123 +#define UCDN_SCRIPT_TIRHUTA 124 +#define UCDN_SCRIPT_WARANG_CITI 125 + +#define UCDN_GENERAL_CATEGORY_CC 0 +#define UCDN_GENERAL_CATEGORY_CF 1 +#define UCDN_GENERAL_CATEGORY_CN 2 +#define UCDN_GENERAL_CATEGORY_CO 3 +#define UCDN_GENERAL_CATEGORY_CS 4 +#define UCDN_GENERAL_CATEGORY_LL 5 +#define UCDN_GENERAL_CATEGORY_LM 6 +#define UCDN_GENERAL_CATEGORY_LO 7 +#define UCDN_GENERAL_CATEGORY_LT 8 +#define UCDN_GENERAL_CATEGORY_LU 9 +#define UCDN_GENERAL_CATEGORY_MC 10 +#define UCDN_GENERAL_CATEGORY_ME 11 +#define UCDN_GENERAL_CATEGORY_MN 12 +#define UCDN_GENERAL_CATEGORY_ND 13 +#define UCDN_GENERAL_CATEGORY_NL 14 +#define UCDN_GENERAL_CATEGORY_NO 15 +#define UCDN_GENERAL_CATEGORY_PC 16 +#define UCDN_GENERAL_CATEGORY_PD 17 +#define UCDN_GENERAL_CATEGORY_PE 18 +#define UCDN_GENERAL_CATEGORY_PF 19 +#define UCDN_GENERAL_CATEGORY_PI 20 +#define UCDN_GENERAL_CATEGORY_PO 21 +#define UCDN_GENERAL_CATEGORY_PS 22 +#define UCDN_GENERAL_CATEGORY_SC 23 +#define UCDN_GENERAL_CATEGORY_SK 24 +#define UCDN_GENERAL_CATEGORY_SM 25 +#define UCDN_GENERAL_CATEGORY_SO 26 +#define UCDN_GENERAL_CATEGORY_ZL 27 +#define UCDN_GENERAL_CATEGORY_ZP 28 +#define UCDN_GENERAL_CATEGORY_ZS 29 + +#define UCDN_BIDI_CLASS_L 0 +#define UCDN_BIDI_CLASS_LRE 1 +#define UCDN_BIDI_CLASS_LRO 2 +#define UCDN_BIDI_CLASS_R 3 +#define UCDN_BIDI_CLASS_AL 4 +#define UCDN_BIDI_CLASS_RLE 5 +#define UCDN_BIDI_CLASS_RLO 6 +#define UCDN_BIDI_CLASS_PDF 7 +#define UCDN_BIDI_CLASS_EN 8 +#define UCDN_BIDI_CLASS_ES 9 +#define UCDN_BIDI_CLASS_ET 10 +#define UCDN_BIDI_CLASS_AN 11 +#define UCDN_BIDI_CLASS_CS 12 +#define UCDN_BIDI_CLASS_NSM 13 +#define UCDN_BIDI_CLASS_BN 14 +#define UCDN_BIDI_CLASS_B 15 +#define UCDN_BIDI_CLASS_S 16 +#define UCDN_BIDI_CLASS_WS 17 +#define UCDN_BIDI_CLASS_ON 18 +#define UCDN_BIDI_CLASS_LRI 19 +#define UCDN_BIDI_CLASS_RLI 20 +#define UCDN_BIDI_CLASS_FSI 21 +#define UCDN_BIDI_CLASS_PDI 22 + +/** + * Return version of the Unicode database. + * + * @return Unicode database version + */ +const char *ucdn_get_unicode_version(void); + +/** + * Get combining class of a codepoint. + * + * @param code Unicode codepoint + * @return combining class value, as defined in UAX#44 + */ +int ucdn_get_combining_class(uint32_t code); + +/** + * Get east-asian width of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_EAST_ASIAN_* and as defined in UAX#11. + */ +int ucdn_get_east_asian_width(uint32_t code); + +/** + * Get general category of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_GENERAL_CATEGORY_* and as defined in + * UAX#44. + */ +int ucdn_get_general_category(uint32_t code); + +/** + * Get bidirectional class of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_BIDI_CLASS_* and as defined in UAX#44. + */ +int ucdn_get_bidi_class(uint32_t code); + +/** + * Get script of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_SCRIPT_* and as defined in UAX#24. + */ +int ucdn_get_script(uint32_t code); + +/** + * Check if codepoint can be mirrored. + * + * @param code Unicode codepoint + * @return 1 if mirrored character exists, otherwise 0 + */ +int ucdn_get_mirrored(uint32_t code); + +/** + * Mirror a codepoint. + * + * @param code Unicode codepoint + * @return mirrored codepoint or the original codepoint if no + * mirrored character exists + */ +uint32_t ucdn_mirror(uint32_t code); + +/** + * Pairwise canonical decomposition of a codepoint. This includes + * Hangul Jamo decomposition (see chapter 3.12 of the Unicode core + * specification). + * + * Hangul is decomposed into L and V jamos for LV forms, and an + * LV precomposed syllable and a T jamo for LVT forms. + * + * @param code Unicode codepoint + * @param a filled with first codepoint of decomposition + * @param b filled with second codepoint of decomposition, or 0 + * @return success + */ +int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b); + +/** + * Compatibility decomposition of a codepoint. + * + * @param code Unicode codepoint + * @param decomposed filled with decomposition, must be able to hold 18 + * characters + * @return length of decomposition or 0 in case none exists + */ +int ucdn_compat_decompose(uint32_t code, uint32_t *decomposed); + +/** + * Pairwise canonical composition of two codepoints. This includes + * Hangul Jamo composition (see chapter 3.12 of the Unicode core + * specification). + * + * Hangul composition expects either L and V jamos, or an LV + * precomposed syllable and a T jamo. This is exactly the inverse + * of pairwise Hangul decomposition. + * + * @param code filled with composition + * @param a first codepoint + * @param b second codepoint + * @return success + */ +int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b); + +HB_END_HEADER + +#endif diff --git a/src/hb-ucdn/unicodedata_db.h b/src/hb-ucdn/unicodedata_db.h new file mode 100644 index 0000000..a78d2e6 --- /dev/null +++ b/src/hb-ucdn/unicodedata_db.h @@ -0,0 +1,5017 @@ +/* this file was generated by makeunicodedata.py 3.2 */ + +#define UNIDATA_VERSION "7.0.0" +/* a list of unique database records */ +static const UCDRecord ucd_records[] = { + {2, 0, 18, 0, 5, 0, 102}, + {0, 0, 14, 0, 5, 0, 0}, + {0, 0, 16, 0, 5, 0, 0}, + {0, 0, 15, 0, 5, 0, 0}, + {0, 0, 17, 0, 5, 0, 0}, + {29, 0, 17, 0, 3, 0, 0}, + {21, 0, 18, 0, 3, 0, 0}, + {21, 0, 10, 0, 3, 0, 0}, + {23, 0, 10, 0, 3, 0, 0}, + {22, 0, 18, 1, 3, 0, 0}, + {18, 0, 18, 1, 3, 0, 0}, + {25, 0, 9, 0, 3, 0, 0}, + {21, 0, 12, 0, 3, 0, 0}, + {17, 0, 9, 0, 3, 0, 0}, + {13, 0, 8, 0, 3, 0, 0}, + {25, 0, 18, 1, 3, 0, 0}, + {25, 0, 18, 0, 3, 0, 0}, + {9, 0, 0, 0, 3, 0, 1}, + {24, 0, 18, 0, 3, 0, 0}, + {16, 0, 18, 0, 3, 0, 0}, + {5, 0, 0, 0, 3, 0, 1}, + {29, 0, 12, 0, 5, 0, 0}, + {21, 0, 18, 0, 4, 0, 0}, + {23, 0, 10, 0, 4, 0, 0}, + {26, 0, 18, 0, 3, 0, 0}, + {24, 0, 18, 0, 4, 0, 0}, + {26, 0, 18, 0, 5, 0, 0}, + {7, 0, 0, 0, 4, 0, 1}, + {20, 0, 18, 1, 5, 0, 0}, + {1, 0, 14, 0, 4, 0, 0}, + {26, 0, 18, 0, 4, 0, 0}, + {26, 0, 10, 0, 4, 0, 0}, + {25, 0, 10, 0, 4, 0, 0}, + {15, 0, 8, 0, 4, 0, 0}, + {5, 0, 0, 0, 5, 0, 0}, + {19, 0, 18, 1, 5, 0, 0}, + {15, 0, 18, 0, 4, 0, 0}, + {9, 0, 0, 0, 5, 0, 1}, + {9, 0, 0, 0, 4, 0, 1}, + {25, 0, 18, 0, 4, 0, 0}, + {5, 0, 0, 0, 4, 0, 1}, + {5, 0, 0, 0, 5, 0, 1}, + {7, 0, 0, 0, 5, 0, 1}, + {8, 0, 0, 0, 5, 0, 1}, + {6, 0, 0, 0, 5, 0, 1}, + {6, 0, 18, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 0}, + {24, 0, 18, 0, 5, 0, 0}, + {6, 0, 18, 0, 4, 0, 0}, + {6, 0, 0, 0, 4, 0, 0}, + {24, 0, 18, 0, 5, 0, 34}, + {12, 230, 13, 0, 4, 0, 40}, + {12, 232, 13, 0, 4, 0, 40}, + {12, 220, 13, 0, 4, 0, 40}, + {12, 216, 13, 0, 4, 0, 40}, + {12, 202, 13, 0, 4, 0, 40}, + {12, 1, 13, 0, 4, 0, 40}, + {12, 240, 13, 0, 4, 0, 40}, + {12, 0, 13, 0, 4, 0, 40}, + {12, 233, 13, 0, 4, 0, 40}, + {12, 234, 13, 0, 4, 0, 40}, + {9, 0, 0, 0, 5, 0, 2}, + {5, 0, 0, 0, 5, 0, 2}, + {24, 0, 18, 0, 5, 0, 2}, + {2, 0, 18, 0, 5, 0, 102}, + {6, 0, 0, 0, 5, 0, 2}, + {21, 0, 18, 0, 5, 0, 0}, + {9, 0, 0, 0, 4, 0, 2}, + {5, 0, 0, 0, 4, 0, 2}, + {9, 0, 0, 0, 5, 0, 54}, + {5, 0, 0, 0, 5, 0, 54}, + {25, 0, 18, 0, 5, 0, 2}, + {9, 0, 0, 0, 5, 0, 3}, + {9, 0, 0, 0, 4, 0, 3}, + {5, 0, 0, 0, 4, 0, 3}, + {5, 0, 0, 0, 5, 0, 3}, + {26, 0, 0, 0, 5, 0, 3}, + {12, 230, 13, 0, 5, 0, 3}, + {12, 230, 13, 0, 5, 0, 40}, + {11, 0, 13, 0, 5, 0, 3}, + {9, 0, 0, 0, 5, 0, 4}, + {6, 0, 0, 0, 5, 0, 4}, + {21, 0, 0, 0, 5, 0, 4}, + {5, 0, 0, 0, 5, 0, 4}, + {21, 0, 0, 0, 5, 0, 0}, + {17, 0, 18, 0, 5, 0, 4}, + {26, 0, 18, 0, 5, 0, 4}, + {23, 0, 10, 0, 5, 0, 4}, + {12, 220, 13, 0, 5, 0, 5}, + {12, 230, 13, 0, 5, 0, 5}, + {12, 222, 13, 0, 5, 0, 5}, + {12, 228, 13, 0, 5, 0, 5}, + {12, 10, 13, 0, 5, 0, 5}, + {12, 11, 13, 0, 5, 0, 5}, + {12, 12, 13, 0, 5, 0, 5}, + {12, 13, 13, 0, 5, 0, 5}, + {12, 14, 13, 0, 5, 0, 5}, + {12, 15, 13, 0, 5, 0, 5}, + {12, 16, 13, 0, 5, 0, 5}, + {12, 17, 13, 0, 5, 0, 5}, + {12, 18, 13, 0, 5, 0, 5}, + {12, 19, 13, 0, 5, 0, 5}, + {12, 20, 13, 0, 5, 0, 5}, + {12, 21, 13, 0, 5, 0, 5}, + {12, 22, 13, 0, 5, 0, 5}, + {17, 0, 3, 0, 5, 0, 5}, + {12, 23, 13, 0, 5, 0, 5}, + {21, 0, 3, 0, 5, 0, 5}, + {12, 24, 13, 0, 5, 0, 5}, + {12, 25, 13, 0, 5, 0, 5}, + {7, 0, 3, 0, 5, 0, 5}, + {1, 0, 11, 0, 5, 0, 6}, + {1, 0, 11, 0, 5, 0, 0}, + {25, 0, 18, 0, 5, 0, 6}, + {25, 0, 4, 0, 5, 0, 6}, + {21, 0, 10, 0, 5, 0, 6}, + {23, 0, 4, 0, 5, 0, 6}, + {21, 0, 12, 0, 5, 0, 0}, + {21, 0, 4, 0, 5, 0, 6}, + {26, 0, 18, 0, 5, 0, 6}, + {12, 230, 13, 0, 5, 0, 6}, + {12, 30, 13, 0, 5, 0, 6}, + {12, 31, 13, 0, 5, 0, 6}, + {12, 32, 13, 0, 5, 0, 6}, + {21, 0, 4, 0, 5, 0, 0}, + {1, 0, 4, 0, 5, 0, 0}, + {7, 0, 4, 0, 5, 0, 6}, + {6, 0, 4, 0, 5, 0, 0}, + {12, 27, 13, 0, 5, 0, 40}, + {12, 28, 13, 0, 5, 0, 40}, + {12, 29, 13, 0, 5, 0, 40}, + {12, 30, 13, 0, 5, 0, 40}, + {12, 31, 13, 0, 5, 0, 40}, + {12, 32, 13, 0, 5, 0, 40}, + {12, 33, 13, 0, 5, 0, 40}, + {12, 34, 13, 0, 5, 0, 40}, + {12, 220, 13, 0, 5, 0, 40}, + {12, 220, 13, 0, 5, 0, 6}, + {13, 0, 11, 0, 5, 0, 0}, + {21, 0, 11, 0, 5, 0, 6}, + {12, 35, 13, 0, 5, 0, 40}, + {6, 0, 4, 0, 5, 0, 6}, + {13, 0, 8, 0, 5, 0, 6}, + {26, 0, 4, 0, 5, 0, 6}, + {21, 0, 4, 0, 5, 0, 7}, + {1, 0, 4, 0, 5, 0, 7}, + {7, 0, 4, 0, 5, 0, 7}, + {12, 36, 13, 0, 5, 0, 7}, + {12, 230, 13, 0, 5, 0, 7}, + {12, 220, 13, 0, 5, 0, 7}, + {7, 0, 4, 0, 5, 0, 8}, + {12, 0, 13, 0, 5, 0, 8}, + {13, 0, 3, 0, 5, 0, 65}, + {7, 0, 3, 0, 5, 0, 65}, + {12, 230, 13, 0, 5, 0, 65}, + {12, 220, 13, 0, 5, 0, 65}, + {6, 0, 3, 0, 5, 0, 65}, + {26, 0, 18, 0, 5, 0, 65}, + {21, 0, 18, 0, 5, 0, 65}, + {7, 0, 3, 0, 5, 0, 81}, + {12, 230, 13, 0, 5, 0, 81}, + {6, 0, 3, 0, 5, 0, 81}, + {21, 0, 3, 0, 5, 0, 81}, + {7, 0, 3, 0, 5, 0, 94}, + {12, 220, 13, 0, 5, 0, 94}, + {21, 0, 3, 0, 5, 0, 94}, + {12, 27, 13, 0, 5, 0, 6}, + {12, 28, 13, 0, 5, 0, 6}, + {12, 29, 13, 0, 5, 0, 6}, + {12, 0, 13, 0, 5, 0, 9}, + {10, 0, 0, 0, 5, 0, 9}, + {7, 0, 0, 0, 5, 0, 9}, + {12, 7, 13, 0, 5, 0, 9}, + {12, 9, 13, 0, 5, 0, 9}, + {12, 230, 13, 0, 5, 0, 9}, + {13, 0, 0, 0, 5, 0, 9}, + {21, 0, 0, 0, 5, 0, 9}, + {6, 0, 0, 0, 5, 0, 9}, + {7, 0, 0, 0, 5, 0, 10}, + {12, 0, 13, 0, 5, 0, 10}, + {10, 0, 0, 0, 5, 0, 10}, + {12, 7, 13, 0, 5, 0, 10}, + {12, 9, 13, 0, 5, 0, 10}, + {13, 0, 0, 0, 5, 0, 10}, + {23, 0, 10, 0, 5, 0, 10}, + {15, 0, 0, 0, 5, 0, 10}, + {26, 0, 0, 0, 5, 0, 10}, + {12, 0, 13, 0, 5, 0, 11}, + {10, 0, 0, 0, 5, 0, 11}, + {7, 0, 0, 0, 5, 0, 11}, + {12, 7, 13, 0, 5, 0, 11}, + {12, 9, 13, 0, 5, 0, 11}, + {13, 0, 0, 0, 5, 0, 11}, + {12, 0, 13, 0, 5, 0, 12}, + {10, 0, 0, 0, 5, 0, 12}, + {7, 0, 0, 0, 5, 0, 12}, + {12, 7, 13, 0, 5, 0, 12}, + {12, 9, 13, 0, 5, 0, 12}, + {13, 0, 0, 0, 5, 0, 12}, + {21, 0, 0, 0, 5, 0, 12}, + {23, 0, 10, 0, 5, 0, 12}, + {12, 0, 13, 0, 5, 0, 13}, + {10, 0, 0, 0, 5, 0, 13}, + {7, 0, 0, 0, 5, 0, 13}, + {12, 7, 13, 0, 5, 0, 13}, + {12, 9, 13, 0, 5, 0, 13}, + {13, 0, 0, 0, 5, 0, 13}, + {26, 0, 0, 0, 5, 0, 13}, + {15, 0, 0, 0, 5, 0, 13}, + {12, 0, 13, 0, 5, 0, 14}, + {7, 0, 0, 0, 5, 0, 14}, + {10, 0, 0, 0, 5, 0, 14}, + {12, 9, 13, 0, 5, 0, 14}, + {13, 0, 0, 0, 5, 0, 14}, + {15, 0, 0, 0, 5, 0, 14}, + {26, 0, 18, 0, 5, 0, 14}, + {23, 0, 10, 0, 5, 0, 14}, + {12, 0, 13, 0, 5, 0, 15}, + {10, 0, 0, 0, 5, 0, 15}, + {7, 0, 0, 0, 5, 0, 15}, + {12, 9, 13, 0, 5, 0, 15}, + {12, 84, 13, 0, 5, 0, 15}, + {12, 91, 13, 0, 5, 0, 15}, + {13, 0, 0, 0, 5, 0, 15}, + {15, 0, 18, 0, 5, 0, 15}, + {26, 0, 0, 0, 5, 0, 15}, + {12, 0, 13, 0, 5, 0, 16}, + {10, 0, 0, 0, 5, 0, 16}, + {7, 0, 0, 0, 5, 0, 16}, + {12, 7, 13, 0, 5, 0, 16}, + {12, 0, 0, 0, 5, 0, 16}, + {12, 9, 13, 0, 5, 0, 16}, + {13, 0, 0, 0, 5, 0, 16}, + {12, 0, 13, 0, 5, 0, 17}, + {10, 0, 0, 0, 5, 0, 17}, + {7, 0, 0, 0, 5, 0, 17}, + {12, 9, 13, 0, 5, 0, 17}, + {13, 0, 0, 0, 5, 0, 17}, + {15, 0, 0, 0, 5, 0, 17}, + {26, 0, 0, 0, 5, 0, 17}, + {10, 0, 0, 0, 5, 0, 18}, + {7, 0, 0, 0, 5, 0, 18}, + {12, 9, 13, 0, 5, 0, 18}, + {12, 0, 13, 0, 5, 0, 18}, + {13, 0, 0, 0, 5, 0, 18}, + {21, 0, 0, 0, 5, 0, 18}, + {7, 0, 0, 0, 5, 0, 19}, + {12, 0, 13, 0, 5, 0, 19}, + {12, 103, 13, 0, 5, 0, 19}, + {12, 9, 13, 0, 5, 0, 19}, + {23, 0, 10, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 19}, + {12, 107, 13, 0, 5, 0, 19}, + {21, 0, 0, 0, 5, 0, 19}, + {13, 0, 0, 0, 5, 0, 19}, + {7, 0, 0, 0, 5, 0, 20}, + {12, 0, 13, 0, 5, 0, 20}, + {12, 118, 13, 0, 5, 0, 20}, + {6, 0, 0, 0, 5, 0, 20}, + {12, 122, 13, 0, 5, 0, 20}, + {13, 0, 0, 0, 5, 0, 20}, + {7, 0, 0, 0, 5, 0, 21}, + {26, 0, 0, 0, 5, 0, 21}, + {21, 0, 0, 0, 5, 0, 21}, + {12, 220, 13, 0, 5, 0, 21}, + {13, 0, 0, 0, 5, 0, 21}, + {15, 0, 0, 0, 5, 0, 21}, + {12, 216, 13, 0, 5, 0, 21}, + {22, 0, 18, 1, 5, 0, 21}, + {18, 0, 18, 1, 5, 0, 21}, + {10, 0, 0, 0, 5, 0, 21}, + {12, 129, 13, 0, 5, 0, 21}, + {12, 130, 13, 0, 5, 0, 21}, + {12, 0, 13, 0, 5, 0, 21}, + {12, 132, 13, 0, 5, 0, 21}, + {12, 230, 13, 0, 5, 0, 21}, + {12, 9, 13, 0, 5, 0, 21}, + {26, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 22}, + {10, 0, 0, 0, 5, 0, 22}, + {12, 0, 13, 0, 5, 0, 22}, + {12, 7, 13, 0, 5, 0, 22}, + {12, 9, 13, 0, 5, 0, 22}, + {13, 0, 0, 0, 5, 0, 22}, + {21, 0, 0, 0, 5, 0, 22}, + {12, 220, 13, 0, 5, 0, 22}, + {26, 0, 0, 0, 5, 0, 22}, + {9, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 5, 0, 23}, + {6, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 2, 0, 24}, + {7, 0, 0, 0, 5, 0, 24}, + {7, 0, 0, 0, 5, 0, 25}, + {12, 230, 13, 0, 5, 0, 25}, + {21, 0, 0, 0, 5, 0, 25}, + {15, 0, 0, 0, 5, 0, 25}, + {26, 0, 18, 0, 5, 0, 25}, + {7, 0, 0, 0, 5, 0, 26}, + {17, 0, 18, 0, 5, 0, 27}, + {7, 0, 0, 0, 5, 0, 27}, + {21, 0, 0, 0, 5, 0, 27}, + {29, 0, 17, 0, 5, 0, 28}, + {7, 0, 0, 0, 5, 0, 28}, + {22, 0, 18, 1, 5, 0, 28}, + {18, 0, 18, 1, 5, 0, 28}, + {7, 0, 0, 0, 5, 0, 29}, + {14, 0, 0, 0, 5, 0, 29}, + {7, 0, 0, 0, 5, 0, 41}, + {12, 0, 13, 0, 5, 0, 41}, + {12, 9, 13, 0, 5, 0, 41}, + {7, 0, 0, 0, 5, 0, 42}, + {12, 0, 13, 0, 5, 0, 42}, + {12, 9, 13, 0, 5, 0, 42}, + {7, 0, 0, 0, 5, 0, 43}, + {12, 0, 13, 0, 5, 0, 43}, + {7, 0, 0, 0, 5, 0, 44}, + {12, 0, 13, 0, 5, 0, 44}, + {7, 0, 0, 0, 5, 0, 30}, + {12, 0, 13, 0, 5, 0, 30}, + {10, 0, 0, 0, 5, 0, 30}, + {12, 9, 13, 0, 5, 0, 30}, + {21, 0, 0, 0, 5, 0, 30}, + {6, 0, 0, 0, 5, 0, 30}, + {23, 0, 10, 0, 5, 0, 30}, + {12, 230, 13, 0, 5, 0, 30}, + {13, 0, 0, 0, 5, 0, 30}, + {15, 0, 18, 0, 5, 0, 30}, + {21, 0, 18, 0, 5, 0, 31}, + {17, 0, 18, 0, 5, 0, 31}, + {12, 0, 13, 0, 5, 0, 31}, + {1, 0, 14, 0, 5, 0, 31}, + {13, 0, 0, 0, 5, 0, 31}, + {7, 0, 0, 0, 5, 0, 31}, + {6, 0, 0, 0, 5, 0, 31}, + {12, 228, 13, 0, 5, 0, 31}, + {7, 0, 0, 0, 5, 0, 45}, + {12, 0, 13, 0, 5, 0, 45}, + {10, 0, 0, 0, 5, 0, 45}, + {12, 222, 13, 0, 5, 0, 45}, + {12, 230, 13, 0, 5, 0, 45}, + {12, 220, 13, 0, 5, 0, 45}, + {26, 0, 18, 0, 5, 0, 45}, + {21, 0, 18, 0, 5, 0, 45}, + {13, 0, 0, 0, 5, 0, 45}, + {7, 0, 0, 0, 5, 0, 46}, + {7, 0, 0, 0, 5, 0, 55}, + {10, 0, 0, 0, 5, 0, 55}, + {13, 0, 0, 0, 5, 0, 55}, + {15, 0, 0, 0, 5, 0, 55}, + {26, 0, 18, 0, 5, 0, 55}, + {26, 0, 18, 0, 5, 0, 30}, + {7, 0, 0, 0, 5, 0, 53}, + {12, 230, 13, 0, 5, 0, 53}, + {12, 220, 13, 0, 5, 0, 53}, + {10, 0, 0, 0, 5, 0, 53}, + {12, 0, 13, 0, 5, 0, 53}, + {21, 0, 0, 0, 5, 0, 53}, + {7, 0, 0, 0, 5, 0, 77}, + {10, 0, 0, 0, 5, 0, 77}, + {12, 0, 13, 0, 5, 0, 77}, + {12, 9, 13, 0, 5, 0, 77}, + {12, 230, 13, 0, 5, 0, 77}, + {12, 220, 13, 0, 5, 0, 77}, + {13, 0, 0, 0, 5, 0, 77}, + {21, 0, 0, 0, 5, 0, 77}, + {6, 0, 0, 0, 5, 0, 77}, + {11, 0, 13, 0, 5, 0, 40}, + {12, 0, 13, 0, 5, 0, 61}, + {10, 0, 0, 0, 5, 0, 61}, + {7, 0, 0, 0, 5, 0, 61}, + {12, 7, 13, 0, 5, 0, 61}, + {10, 9, 0, 0, 5, 0, 61}, + {13, 0, 0, 0, 5, 0, 61}, + {21, 0, 0, 0, 5, 0, 61}, + {26, 0, 0, 0, 5, 0, 61}, + {12, 230, 13, 0, 5, 0, 61}, + {12, 220, 13, 0, 5, 0, 61}, + {12, 0, 13, 0, 5, 0, 66}, + {10, 0, 0, 0, 5, 0, 66}, + {7, 0, 0, 0, 5, 0, 66}, + {10, 9, 0, 0, 5, 0, 66}, + {12, 9, 13, 0, 5, 0, 66}, + {13, 0, 0, 0, 5, 0, 66}, + {7, 0, 0, 0, 5, 0, 92}, + {12, 7, 13, 0, 5, 0, 92}, + {10, 0, 0, 0, 5, 0, 92}, + {12, 0, 13, 0, 5, 0, 92}, + {10, 9, 0, 0, 5, 0, 92}, + {21, 0, 0, 0, 5, 0, 92}, + {7, 0, 0, 0, 5, 0, 67}, + {10, 0, 0, 0, 5, 0, 67}, + {12, 0, 13, 0, 5, 0, 67}, + {12, 7, 13, 0, 5, 0, 67}, + {21, 0, 0, 0, 5, 0, 67}, + {13, 0, 0, 0, 5, 0, 67}, + {13, 0, 0, 0, 5, 0, 68}, + {7, 0, 0, 0, 5, 0, 68}, + {6, 0, 0, 0, 5, 0, 68}, + {21, 0, 0, 0, 5, 0, 68}, + {21, 0, 0, 0, 5, 0, 66}, + {12, 1, 13, 0, 5, 0, 40}, + {10, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 3}, + {12, 234, 13, 0, 5, 0, 40}, + {12, 214, 13, 0, 5, 0, 40}, + {12, 202, 13, 0, 5, 0, 40}, + {12, 233, 13, 0, 5, 0, 40}, + {8, 0, 0, 0, 5, 0, 2}, + {29, 0, 17, 0, 5, 0, 0}, + {1, 0, 14, 0, 5, 0, 0}, + {1, 0, 14, 0, 5, 0, 40}, + {1, 0, 0, 0, 5, 0, 0}, + {1, 0, 3, 0, 5, 0, 0}, + {17, 0, 18, 0, 4, 0, 0}, + {17, 0, 18, 0, 5, 0, 0}, + {20, 0, 18, 0, 4, 0, 0}, + {19, 0, 18, 0, 4, 0, 0}, + {22, 0, 18, 0, 5, 0, 0}, + {20, 0, 18, 0, 5, 0, 0}, + {27, 0, 17, 0, 5, 0, 0}, + {28, 0, 15, 0, 5, 0, 0}, + {1, 0, 1, 0, 5, 0, 0}, + {1, 0, 5, 0, 5, 0, 0}, + {1, 0, 7, 0, 5, 0, 0}, + {1, 0, 2, 0, 5, 0, 0}, + {1, 0, 6, 0, 5, 0, 0}, + {21, 0, 10, 0, 4, 0, 0}, + {21, 0, 10, 0, 5, 0, 0}, + {16, 0, 18, 0, 5, 0, 0}, + {25, 0, 12, 0, 5, 0, 0}, + {22, 0, 18, 1, 5, 0, 0}, + {18, 0, 18, 1, 5, 0, 0}, + {25, 0, 18, 0, 5, 0, 0}, + {1, 0, 19, 0, 5, 0, 0}, + {1, 0, 20, 0, 5, 0, 0}, + {1, 0, 21, 0, 5, 0, 0}, + {1, 0, 22, 0, 5, 0, 0}, + {15, 0, 8, 0, 5, 0, 0}, + {25, 0, 9, 0, 5, 0, 0}, + {6, 0, 0, 0, 4, 0, 1}, + {23, 0, 10, 0, 1, 0, 0}, + {9, 0, 0, 0, 5, 0, 0}, + {5, 0, 0, 0, 4, 0, 0}, + {26, 0, 10, 0, 5, 0, 0}, + {25, 0, 18, 1, 5, 0, 0}, + {15, 0, 18, 0, 5, 0, 0}, + {14, 0, 0, 0, 4, 0, 1}, + {14, 0, 0, 0, 5, 0, 1}, + {25, 0, 18, 1, 4, 0, 0}, + {25, 0, 10, 0, 5, 0, 0}, + {22, 0, 18, 1, 2, 0, 0}, + {18, 0, 18, 1, 2, 0, 0}, + {26, 0, 0, 0, 4, 0, 0}, + {26, 0, 0, 0, 5, 0, 52}, + {9, 0, 0, 0, 5, 0, 56}, + {5, 0, 0, 0, 5, 0, 56}, + {26, 0, 18, 0, 5, 0, 54}, + {12, 230, 13, 0, 5, 0, 54}, + {21, 0, 18, 0, 5, 0, 54}, + {15, 0, 18, 0, 5, 0, 54}, + {5, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 5, 0, 57}, + {6, 0, 0, 0, 5, 0, 57}, + {21, 0, 0, 0, 5, 0, 57}, + {12, 9, 13, 0, 5, 0, 57}, + {26, 0, 18, 0, 2, 0, 35}, + {26, 0, 18, 0, 2, 0, 0}, + {29, 0, 17, 0, 0, 0, 0}, + {21, 0, 18, 0, 2, 0, 0}, + {6, 0, 0, 0, 2, 0, 35}, + {7, 0, 0, 0, 2, 0, 0}, + {14, 0, 0, 0, 2, 0, 35}, + {17, 0, 18, 0, 2, 0, 0}, + {22, 0, 18, 0, 2, 0, 0}, + {18, 0, 18, 0, 2, 0, 0}, + {12, 218, 13, 0, 2, 0, 40}, + {12, 228, 13, 0, 2, 0, 40}, + {12, 232, 13, 0, 2, 0, 40}, + {12, 222, 13, 0, 2, 0, 40}, + {10, 224, 0, 0, 2, 0, 24}, + {6, 0, 0, 0, 2, 0, 0}, + {7, 0, 0, 0, 2, 0, 32}, + {12, 8, 13, 0, 2, 0, 40}, + {24, 0, 18, 0, 2, 0, 0}, + {6, 0, 0, 0, 2, 0, 32}, + {7, 0, 0, 0, 2, 0, 33}, + {6, 0, 0, 0, 2, 0, 33}, + {7, 0, 0, 0, 2, 0, 34}, + {26, 0, 0, 0, 2, 0, 0}, + {15, 0, 0, 0, 2, 0, 0}, + {26, 0, 0, 0, 2, 0, 24}, + {26, 0, 18, 0, 2, 0, 24}, + {15, 0, 0, 0, 4, 0, 0}, + {15, 0, 18, 0, 2, 0, 0}, + {26, 0, 0, 0, 2, 0, 33}, + {7, 0, 0, 0, 2, 0, 35}, + {2, 0, 18, 0, 2, 0, 35}, + {2, 0, 18, 0, 2, 0, 102}, + {7, 0, 0, 0, 2, 0, 36}, + {6, 0, 0, 0, 2, 0, 36}, + {26, 0, 18, 0, 2, 0, 36}, + {7, 0, 0, 0, 5, 0, 82}, + {6, 0, 0, 0, 5, 0, 82}, + {21, 0, 0, 0, 5, 0, 82}, + {7, 0, 0, 0, 5, 0, 69}, + {6, 0, 0, 0, 5, 0, 69}, + {21, 0, 18, 0, 5, 0, 69}, + {13, 0, 0, 0, 5, 0, 69}, + {7, 0, 0, 0, 5, 0, 3}, + {21, 0, 18, 0, 5, 0, 3}, + {6, 0, 18, 0, 5, 0, 3}, + {7, 0, 0, 0, 5, 0, 83}, + {14, 0, 0, 0, 5, 0, 83}, + {12, 230, 13, 0, 5, 0, 83}, + {21, 0, 0, 0, 5, 0, 83}, + {24, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 58}, + {12, 0, 13, 0, 5, 0, 58}, + {12, 9, 13, 0, 5, 0, 58}, + {10, 0, 0, 0, 5, 0, 58}, + {26, 0, 18, 0, 5, 0, 58}, + {15, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 64}, + {21, 0, 18, 0, 5, 0, 64}, + {10, 0, 0, 0, 5, 0, 70}, + {7, 0, 0, 0, 5, 0, 70}, + {12, 9, 13, 0, 5, 0, 70}, + {21, 0, 0, 0, 5, 0, 70}, + {13, 0, 0, 0, 5, 0, 70}, + {13, 0, 0, 0, 5, 0, 71}, + {7, 0, 0, 0, 5, 0, 71}, + {12, 0, 13, 0, 5, 0, 71}, + {12, 220, 13, 0, 5, 0, 71}, + {21, 0, 0, 0, 5, 0, 71}, + {7, 0, 0, 0, 5, 0, 72}, + {12, 0, 13, 0, 5, 0, 72}, + {10, 0, 0, 0, 5, 0, 72}, + {10, 9, 0, 0, 5, 0, 72}, + {21, 0, 0, 0, 5, 0, 72}, + {12, 0, 13, 0, 5, 0, 84}, + {10, 0, 0, 0, 5, 0, 84}, + {7, 0, 0, 0, 5, 0, 84}, + {12, 7, 13, 0, 5, 0, 84}, + {10, 9, 0, 0, 5, 0, 84}, + {21, 0, 0, 0, 5, 0, 84}, + {13, 0, 0, 0, 5, 0, 84}, + {6, 0, 0, 0, 5, 0, 22}, + {7, 0, 0, 0, 5, 0, 76}, + {12, 0, 13, 0, 5, 0, 76}, + {10, 0, 0, 0, 5, 0, 76}, + {13, 0, 0, 0, 5, 0, 76}, + {21, 0, 0, 0, 5, 0, 76}, + {7, 0, 0, 0, 5, 0, 78}, + {12, 230, 13, 0, 5, 0, 78}, + {12, 220, 13, 0, 5, 0, 78}, + {6, 0, 0, 0, 5, 0, 78}, + {21, 0, 0, 0, 5, 0, 78}, + {7, 0, 0, 0, 5, 0, 85}, + {10, 0, 0, 0, 5, 0, 85}, + {12, 0, 13, 0, 5, 0, 85}, + {21, 0, 0, 0, 5, 0, 85}, + {6, 0, 0, 0, 5, 0, 85}, + {12, 9, 13, 0, 5, 0, 85}, + {13, 0, 0, 0, 5, 0, 85}, + {2, 0, 18, 0, 2, 0, 24}, + {4, 0, 0, 0, 5, 0, 102}, + {3, 0, 0, 0, 4, 0, 102}, + {2, 0, 18, 0, 4, 0, 102}, + {12, 26, 13, 0, 5, 0, 5}, + {25, 0, 9, 0, 5, 0, 5}, + {24, 0, 4, 0, 5, 0, 6}, + {18, 0, 18, 0, 5, 0, 0}, + {16, 0, 18, 0, 2, 0, 0}, + {21, 0, 12, 0, 2, 0, 0}, + {21, 0, 10, 0, 2, 0, 0}, + {25, 0, 9, 0, 2, 0, 0}, + {17, 0, 9, 0, 2, 0, 0}, + {25, 0, 18, 1, 2, 0, 0}, + {25, 0, 18, 0, 2, 0, 0}, + {23, 0, 10, 0, 2, 0, 0}, + {21, 0, 18, 0, 0, 0, 0}, + {21, 0, 10, 0, 0, 0, 0}, + {23, 0, 10, 0, 0, 0, 0}, + {22, 0, 18, 1, 0, 0, 0}, + {18, 0, 18, 1, 0, 0, 0}, + {25, 0, 9, 0, 0, 0, 0}, + {21, 0, 12, 0, 0, 0, 0}, + {17, 0, 9, 0, 0, 0, 0}, + {13, 0, 8, 0, 0, 0, 0}, + {25, 0, 18, 1, 0, 0, 0}, + {25, 0, 18, 0, 0, 0, 0}, + {9, 0, 0, 0, 0, 0, 1}, + {24, 0, 18, 0, 0, 0, 0}, + {16, 0, 18, 0, 0, 0, 0}, + {5, 0, 0, 0, 0, 0, 1}, + {21, 0, 18, 0, 1, 0, 0}, + {22, 0, 18, 1, 1, 0, 0}, + {18, 0, 18, 1, 1, 0, 0}, + {7, 0, 0, 0, 1, 0, 33}, + {6, 0, 0, 0, 1, 0, 0}, + {7, 0, 0, 0, 1, 0, 24}, + {26, 0, 18, 0, 0, 0, 0}, + {26, 0, 18, 0, 1, 0, 0}, + {25, 0, 18, 0, 1, 0, 0}, + {1, 0, 18, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 47}, + {14, 0, 18, 0, 5, 0, 2}, + {15, 0, 18, 0, 5, 0, 2}, + {26, 0, 18, 0, 5, 0, 2}, + {7, 0, 0, 0, 5, 0, 73}, + {7, 0, 0, 0, 5, 0, 74}, + {7, 0, 0, 0, 5, 0, 37}, + {15, 0, 0, 0, 5, 0, 37}, + {7, 0, 0, 0, 5, 0, 38}, + {14, 0, 0, 0, 5, 0, 38}, + {7, 0, 0, 0, 5, 0, 118}, + {12, 230, 13, 0, 5, 0, 118}, + {7, 0, 0, 0, 5, 0, 48}, + {21, 0, 0, 0, 5, 0, 48}, + {7, 0, 0, 0, 5, 0, 59}, + {21, 0, 0, 0, 5, 0, 59}, + {14, 0, 0, 0, 5, 0, 59}, + {9, 0, 0, 0, 5, 0, 39}, + {5, 0, 0, 0, 5, 0, 39}, + {7, 0, 0, 0, 5, 0, 49}, + {7, 0, 0, 0, 5, 0, 50}, + {13, 0, 0, 0, 5, 0, 50}, + {7, 0, 0, 0, 5, 0, 106}, + {7, 0, 0, 0, 5, 0, 104}, + {21, 0, 0, 0, 5, 0, 104}, + {7, 0, 0, 0, 5, 0, 110}, + {7, 0, 3, 0, 5, 0, 51}, + {7, 0, 3, 0, 5, 0, 86}, + {21, 0, 3, 0, 5, 0, 86}, + {15, 0, 3, 0, 5, 0, 86}, + {7, 0, 3, 0, 5, 0, 120}, + {26, 0, 3, 0, 5, 0, 120}, + {15, 0, 3, 0, 5, 0, 120}, + {7, 0, 3, 0, 5, 0, 116}, + {15, 0, 3, 0, 5, 0, 116}, + {7, 0, 3, 0, 5, 0, 63}, + {15, 0, 3, 0, 5, 0, 63}, + {21, 0, 18, 0, 5, 0, 63}, + {7, 0, 3, 0, 5, 0, 75}, + {21, 0, 3, 0, 5, 0, 75}, + {7, 0, 3, 0, 5, 0, 97}, + {7, 0, 3, 0, 5, 0, 96}, + {7, 0, 3, 0, 5, 0, 60}, + {12, 0, 13, 0, 5, 0, 60}, + {12, 220, 13, 0, 5, 0, 60}, + {12, 230, 13, 0, 5, 0, 60}, + {12, 1, 13, 0, 5, 0, 60}, + {12, 9, 13, 0, 5, 0, 60}, + {15, 0, 3, 0, 5, 0, 60}, + {21, 0, 3, 0, 5, 0, 60}, + {7, 0, 3, 0, 5, 0, 87}, + {15, 0, 3, 0, 5, 0, 87}, + {21, 0, 3, 0, 5, 0, 87}, + {7, 0, 3, 0, 5, 0, 117}, + {15, 0, 3, 0, 5, 0, 117}, + {7, 0, 3, 0, 5, 0, 112}, + {26, 0, 3, 0, 5, 0, 112}, + {12, 230, 13, 0, 5, 0, 112}, + {12, 220, 13, 0, 5, 0, 112}, + {15, 0, 3, 0, 5, 0, 112}, + {21, 0, 3, 0, 5, 0, 112}, + {7, 0, 3, 0, 5, 0, 79}, + {21, 0, 18, 0, 5, 0, 79}, + {7, 0, 3, 0, 5, 0, 88}, + {15, 0, 3, 0, 5, 0, 88}, + {7, 0, 3, 0, 5, 0, 89}, + {15, 0, 3, 0, 5, 0, 89}, + {7, 0, 3, 0, 5, 0, 122}, + {21, 0, 3, 0, 5, 0, 122}, + {15, 0, 3, 0, 5, 0, 122}, + {7, 0, 3, 0, 5, 0, 90}, + {15, 0, 11, 0, 5, 0, 6}, + {10, 0, 0, 0, 5, 0, 93}, + {12, 0, 13, 0, 5, 0, 93}, + {7, 0, 0, 0, 5, 0, 93}, + {12, 9, 13, 0, 5, 0, 93}, + {21, 0, 0, 0, 5, 0, 93}, + {15, 0, 18, 0, 5, 0, 93}, + {13, 0, 0, 0, 5, 0, 93}, + {12, 0, 13, 0, 5, 0, 91}, + {10, 0, 0, 0, 5, 0, 91}, + {7, 0, 0, 0, 5, 0, 91}, + {12, 9, 13, 0, 5, 0, 91}, + {12, 7, 13, 0, 5, 0, 91}, + {21, 0, 0, 0, 5, 0, 91}, + {1, 0, 0, 0, 5, 0, 91}, + {7, 0, 0, 0, 5, 0, 100}, + {13, 0, 0, 0, 5, 0, 100}, + {12, 230, 13, 0, 5, 0, 95}, + {7, 0, 0, 0, 5, 0, 95}, + {12, 0, 13, 0, 5, 0, 95}, + {10, 0, 0, 0, 5, 0, 95}, + {12, 9, 13, 0, 5, 0, 95}, + {13, 0, 0, 0, 5, 0, 95}, + {21, 0, 0, 0, 5, 0, 95}, + {7, 0, 0, 0, 5, 0, 111}, + {12, 7, 13, 0, 5, 0, 111}, + {21, 0, 0, 0, 5, 0, 111}, + {12, 0, 13, 0, 5, 0, 99}, + {10, 0, 0, 0, 5, 0, 99}, + {7, 0, 0, 0, 5, 0, 99}, + {10, 9, 0, 0, 5, 0, 99}, + {21, 0, 0, 0, 5, 0, 99}, + {13, 0, 0, 0, 5, 0, 99}, + {15, 0, 0, 0, 5, 0, 18}, + {7, 0, 0, 0, 5, 0, 108}, + {10, 0, 0, 0, 5, 0, 108}, + {12, 0, 13, 0, 5, 0, 108}, + {10, 9, 0, 0, 5, 0, 108}, + {12, 7, 13, 0, 5, 0, 108}, + {21, 0, 0, 0, 5, 0, 108}, + {7, 0, 0, 0, 5, 0, 109}, + {12, 0, 13, 0, 5, 0, 109}, + {10, 0, 0, 0, 5, 0, 109}, + {12, 7, 13, 0, 5, 0, 109}, + {12, 9, 13, 0, 5, 0, 109}, + {13, 0, 0, 0, 5, 0, 109}, + {12, 0, 13, 0, 5, 0, 107}, + {10, 0, 0, 0, 5, 0, 107}, + {7, 0, 0, 0, 5, 0, 107}, + {12, 7, 13, 0, 5, 0, 107}, + {10, 9, 0, 0, 5, 0, 107}, + {12, 230, 13, 0, 5, 0, 107}, + {7, 0, 0, 0, 5, 0, 124}, + {10, 0, 0, 0, 5, 0, 124}, + {12, 0, 13, 0, 5, 0, 124}, + {12, 9, 13, 0, 5, 0, 124}, + {12, 7, 13, 0, 5, 0, 124}, + {21, 0, 0, 0, 5, 0, 124}, + {13, 0, 0, 0, 5, 0, 124}, + {7, 0, 0, 0, 5, 0, 123}, + {10, 0, 0, 0, 5, 0, 123}, + {12, 0, 13, 0, 5, 0, 123}, + {12, 9, 13, 0, 5, 0, 123}, + {12, 7, 13, 0, 5, 0, 123}, + {21, 0, 0, 0, 5, 0, 123}, + {7, 0, 0, 0, 5, 0, 114}, + {10, 0, 0, 0, 5, 0, 114}, + {12, 0, 13, 0, 5, 0, 114}, + {12, 9, 13, 0, 5, 0, 114}, + {21, 0, 0, 0, 5, 0, 114}, + {13, 0, 0, 0, 5, 0, 114}, + {7, 0, 0, 0, 5, 0, 101}, + {12, 0, 13, 0, 5, 0, 101}, + {10, 0, 0, 0, 5, 0, 101}, + {10, 9, 0, 0, 5, 0, 101}, + {12, 7, 13, 0, 5, 0, 101}, + {13, 0, 0, 0, 5, 0, 101}, + {9, 0, 0, 0, 5, 0, 125}, + {5, 0, 0, 0, 5, 0, 125}, + {13, 0, 0, 0, 5, 0, 125}, + {15, 0, 0, 0, 5, 0, 125}, + {7, 0, 0, 0, 5, 0, 125}, + {7, 0, 0, 0, 5, 0, 121}, + {7, 0, 0, 0, 5, 0, 62}, + {14, 0, 0, 0, 5, 0, 62}, + {21, 0, 0, 0, 5, 0, 62}, + {7, 0, 0, 0, 5, 0, 80}, + {7, 0, 0, 0, 5, 0, 115}, + {13, 0, 0, 0, 5, 0, 115}, + {21, 0, 0, 0, 5, 0, 115}, + {7, 0, 0, 0, 5, 0, 103}, + {12, 1, 13, 0, 5, 0, 103}, + {21, 0, 0, 0, 5, 0, 103}, + {7, 0, 0, 0, 5, 0, 119}, + {12, 230, 13, 0, 5, 0, 119}, + {21, 0, 0, 0, 5, 0, 119}, + {26, 0, 0, 0, 5, 0, 119}, + {6, 0, 0, 0, 5, 0, 119}, + {13, 0, 0, 0, 5, 0, 119}, + {15, 0, 0, 0, 5, 0, 119}, + {7, 0, 0, 0, 5, 0, 98}, + {10, 0, 0, 0, 5, 0, 98}, + {12, 0, 13, 0, 5, 0, 98}, + {6, 0, 0, 0, 5, 0, 98}, + {7, 0, 0, 0, 5, 0, 105}, + {26, 0, 0, 0, 5, 0, 105}, + {12, 0, 13, 0, 5, 0, 105}, + {12, 1, 13, 0, 5, 0, 105}, + {21, 0, 0, 0, 5, 0, 105}, + {10, 216, 0, 0, 5, 0, 0}, + {10, 226, 0, 0, 5, 0, 0}, + {12, 230, 13, 0, 5, 0, 2}, + {25, 0, 0, 0, 5, 0, 0}, + {13, 0, 8, 0, 5, 0, 0}, + {7, 0, 3, 0, 5, 0, 113}, + {15, 0, 3, 0, 5, 0, 113}, + {12, 220, 13, 0, 5, 0, 113}, + {26, 0, 0, 0, 2, 0, 32}, +}; + +#define BIDI_MIRROR_LEN 364 +static const MirrorPair mirror_pairs[] = { + {40, 41}, + {41, 40}, + {60, 62}, + {62, 60}, + {91, 93}, + {93, 91}, + {123, 125}, + {125, 123}, + {171, 187}, + {187, 171}, + {3898, 3899}, + {3899, 3898}, + {3900, 3901}, + {3901, 3900}, + {5787, 5788}, + {5788, 5787}, + {8249, 8250}, + {8250, 8249}, + {8261, 8262}, + {8262, 8261}, + {8317, 8318}, + {8318, 8317}, + {8333, 8334}, + {8334, 8333}, + {8712, 8715}, + {8713, 8716}, + {8714, 8717}, + {8715, 8712}, + {8716, 8713}, + {8717, 8714}, + {8725, 10741}, + {8764, 8765}, + {8765, 8764}, + {8771, 8909}, + {8786, 8787}, + {8787, 8786}, + {8788, 8789}, + {8789, 8788}, + {8804, 8805}, + {8805, 8804}, + {8806, 8807}, + {8807, 8806}, + {8808, 8809}, + {8809, 8808}, + {8810, 8811}, + {8811, 8810}, + {8814, 8815}, + {8815, 8814}, + {8816, 8817}, + {8817, 8816}, + {8818, 8819}, + {8819, 8818}, + {8820, 8821}, + {8821, 8820}, + {8822, 8823}, + {8823, 8822}, + {8824, 8825}, + {8825, 8824}, + {8826, 8827}, + {8827, 8826}, + {8828, 8829}, + {8829, 8828}, + {8830, 8831}, + {8831, 8830}, + {8832, 8833}, + {8833, 8832}, + {8834, 8835}, + {8835, 8834}, + {8836, 8837}, + {8837, 8836}, + {8838, 8839}, + {8839, 8838}, + {8840, 8841}, + {8841, 8840}, + {8842, 8843}, + {8843, 8842}, + {8847, 8848}, + {8848, 8847}, + {8849, 8850}, + {8850, 8849}, + {8856, 10680}, + {8866, 8867}, + {8867, 8866}, + {8870, 10974}, + {8872, 10980}, + {8873, 10979}, + {8875, 10981}, + {8880, 8881}, + {8881, 8880}, + {8882, 8883}, + {8883, 8882}, + {8884, 8885}, + {8885, 8884}, + {8886, 8887}, + {8887, 8886}, + {8905, 8906}, + {8906, 8905}, + {8907, 8908}, + {8908, 8907}, + {8909, 8771}, + {8912, 8913}, + {8913, 8912}, + {8918, 8919}, + {8919, 8918}, + {8920, 8921}, + {8921, 8920}, + {8922, 8923}, + {8923, 8922}, + {8924, 8925}, + {8925, 8924}, + {8926, 8927}, + {8927, 8926}, + {8928, 8929}, + {8929, 8928}, + {8930, 8931}, + {8931, 8930}, + {8932, 8933}, + {8933, 8932}, + {8934, 8935}, + {8935, 8934}, + {8936, 8937}, + {8937, 8936}, + {8938, 8939}, + {8939, 8938}, + {8940, 8941}, + {8941, 8940}, + {8944, 8945}, + {8945, 8944}, + {8946, 8954}, + {8947, 8955}, + {8948, 8956}, + {8950, 8957}, + {8951, 8958}, + {8954, 8946}, + {8955, 8947}, + {8956, 8948}, + {8957, 8950}, + {8958, 8951}, + {8968, 8969}, + {8969, 8968}, + {8970, 8971}, + {8971, 8970}, + {9001, 9002}, + {9002, 9001}, + {10088, 10089}, + {10089, 10088}, + {10090, 10091}, + {10091, 10090}, + {10092, 10093}, + {10093, 10092}, + {10094, 10095}, + {10095, 10094}, + {10096, 10097}, + {10097, 10096}, + {10098, 10099}, + {10099, 10098}, + {10100, 10101}, + {10101, 10100}, + {10179, 10180}, + {10180, 10179}, + {10181, 10182}, + {10182, 10181}, + {10184, 10185}, + {10185, 10184}, + {10187, 10189}, + {10189, 10187}, + {10197, 10198}, + {10198, 10197}, + {10205, 10206}, + {10206, 10205}, + {10210, 10211}, + {10211, 10210}, + {10212, 10213}, + {10213, 10212}, + {10214, 10215}, + {10215, 10214}, + {10216, 10217}, + {10217, 10216}, + {10218, 10219}, + {10219, 10218}, + {10220, 10221}, + {10221, 10220}, + {10222, 10223}, + {10223, 10222}, + {10627, 10628}, + {10628, 10627}, + {10629, 10630}, + {10630, 10629}, + {10631, 10632}, + {10632, 10631}, + {10633, 10634}, + {10634, 10633}, + {10635, 10636}, + {10636, 10635}, + {10637, 10640}, + {10638, 10639}, + {10639, 10638}, + {10640, 10637}, + {10641, 10642}, + {10642, 10641}, + {10643, 10644}, + {10644, 10643}, + {10645, 10646}, + {10646, 10645}, + {10647, 10648}, + {10648, 10647}, + {10680, 8856}, + {10688, 10689}, + {10689, 10688}, + {10692, 10693}, + {10693, 10692}, + {10703, 10704}, + {10704, 10703}, + {10705, 10706}, + {10706, 10705}, + {10708, 10709}, + {10709, 10708}, + {10712, 10713}, + {10713, 10712}, + {10714, 10715}, + {10715, 10714}, + {10741, 8725}, + {10744, 10745}, + {10745, 10744}, + {10748, 10749}, + {10749, 10748}, + {10795, 10796}, + {10796, 10795}, + {10797, 10798}, + {10798, 10797}, + {10804, 10805}, + {10805, 10804}, + {10812, 10813}, + {10813, 10812}, + {10852, 10853}, + {10853, 10852}, + {10873, 10874}, + {10874, 10873}, + {10877, 10878}, + {10878, 10877}, + {10879, 10880}, + {10880, 10879}, + {10881, 10882}, + {10882, 10881}, + {10883, 10884}, + {10884, 10883}, + {10891, 10892}, + {10892, 10891}, + {10897, 10898}, + {10898, 10897}, + {10899, 10900}, + {10900, 10899}, + {10901, 10902}, + {10902, 10901}, + {10903, 10904}, + {10904, 10903}, + {10905, 10906}, + {10906, 10905}, + {10907, 10908}, + {10908, 10907}, + {10913, 10914}, + {10914, 10913}, + {10918, 10919}, + {10919, 10918}, + {10920, 10921}, + {10921, 10920}, + {10922, 10923}, + {10923, 10922}, + {10924, 10925}, + {10925, 10924}, + {10927, 10928}, + {10928, 10927}, + {10931, 10932}, + {10932, 10931}, + {10939, 10940}, + {10940, 10939}, + {10941, 10942}, + {10942, 10941}, + {10943, 10944}, + {10944, 10943}, + {10945, 10946}, + {10946, 10945}, + {10947, 10948}, + {10948, 10947}, + {10949, 10950}, + {10950, 10949}, + {10957, 10958}, + {10958, 10957}, + {10959, 10960}, + {10960, 10959}, + {10961, 10962}, + {10962, 10961}, + {10963, 10964}, + {10964, 10963}, + {10965, 10966}, + {10966, 10965}, + {10974, 8870}, + {10979, 8873}, + {10980, 8872}, + {10981, 8875}, + {10988, 10989}, + {10989, 10988}, + {10999, 11000}, + {11000, 10999}, + {11001, 11002}, + {11002, 11001}, + {11778, 11779}, + {11779, 11778}, + {11780, 11781}, + {11781, 11780}, + {11785, 11786}, + {11786, 11785}, + {11788, 11789}, + {11789, 11788}, + {11804, 11805}, + {11805, 11804}, + {11808, 11809}, + {11809, 11808}, + {11810, 11811}, + {11811, 11810}, + {11812, 11813}, + {11813, 11812}, + {11814, 11815}, + {11815, 11814}, + {11816, 11817}, + {11817, 11816}, + {12296, 12297}, + {12297, 12296}, + {12298, 12299}, + {12299, 12298}, + {12300, 12301}, + {12301, 12300}, + {12302, 12303}, + {12303, 12302}, + {12304, 12305}, + {12305, 12304}, + {12308, 12309}, + {12309, 12308}, + {12310, 12311}, + {12311, 12310}, + {12312, 12313}, + {12313, 12312}, + {12314, 12315}, + {12315, 12314}, + {65113, 65114}, + {65114, 65113}, + {65115, 65116}, + {65116, 65115}, + {65117, 65118}, + {65118, 65117}, + {65124, 65125}, + {65125, 65124}, + {65288, 65289}, + {65289, 65288}, + {65308, 65310}, + {65310, 65308}, + {65339, 65341}, + {65341, 65339}, + {65371, 65373}, + {65373, 65371}, + {65375, 65376}, + {65376, 65375}, + {65378, 65379}, + {65379, 65378}, +}; + +/* Reindexing of NFC first characters. */ +#define TOTAL_FIRST 376 +#define TOTAL_LAST 62 +static const Reindex nfc_first[] = { + { 60, 2, 0}, + { 65, 15, 3}, + { 82, 8, 19}, + { 97, 15, 28}, + { 114, 8, 44}, + { 168, 0, 53}, + { 194, 0, 54}, + { 196, 3, 55}, + { 202, 0, 59}, + { 207, 0, 60}, + { 212, 2, 61}, + { 216, 0, 64}, + { 220, 0, 65}, + { 226, 0, 66}, + { 228, 3, 67}, + { 234, 0, 71}, + { 239, 0, 72}, + { 244, 2, 73}, + { 248, 0, 76}, + { 252, 0, 77}, + { 258, 1, 78}, + { 274, 1, 80}, + { 332, 1, 82}, + { 346, 1, 84}, + { 352, 1, 86}, + { 360, 3, 88}, + { 383, 0, 92}, + { 416, 1, 93}, + { 431, 1, 95}, + { 439, 0, 97}, + { 490, 1, 98}, + { 550, 3, 100}, + { 558, 1, 104}, + { 658, 0, 106}, + { 913, 0, 107}, + { 917, 0, 108}, + { 919, 0, 109}, + { 921, 0, 110}, + { 927, 0, 111}, + { 929, 0, 112}, + { 933, 0, 113}, + { 937, 0, 114}, + { 940, 0, 115}, + { 942, 0, 116}, + { 945, 0, 117}, + { 949, 0, 118}, + { 951, 0, 119}, + { 953, 0, 120}, + { 959, 0, 121}, + { 961, 0, 122}, + { 965, 0, 123}, + { 969, 2, 124}, + { 974, 0, 127}, + { 978, 0, 128}, + { 1030, 0, 129}, + { 1040, 0, 130}, + { 1043, 0, 131}, + { 1045, 3, 132}, + { 1050, 0, 136}, + { 1054, 0, 137}, + { 1059, 0, 138}, + { 1063, 0, 139}, + { 1067, 0, 140}, + { 1069, 0, 141}, + { 1072, 0, 142}, + { 1075, 0, 143}, + { 1077, 3, 144}, + { 1082, 0, 148}, + { 1086, 0, 149}, + { 1091, 0, 150}, + { 1095, 0, 151}, + { 1099, 0, 152}, + { 1101, 0, 153}, + { 1110, 0, 154}, + { 1140, 1, 155}, + { 1240, 1, 157}, + { 1256, 1, 159}, + { 1575, 0, 161}, + { 1608, 0, 162}, + { 1610, 0, 163}, + { 1729, 0, 164}, + { 1746, 0, 165}, + { 1749, 0, 166}, + { 2344, 0, 167}, + { 2352, 0, 168}, + { 2355, 0, 169}, + { 2503, 0, 170}, + { 2887, 0, 171}, + { 2962, 0, 172}, + { 3014, 1, 173}, + { 3142, 0, 175}, + { 3263, 0, 176}, + { 3270, 0, 177}, + { 3274, 0, 178}, + { 3398, 1, 179}, + { 3545, 0, 181}, + { 3548, 0, 182}, + { 4133, 0, 183}, + { 6917, 0, 184}, + { 6919, 0, 185}, + { 6921, 0, 186}, + { 6923, 0, 187}, + { 6925, 0, 188}, + { 6929, 0, 189}, + { 6970, 0, 190}, + { 6972, 0, 191}, + { 6974, 1, 192}, + { 6978, 0, 194}, + { 7734, 1, 195}, + { 7770, 1, 197}, + { 7778, 1, 199}, + { 7840, 1, 201}, + { 7864, 1, 203}, + { 7884, 1, 205}, + { 7936, 17, 207}, + { 7960, 1, 225}, + { 7968, 17, 227}, + { 7992, 1, 245}, + { 8000, 1, 247}, + { 8008, 1, 249}, + { 8016, 1, 251}, + { 8025, 0, 253}, + { 8032, 16, 254}, + { 8052, 0, 271}, + { 8060, 0, 272}, + { 8118, 0, 273}, + { 8127, 0, 274}, + { 8134, 0, 275}, + { 8182, 0, 276}, + { 8190, 0, 277}, + { 8592, 0, 278}, + { 8594, 0, 279}, + { 8596, 0, 280}, + { 8656, 0, 281}, + { 8658, 0, 282}, + { 8660, 0, 283}, + { 8707, 0, 284}, + { 8712, 0, 285}, + { 8715, 0, 286}, + { 8739, 0, 287}, + { 8741, 0, 288}, + { 8764, 0, 289}, + { 8771, 0, 290}, + { 8773, 0, 291}, + { 8776, 0, 292}, + { 8781, 0, 293}, + { 8801, 0, 294}, + { 8804, 1, 295}, + { 8818, 1, 297}, + { 8822, 1, 299}, + { 8826, 3, 301}, + { 8834, 1, 305}, + { 8838, 1, 307}, + { 8849, 1, 309}, + { 8866, 0, 311}, + { 8872, 1, 312}, + { 8875, 0, 314}, + { 8882, 3, 315}, + { 12358, 0, 319}, + { 12363, 0, 320}, + { 12365, 0, 321}, + { 12367, 0, 322}, + { 12369, 0, 323}, + { 12371, 0, 324}, + { 12373, 0, 325}, + { 12375, 0, 326}, + { 12377, 0, 327}, + { 12379, 0, 328}, + { 12381, 0, 329}, + { 12383, 0, 330}, + { 12385, 0, 331}, + { 12388, 0, 332}, + { 12390, 0, 333}, + { 12392, 0, 334}, + { 12399, 0, 335}, + { 12402, 0, 336}, + { 12405, 0, 337}, + { 12408, 0, 338}, + { 12411, 0, 339}, + { 12445, 0, 340}, + { 12454, 0, 341}, + { 12459, 0, 342}, + { 12461, 0, 343}, + { 12463, 0, 344}, + { 12465, 0, 345}, + { 12467, 0, 346}, + { 12469, 0, 347}, + { 12471, 0, 348}, + { 12473, 0, 349}, + { 12475, 0, 350}, + { 12477, 0, 351}, + { 12479, 0, 352}, + { 12481, 0, 353}, + { 12484, 0, 354}, + { 12486, 0, 355}, + { 12488, 0, 356}, + { 12495, 0, 357}, + { 12498, 0, 358}, + { 12501, 0, 359}, + { 12504, 0, 360}, + { 12507, 0, 361}, + { 12527, 3, 362}, + { 12541, 0, 366}, + { 69785, 0, 367}, + { 69787, 0, 368}, + { 69797, 0, 369}, + { 69937, 1, 370}, + { 70471, 0, 372}, + { 70841, 0, 373}, + { 71096, 1, 374}, + {0,0,0} +}; + +static const Reindex nfc_last[] = { + { 768, 4, 0}, + { 774, 6, 5}, + { 783, 0, 12}, + { 785, 0, 13}, + { 787, 1, 14}, + { 795, 0, 16}, + { 803, 5, 17}, + { 813, 1, 23}, + { 816, 1, 25}, + { 824, 0, 27}, + { 834, 0, 28}, + { 837, 0, 29}, + { 1619, 2, 30}, + { 2364, 0, 33}, + { 2494, 0, 34}, + { 2519, 0, 35}, + { 2878, 0, 36}, + { 2902, 1, 37}, + { 3006, 0, 39}, + { 3031, 0, 40}, + { 3158, 0, 41}, + { 3266, 0, 42}, + { 3285, 1, 43}, + { 3390, 0, 45}, + { 3415, 0, 46}, + { 3530, 0, 47}, + { 3535, 0, 48}, + { 3551, 0, 49}, + { 4142, 0, 50}, + { 6965, 0, 51}, + { 12441, 1, 52}, + { 69818, 0, 54}, + { 69927, 0, 55}, + { 70462, 0, 56}, + { 70487, 0, 57}, + { 70832, 0, 58}, + { 70842, 0, 59}, + { 70845, 0, 60}, + { 71087, 0, 61}, + {0,0,0} +}; + +#define UCDN_EAST_ASIAN_F 0 +#define UCDN_EAST_ASIAN_H 1 +#define UCDN_EAST_ASIAN_W 2 +#define UCDN_EAST_ASIAN_NA 3 +#define UCDN_EAST_ASIAN_A 4 +#define UCDN_EAST_ASIAN_N 5 + +#define UCDN_SCRIPT_COMMON 0 +#define UCDN_SCRIPT_LATIN 1 +#define UCDN_SCRIPT_GREEK 2 +#define UCDN_SCRIPT_CYRILLIC 3 +#define UCDN_SCRIPT_ARMENIAN 4 +#define UCDN_SCRIPT_HEBREW 5 +#define UCDN_SCRIPT_ARABIC 6 +#define UCDN_SCRIPT_SYRIAC 7 +#define UCDN_SCRIPT_THAANA 8 +#define UCDN_SCRIPT_DEVANAGARI 9 +#define UCDN_SCRIPT_BENGALI 10 +#define UCDN_SCRIPT_GURMUKHI 11 +#define UCDN_SCRIPT_GUJARATI 12 +#define UCDN_SCRIPT_ORIYA 13 +#define UCDN_SCRIPT_TAMIL 14 +#define UCDN_SCRIPT_TELUGU 15 +#define UCDN_SCRIPT_KANNADA 16 +#define UCDN_SCRIPT_MALAYALAM 17 +#define UCDN_SCRIPT_SINHALA 18 +#define UCDN_SCRIPT_THAI 19 +#define UCDN_SCRIPT_LAO 20 +#define UCDN_SCRIPT_TIBETAN 21 +#define UCDN_SCRIPT_MYANMAR 22 +#define UCDN_SCRIPT_GEORGIAN 23 +#define UCDN_SCRIPT_HANGUL 24 +#define UCDN_SCRIPT_ETHIOPIC 25 +#define UCDN_SCRIPT_CHEROKEE 26 +#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 +#define UCDN_SCRIPT_OGHAM 28 +#define UCDN_SCRIPT_RUNIC 29 +#define UCDN_SCRIPT_KHMER 30 +#define UCDN_SCRIPT_MONGOLIAN 31 +#define UCDN_SCRIPT_HIRAGANA 32 +#define UCDN_SCRIPT_KATAKANA 33 +#define UCDN_SCRIPT_BOPOMOFO 34 +#define UCDN_SCRIPT_HAN 35 +#define UCDN_SCRIPT_YI 36 +#define UCDN_SCRIPT_OLD_ITALIC 37 +#define UCDN_SCRIPT_GOTHIC 38 +#define UCDN_SCRIPT_DESERET 39 +#define UCDN_SCRIPT_INHERITED 40 +#define UCDN_SCRIPT_TAGALOG 41 +#define UCDN_SCRIPT_HANUNOO 42 +#define UCDN_SCRIPT_BUHID 43 +#define UCDN_SCRIPT_TAGBANWA 44 +#define UCDN_SCRIPT_LIMBU 45 +#define UCDN_SCRIPT_TAI_LE 46 +#define UCDN_SCRIPT_LINEAR_B 47 +#define UCDN_SCRIPT_UGARITIC 48 +#define UCDN_SCRIPT_SHAVIAN 49 +#define UCDN_SCRIPT_OSMANYA 50 +#define UCDN_SCRIPT_CYPRIOT 51 +#define UCDN_SCRIPT_BRAILLE 52 +#define UCDN_SCRIPT_BUGINESE 53 +#define UCDN_SCRIPT_COPTIC 54 +#define UCDN_SCRIPT_NEW_TAI_LUE 55 +#define UCDN_SCRIPT_GLAGOLITIC 56 +#define UCDN_SCRIPT_TIFINAGH 57 +#define UCDN_SCRIPT_SYLOTI_NAGRI 58 +#define UCDN_SCRIPT_OLD_PERSIAN 59 +#define UCDN_SCRIPT_KHAROSHTHI 60 +#define UCDN_SCRIPT_BALINESE 61 +#define UCDN_SCRIPT_CUNEIFORM 62 +#define UCDN_SCRIPT_PHOENICIAN 63 +#define UCDN_SCRIPT_PHAGS_PA 64 +#define UCDN_SCRIPT_NKO 65 +#define UCDN_SCRIPT_SUNDANESE 66 +#define UCDN_SCRIPT_LEPCHA 67 +#define UCDN_SCRIPT_OL_CHIKI 68 +#define UCDN_SCRIPT_VAI 69 +#define UCDN_SCRIPT_SAURASHTRA 70 +#define UCDN_SCRIPT_KAYAH_LI 71 +#define UCDN_SCRIPT_REJANG 72 +#define UCDN_SCRIPT_LYCIAN 73 +#define UCDN_SCRIPT_CARIAN 74 +#define UCDN_SCRIPT_LYDIAN 75 +#define UCDN_SCRIPT_CHAM 76 +#define UCDN_SCRIPT_TAI_THAM 77 +#define UCDN_SCRIPT_TAI_VIET 78 +#define UCDN_SCRIPT_AVESTAN 79 +#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 +#define UCDN_SCRIPT_SAMARITAN 81 +#define UCDN_SCRIPT_LISU 82 +#define UCDN_SCRIPT_BAMUM 83 +#define UCDN_SCRIPT_JAVANESE 84 +#define UCDN_SCRIPT_MEETEI_MAYEK 85 +#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 +#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 +#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 +#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 +#define UCDN_SCRIPT_OLD_TURKIC 90 +#define UCDN_SCRIPT_KAITHI 91 +#define UCDN_SCRIPT_BATAK 92 +#define UCDN_SCRIPT_BRAHMI 93 +#define UCDN_SCRIPT_MANDAIC 94 +#define UCDN_SCRIPT_CHAKMA 95 +#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 +#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 +#define UCDN_SCRIPT_MIAO 98 +#define UCDN_SCRIPT_SHARADA 99 +#define UCDN_SCRIPT_SORA_SOMPENG 100 +#define UCDN_SCRIPT_TAKRI 101 +#define UCDN_SCRIPT_UNKNOWN 102 +#define UCDN_SCRIPT_BASSA_VAH 103 +#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 +#define UCDN_SCRIPT_DUPLOYAN 105 +#define UCDN_SCRIPT_ELBASAN 106 +#define UCDN_SCRIPT_GRANTHA 107 +#define UCDN_SCRIPT_KHOJKI 108 +#define UCDN_SCRIPT_KHUDAWADI 109 +#define UCDN_SCRIPT_LINEAR_A 110 +#define UCDN_SCRIPT_MAHAJANI 111 +#define UCDN_SCRIPT_MANICHAEAN 112 +#define UCDN_SCRIPT_MENDE_KIKAKUI 113 +#define UCDN_SCRIPT_MODI 114 +#define UCDN_SCRIPT_MRO 115 +#define UCDN_SCRIPT_NABATAEAN 116 +#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 +#define UCDN_SCRIPT_OLD_PERMIC 118 +#define UCDN_SCRIPT_PAHAWH_HMONG 119 +#define UCDN_SCRIPT_PALMYRENE 120 +#define UCDN_SCRIPT_PAU_CIN_HAU 121 +#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 +#define UCDN_SCRIPT_SIDDHAM 123 +#define UCDN_SCRIPT_TIRHUTA 124 +#define UCDN_SCRIPT_WARANG_CITI 125 + +#define UCDN_GENERAL_CATEGORY_CC 0 +#define UCDN_GENERAL_CATEGORY_CF 1 +#define UCDN_GENERAL_CATEGORY_CN 2 +#define UCDN_GENERAL_CATEGORY_CO 3 +#define UCDN_GENERAL_CATEGORY_CS 4 +#define UCDN_GENERAL_CATEGORY_LL 5 +#define UCDN_GENERAL_CATEGORY_LM 6 +#define UCDN_GENERAL_CATEGORY_LO 7 +#define UCDN_GENERAL_CATEGORY_LT 8 +#define UCDN_GENERAL_CATEGORY_LU 9 +#define UCDN_GENERAL_CATEGORY_MC 10 +#define UCDN_GENERAL_CATEGORY_ME 11 +#define UCDN_GENERAL_CATEGORY_MN 12 +#define UCDN_GENERAL_CATEGORY_ND 13 +#define UCDN_GENERAL_CATEGORY_NL 14 +#define UCDN_GENERAL_CATEGORY_NO 15 +#define UCDN_GENERAL_CATEGORY_PC 16 +#define UCDN_GENERAL_CATEGORY_PD 17 +#define UCDN_GENERAL_CATEGORY_PE 18 +#define UCDN_GENERAL_CATEGORY_PF 19 +#define UCDN_GENERAL_CATEGORY_PI 20 +#define UCDN_GENERAL_CATEGORY_PO 21 +#define UCDN_GENERAL_CATEGORY_PS 22 +#define UCDN_GENERAL_CATEGORY_SC 23 +#define UCDN_GENERAL_CATEGORY_SK 24 +#define UCDN_GENERAL_CATEGORY_SM 25 +#define UCDN_GENERAL_CATEGORY_SO 26 +#define UCDN_GENERAL_CATEGORY_ZL 27 +#define UCDN_GENERAL_CATEGORY_ZP 28 +#define UCDN_GENERAL_CATEGORY_ZS 29 + +#define UCDN_BIDI_CLASS_L 0 +#define UCDN_BIDI_CLASS_LRE 1 +#define UCDN_BIDI_CLASS_LRO 2 +#define UCDN_BIDI_CLASS_R 3 +#define UCDN_BIDI_CLASS_AL 4 +#define UCDN_BIDI_CLASS_RLE 5 +#define UCDN_BIDI_CLASS_RLO 6 +#define UCDN_BIDI_CLASS_PDF 7 +#define UCDN_BIDI_CLASS_EN 8 +#define UCDN_BIDI_CLASS_ES 9 +#define UCDN_BIDI_CLASS_ET 10 +#define UCDN_BIDI_CLASS_AN 11 +#define UCDN_BIDI_CLASS_CS 12 +#define UCDN_BIDI_CLASS_NSM 13 +#define UCDN_BIDI_CLASS_BN 14 +#define UCDN_BIDI_CLASS_B 15 +#define UCDN_BIDI_CLASS_S 16 +#define UCDN_BIDI_CLASS_WS 17 +#define UCDN_BIDI_CLASS_ON 18 +#define UCDN_BIDI_CLASS_LRI 19 +#define UCDN_BIDI_CLASS_RLI 20 +#define UCDN_BIDI_CLASS_FSI 21 +#define UCDN_BIDI_CLASS_PDI 22 + +/* index tables for the database records */ +#define SHIFT1 5 +#define SHIFT2 3 +static const unsigned char index0[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 55, 56, 57, 57, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 69, 70, 70, + 71, 69, 70, 70, 72, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 70, 96, 70, 97, + 98, 99, 100, 101, 102, 103, 70, 104, 70, 105, 70, 70, 70, 70, 70, 106, + 106, 106, 107, 108, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 109, 109, + 109, 109, 110, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 111, 111, 112, 113, 70, 70, 70, 114, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 115, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 116, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 117, 118, + 119, 120, 121, 122, 123, 124, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 125, 70, 70, 70, 70, 70, 126, 70, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 70, 70, 70, 70, 70, 70, 70, 52, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 136, + 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 137, 138, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 76, 76, 140, 139, 139, 139, 139, 141, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 141, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 142, 143, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 73, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 144, 73, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 144, +}; + +static const unsigned short index1[] = { + 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 0, 0, 0, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 31, 32, + 33, 34, 35, 27, 30, 29, 27, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 27, 27, 49, 27, 27, 27, 27, 27, 27, 27, 50, 51, 52, 27, 53, 54, + 53, 54, 54, 54, 54, 54, 55, 54, 54, 54, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 65, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 97, 97, 97, 98, 98, 98, 98, 99, 100, 101, 101, 101, 101, 102, 103, + 101, 101, 101, 101, 101, 101, 104, 105, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 106, 107, 107, 107, 108, 109, 110, 111, + 111, 111, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 121, + 121, 122, 123, 120, 124, 125, 126, 127, 128, 128, 128, 128, 129, 130, + 131, 132, 133, 134, 135, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 145, 145, + 146, 147, 148, 149, 128, 128, 128, 128, 128, 128, 150, 150, 150, 150, + 151, 152, 153, 120, 154, 155, 156, 156, 156, 157, 158, 159, 160, 160, + 161, 162, 163, 164, 165, 166, 167, 167, 167, 168, 120, 120, 120, 120, + 120, 120, 120, 120, 128, 128, 169, 120, 120, 120, 120, 120, 170, 171, + 172, 173, 174, 175, 175, 175, 175, 175, 175, 176, 177, 178, 179, 175, + 180, 181, 182, 175, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 120, 212, 213, 214, 215, 215, 216, + 217, 218, 219, 220, 221, 120, 222, 223, 224, 120, 225, 226, 227, 228, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 120, 239, 240, + 241, 242, 243, 240, 244, 245, 246, 247, 248, 120, 249, 250, 251, 252, + 253, 254, 255, 256, 256, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 120, 264, 265, 266, 267, 268, 268, 267, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 120, 278, 279, 280, 281, 281, 281, 281, 282, 283, 284, + 285, 120, 286, 287, 288, 289, 290, 291, 292, 293, 291, 291, 294, 295, + 292, 296, 297, 298, 299, 300, 301, 120, 302, 303, 303, 303, 303, 303, + 304, 305, 306, 307, 308, 309, 120, 120, 120, 120, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 120, 120, 120, 120, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 330, 330, 330, 332, 333, 334, + 335, 336, 337, 338, 337, 337, 337, 339, 340, 341, 342, 343, 120, 120, + 120, 120, 344, 344, 344, 344, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 344, 355, 356, 348, 357, 358, 358, 358, 358, 359, 360, + 361, 361, 361, 361, 361, 362, 363, 363, 363, 363, 363, 363, 363, 363, + 363, 363, 363, 363, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 366, 367, 366, 365, 365, 365, 365, 365, 366, + 365, 365, 365, 365, 366, 367, 366, 365, 367, 365, 365, 365, 365, 365, + 365, 365, 366, 365, 365, 365, 365, 365, 365, 365, 365, 368, 369, 370, + 371, 372, 365, 365, 373, 374, 375, 375, 375, 375, 375, 375, 375, 375, + 375, 375, 376, 120, 377, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 379, 378, 378, + 380, 381, 381, 382, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, + 385, 386, 387, 388, 389, 120, 390, 390, 391, 120, 392, 392, 393, 120, + 394, 395, 396, 120, 397, 397, 397, 397, 397, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 412, 412, 412, + 413, 412, 412, 412, 412, 412, 412, 120, 412, 412, 412, 412, 412, 414, + 378, 378, 378, 378, 378, 378, 378, 378, 415, 120, 416, 416, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 424, 424, 425, 426, 120, 427, 427, + 427, 427, 427, 428, 429, 429, 430, 431, 432, 433, 434, 434, 434, 434, + 435, 435, 436, 437, 438, 438, 438, 438, 438, 438, 439, 440, 441, 442, + 443, 444, 445, 446, 445, 446, 447, 448, 449, 450, 120, 120, 120, 120, + 120, 120, 120, 120, 451, 452, 452, 452, 452, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 464, 464, 465, 466, 467, 468, + 469, 469, 469, 469, 470, 471, 472, 473, 474, 474, 474, 474, 475, 476, + 477, 478, 479, 480, 481, 482, 483, 483, 483, 484, 120, 120, 120, 120, + 120, 120, 120, 120, 485, 120, 486, 487, 488, 489, 490, 491, 54, 54, 54, + 54, 492, 493, 56, 56, 56, 56, 56, 494, 495, 496, 54, 497, 54, 54, 54, + 498, 56, 56, 56, 499, 500, 501, 502, 503, 503, 503, 504, 505, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 506, 507, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 508, 509, 510, 511, 508, 509, + 508, 509, 510, 511, 508, 512, 508, 509, 508, 510, 508, 513, 508, 513, + 508, 513, 514, 515, 516, 517, 518, 519, 508, 520, 521, 522, 523, 524, + 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, + 539, 540, 56, 541, 542, 543, 542, 544, 120, 120, 545, 546, 547, 548, 549, + 120, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, + 563, 562, 564, 565, 566, 567, 568, 569, 570, 571, 572, 571, 573, 574, + 571, 575, 571, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, + 587, 588, 589, 590, 591, 586, 586, 592, 593, 594, 595, 596, 586, 586, + 597, 577, 598, 599, 586, 586, 600, 586, 586, 571, 601, 602, 571, 603, + 604, 605, 606, 606, 606, 606, 606, 606, 606, 606, 607, 571, 571, 608, + 609, 577, 577, 610, 571, 571, 571, 571, 576, 611, 571, 571, 612, 571, + 571, 571, 571, 613, 120, 120, 120, 571, 612, 120, 120, 614, 614, 614, + 614, 614, 615, 615, 616, 617, 617, 617, 617, 617, 617, 617, 617, 617, + 618, 614, 614, 619, 619, 619, 619, 619, 619, 619, 619, 619, 620, 619, + 619, 619, 619, 620, 571, 619, 619, 621, 571, 622, 572, 623, 624, 625, + 626, 572, 571, 621, 575, 571, 577, 627, 628, 624, 629, 571, 571, 571, + 571, 630, 571, 571, 571, 631, 632, 571, 571, 571, 571, 571, 633, 571, + 634, 571, 633, 635, 636, 619, 619, 637, 619, 619, 619, 571, 571, 571, + 571, 571, 571, 571, 638, 571, 571, 575, 571, 571, 639, 640, 614, 641, + 641, 642, 571, 571, 571, 571, 571, 643, 644, 645, 646, 647, 648, 577, + 577, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, + 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, + 649, 649, 649, 649, 649, 577, 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 650, 651, 651, 652, 586, 586, 577, + 653, 600, 654, 655, 656, 657, 658, 659, 660, 577, 661, 586, 662, 663, + 664, 665, 646, 577, 577, 589, 653, 665, 666, 667, 668, 586, 586, 586, + 586, 669, 670, 586, 586, 586, 586, 671, 672, 673, 646, 674, 675, 571, + 571, 571, 571, 571, 571, 577, 577, 676, 677, 678, 572, 571, 571, 679, + 571, 571, 571, 680, 571, 571, 571, 571, 681, 571, 682, 683, 120, 120, + 120, 120, 120, 684, 684, 684, 684, 684, 685, 686, 686, 686, 686, 686, + 687, 688, 689, 690, 691, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 692, 693, 694, 695, 696, 696, 696, 696, 697, 698, 699, 699, 699, 699, + 699, 699, 699, 700, 701, 702, 365, 365, 367, 120, 367, 367, 367, 367, + 367, 367, 367, 367, 703, 703, 703, 703, 704, 705, 706, 707, 708, 709, + 532, 710, 711, 120, 120, 120, 120, 120, 120, 120, 712, 712, 712, 713, + 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 714, 120, 712, 712, + 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, + 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 715, 120, 120, 120, + 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 727, 727, + 727, 727, 727, 727, 727, 727, 728, 729, 730, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 732, 733, 734, 734, 734, 734, 735, 736, 363, + 363, 363, 363, 363, 363, 363, 363, 363, 363, 737, 738, 739, 734, 734, + 734, 740, 716, 716, 716, 716, 717, 120, 731, 731, 741, 741, 741, 742, + 743, 744, 739, 739, 739, 745, 746, 747, 741, 741, 741, 748, 743, 744, + 739, 739, 739, 739, 749, 747, 739, 750, 751, 751, 751, 751, 751, 752, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 739, 739, 739, + 753, 754, 739, 739, 739, 739, 739, 739, 739, 739, 739, 739, 739, 755, + 739, 739, 739, 753, 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 758, 759, 571, 571, 571, 571, 571, 571, + 571, 571, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 760, + 759, 759, 759, 759, 759, 759, 761, 761, 762, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 763, 764, 764, 764, 764, 764, 764, 765, 120, 766, 766, + 766, 766, 766, 767, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, + 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, + 768, 768, 768, 768, 768, 768, 768, 768, 768, 769, 768, 768, 770, 771, + 120, 120, 101, 101, 101, 101, 101, 772, 773, 774, 101, 101, 101, 775, + 776, 776, 776, 776, 776, 776, 776, 776, 777, 778, 779, 120, 64, 64, 780, + 781, 782, 27, 783, 27, 27, 27, 27, 27, 27, 27, 784, 785, 27, 786, 787, + 27, 27, 788, 789, 120, 120, 120, 120, 120, 120, 120, 790, 791, 792, 793, + 794, 794, 795, 796, 797, 798, 799, 799, 799, 799, 799, 799, 800, 120, + 801, 802, 802, 802, 802, 802, 803, 804, 805, 806, 807, 808, 809, 809, + 810, 811, 812, 813, 814, 814, 815, 816, 817, 817, 818, 819, 820, 821, + 363, 363, 363, 822, 823, 824, 824, 824, 824, 824, 825, 826, 827, 828, + 829, 830, 831, 344, 348, 832, 833, 833, 833, 833, 833, 834, 835, 120, + 836, 837, 838, 839, 344, 344, 840, 841, 842, 842, 842, 842, 842, 842, + 843, 844, 845, 120, 120, 846, 847, 848, 849, 120, 850, 850, 850, 120, + 367, 367, 54, 54, 54, 54, 54, 851, 852, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 847, 847, 847, 847, 853, 854, 855, 856, 857, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, + 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 858, 859, + 120, 364, 364, 860, 861, 364, 364, 364, 364, 364, 862, 863, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 864, 863, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 864, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 864, 865, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 867, 868, 868, 868, + 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 869, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 870, 759, 759, 759, 759, 871, 120, 872, 873, 121, 874, 875, 876, 877, + 121, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 878, + 879, 880, 120, 881, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 882, 120, 120, 128, 128, 128, 128, 128, + 128, 128, 128, 883, 128, 128, 128, 128, 128, 128, 120, 120, 120, 120, + 120, 128, 884, 885, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, + 895, 896, 897, 898, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 899, 900, 901, 902, 903, 904, 905, 905, + 906, 907, 908, 908, 909, 910, 911, 912, 911, 911, 911, 911, 913, 914, + 914, 914, 915, 916, 916, 916, 917, 918, 919, 120, 920, 921, 922, 921, + 921, 923, 921, 921, 924, 921, 925, 921, 925, 120, 120, 120, 120, 921, + 921, 921, 921, 921, 921, 921, 921, 921, 921, 921, 921, 921, 921, 921, + 926, 927, 928, 928, 928, 928, 928, 929, 606, 930, 930, 930, 930, 930, + 930, 931, 932, 933, 934, 571, 935, 936, 120, 120, 120, 120, 120, 606, + 606, 606, 606, 606, 937, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 938, 938, 938, 939, 940, 940, 940, + 940, 940, 940, 941, 120, 942, 943, 943, 944, 945, 945, 945, 945, 946, + 120, 947, 947, 948, 949, 950, 950, 950, 950, 951, 952, 953, 953, 953, + 954, 955, 955, 955, 955, 956, 955, 957, 120, 120, 120, 120, 120, 958, + 958, 958, 958, 958, 959, 959, 959, 959, 959, 960, 960, 960, 960, 960, + 960, 961, 961, 961, 962, 963, 964, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 965, 965, 965, 965, 965, 120, 966, 966, 966, 966, 966, + 966, 967, 968, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 969, 969, 969, 969, 969, 969, 969, + 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + 969, 969, 969, 970, 120, 969, 969, 971, 120, 969, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 972, 973, 974, 974, 974, 974, 975, 976, 977, 977, 978, 979, 980, + 980, 981, 982, 983, 983, 983, 984, 985, 986, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 987, 987, 988, 989, 990, 990, 990, 991, 120, + 120, 120, 120, 120, 120, 120, 120, 992, 992, 992, 992, 993, 993, 993, + 994, 120, 120, 120, 120, 120, 120, 120, 120, 995, 996, 997, 998, 999, + 999, 1000, 1001, 1002, 120, 1003, 1004, 1005, 1005, 1005, 1006, 1007, + 1007, 1007, 1008, 120, 120, 120, 120, 1009, 1010, 1009, 1009, 1011, 1012, + 1013, 120, 1014, 1014, 1014, 1014, 1014, 1014, 1015, 1016, 1017, 1017, + 1018, 1019, 1020, 1020, 1021, 1022, 1023, 1023, 1024, 1025, 120, 1026, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1027, 1027, 1027, 1027, + 1027, 1027, 1027, 1027, 1027, 1028, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1029, + 1029, 1029, 1030, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 1031, 1032, 1032, 1032, 1032, 1032, 1032, 1033, + 1034, 1035, 1036, 1037, 1038, 1039, 120, 1040, 1041, 1042, 1042, 1042, + 1042, 1042, 1043, 1044, 1045, 120, 1046, 1046, 1046, 1047, 1048, 1049, + 1050, 1051, 1051, 1051, 1052, 1053, 1054, 1055, 1056, 120, 1057, 1057, + 1057, 1057, 1058, 120, 1059, 1060, 1060, 1060, 1060, 1060, 1061, 1062, + 1063, 1064, 1065, 1066, 1067, 1068, 1069, 120, 1070, 1070, 1071, 1070, + 1070, 1072, 1073, 1074, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 1075, 1075, 1075, 1075, 1075, 1076, 1077, 1078, 1079, + 1080, 1081, 1082, 1083, 1084, 1084, 1085, 1086, 1087, 1088, 1089, 1090, + 1091, 1092, 1093, 1093, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 1094, 1094, 1094, 1094, + 1094, 1094, 1095, 1096, 1097, 120, 1098, 1099, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 1100, 1100, 1100, 1100, 1100, 1101, 1102, 1103, 1104, 1105, 120, + 120, 120, 120, 120, 120, 1106, 1106, 1106, 1106, 1106, 1106, 1107, 1108, + 1109, 120, 1110, 1111, 120, 120, 120, 120, 1112, 1112, 1112, 1112, 1112, + 1113, 1114, 120, 1115, 1116, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 1117, 1117, 1117, 1117, 1118, 1118, 1118, 1118, 1119, + 1120, 1121, 1122, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1124, 1125, 1125, 1125, 1125, 1125, + 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, + 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, + 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, + 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1126, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1127, 1127, 1127, + 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1128, 1129, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, + 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, + 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, + 1130, 1130, 1130, 1130, 1131, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, + 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, + 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, + 776, 1132, 1133, 1133, 1133, 1134, 1135, 1136, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 1137, 1137, 1137, 1138, 1139, 120, + 1140, 1140, 1140, 1140, 1140, 1140, 1141, 1142, 1143, 120, 1144, 1145, + 1146, 1140, 1140, 1147, 1140, 1140, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 1148, 1148, 1148, 1148, 1148, 1148, + 1148, 1148, 1149, 120, 1150, 1151, 1151, 1151, 1151, 1152, 120, 1153, + 1154, 1155, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 1156, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, + 1157, 1157, 1157, 1157, 1158, 1157, 1159, 1157, 1160, 1157, 1161, 1162, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 606, 606, 606, + 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, + 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 1163, + 120, 606, 606, 606, 606, 1164, 1165, 606, 606, 606, 606, 606, 606, 1166, + 1167, 1168, 1169, 1170, 1171, 606, 606, 606, 1172, 606, 606, 606, 606, + 606, 1163, 120, 120, 120, 120, 933, 933, 933, 933, 933, 933, 933, 933, + 1173, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 571, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 613, 120, 928, 928, 1174, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 1175, 1175, 1175, 1176, 1177, 1177, 1178, 1175, 1175, 1179, 1180, 1177, + 1177, 1175, 1175, 1175, 1176, 1177, 1177, 1181, 1182, 1183, 1179, 1184, + 1185, 1177, 1175, 1175, 1175, 1176, 1177, 1177, 1186, 1187, 1188, 1189, + 1177, 1177, 1177, 1190, 1191, 1192, 1193, 1177, 1177, 1178, 1175, 1175, + 1179, 1177, 1177, 1177, 1175, 1175, 1175, 1176, 1177, 1177, 1178, 1175, + 1175, 1179, 1177, 1177, 1177, 1175, 1175, 1175, 1176, 1177, 1177, 1178, + 1175, 1175, 1179, 1177, 1177, 1177, 1175, 1175, 1175, 1176, 1177, 1177, + 1194, 1175, 1175, 1175, 1195, 1177, 1177, 1196, 1197, 1175, 1175, 1198, + 1177, 1177, 1199, 1178, 1175, 1175, 1200, 1177, 1177, 1201, 1202, 1175, + 1175, 1203, 1177, 1177, 1177, 1204, 1175, 1175, 1175, 1195, 1177, 1177, + 1196, 1205, 1206, 1206, 1206, 1206, 1206, 1206, 1207, 1207, 1207, 1207, + 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, + 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1207, 1208, 1209, 1210, 120, + 120, 120, 120, 120, 1211, 128, 128, 128, 1212, 1213, 1214, 1215, 1216, + 1217, 1212, 1218, 1212, 1214, 1214, 1219, 128, 1220, 128, 1221, 1222, + 1220, 128, 1221, 120, 120, 120, 120, 120, 120, 1223, 120, 571, 571, 571, + 571, 571, 935, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 935, 120, 571, 613, 1224, 571, 1224, 571, 1224, 571, 571, 571, 680, + 120, 615, 1225, 617, 617, 617, 1226, 617, 617, 617, 617, 617, 617, 617, + 1227, 617, 617, 617, 617, 617, 1228, 120, 120, 120, 120, 120, 120, 120, + 120, 1229, 606, 606, 606, 1230, 120, 739, 739, 739, 739, 739, 1231, 739, + 1232, 1233, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 571, 571, 571, 571, 571, + 1234, 571, 571, 571, 571, 571, 571, 571, 571, 571, 680, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 613, 1235, 571, 571, 571, 571, 120, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 571, 613, 571, 571, 571, 571, 571, 571, 571, 571, 571, 612, 571, + 571, 571, 571, 571, 1236, 571, 571, 571, 571, 1237, 571, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 1238, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 571, 571, 571, 571, 120, 120, 571, 1234, 935, 120, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 935, 120, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 571, 1234, 120, 120, 120, 120, + 120, 571, 935, 571, 571, 571, 571, 571, 571, 571, 120, 571, 683, 571, + 571, 571, 571, 571, 120, 571, 571, 571, 680, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 1239, 759, 759, 759, 759, 759, 757, 757, 757, 757, 757, + 757, 760, 759, 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 758, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 868, 868, 868, 869, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, + 1240, 1241, 120, 120, 120, 1242, 1242, 1242, 1242, 1242, 1242, 1242, + 1242, 1242, 1242, 1242, 1242, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 885, 885, 885, 885, 885, 885, + 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, + 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 120, 120, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 1243, +}; + +static const unsigned short index2[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 2, + 5, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, 12, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 12, 6, 15, 16, 15, 6, 6, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 9, 6, 10, 18, 19, 18, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 16, + 10, 16, 1, 1, 1, 1, 1, 1, 3, 1, 1, 21, 22, 8, 8, 23, 8, 24, 22, 25, 26, + 27, 28, 16, 29, 30, 18, 31, 32, 33, 33, 25, 34, 22, 22, 25, 33, 27, 35, + 36, 36, 36, 22, 37, 37, 37, 37, 37, 37, 38, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 38, 37, 37, 37, 37, 37, 37, 39, 38, 37, 37, 37, 37, 37, 38, 40, + 40, 40, 41, 41, 41, 41, 40, 41, 40, 40, 40, 41, 40, 40, 41, 41, 40, 41, + 40, 40, 41, 41, 41, 39, 40, 40, 40, 41, 40, 41, 40, 41, 37, 40, 37, 41, + 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 37, 40, 37, 40, 37, 41, + 37, 41, 37, 41, 37, 40, 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 38, 40, + 37, 40, 38, 40, 37, 41, 37, 41, 40, 37, 41, 37, 41, 37, 41, 38, 40, 38, + 40, 37, 40, 37, 41, 37, 40, 40, 38, 40, 37, 40, 37, 41, 37, 41, 38, 40, + 37, 41, 37, 41, 37, 37, 41, 37, 41, 37, 41, 41, 41, 37, 37, 41, 37, 41, + 37, 37, 41, 37, 37, 37, 41, 41, 37, 37, 37, 37, 41, 37, 37, 41, 37, 37, + 37, 41, 41, 41, 37, 37, 41, 37, 37, 41, 37, 41, 37, 41, 37, 37, 41, 37, + 41, 41, 37, 41, 37, 37, 41, 37, 37, 37, 41, 37, 41, 37, 37, 41, 41, 42, + 37, 41, 41, 41, 42, 42, 42, 42, 37, 43, 41, 37, 43, 41, 37, 43, 41, 37, + 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 41, 37, 41, + 41, 37, 43, 41, 37, 41, 37, 37, 37, 41, 37, 41, 41, 41, 41, 41, 41, 41, + 37, 37, 41, 37, 37, 41, 41, 37, 41, 37, 37, 37, 37, 41, 41, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 41, + 41, 41, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 46, 46, 46, 46, 46, + 46, 46, 47, 47, 25, 47, 45, 48, 45, 48, 48, 48, 45, 48, 45, 45, 49, 46, + 47, 47, 47, 47, 47, 47, 25, 25, 25, 25, 47, 25, 47, 25, 44, 44, 44, 44, + 44, 47, 47, 47, 47, 47, 50, 50, 45, 47, 46, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, 53, + 53, 53, 52, 54, 53, 53, 53, 53, 53, 55, 55, 53, 53, 53, 53, 55, 55, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 56, 56, 56, 56, 56, 53, 53, 53, + 53, 51, 51, 51, 51, 51, 51, 51, 51, 57, 51, 53, 53, 53, 51, 51, 51, 53, + 53, 58, 51, 51, 51, 53, 53, 53, 53, 51, 52, 53, 53, 51, 59, 60, 60, 59, + 60, 60, 59, 51, 51, 51, 51, 51, 61, 62, 61, 62, 45, 63, 61, 62, 64, 64, + 65, 62, 62, 62, 66, 61, 64, 64, 64, 64, 63, 47, 61, 66, 61, 61, 61, 64, + 61, 64, 61, 61, 62, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 64, 67, 67, 67, 67, 67, 67, 67, 61, 61, 62, 62, 62, 62, + 62, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 62, 68, 68, 68, 68, 68, 68, 68, 62, 62, 62, 62, 62, 61, 62, 62, 61, 61, + 61, 62, 62, 62, 61, 62, 61, 62, 61, 62, 61, 62, 61, 62, 69, 70, 69, 70, + 69, 70, 69, 70, 69, 70, 69, 70, 69, 70, 62, 62, 62, 62, 61, 62, 71, 61, + 62, 61, 61, 62, 62, 61, 61, 61, 72, 73, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, + 74, 74, 74, 74, 75, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 72, 75, 72, 75, 72, 75, 72, 75, 72, 75, 76, 77, 77, 78, 78, 77, + 79, 79, 72, 75, 72, 75, 72, 75, 72, 72, 75, 72, 75, 72, 75, 72, 75, 72, + 75, 72, 75, 72, 75, 75, 64, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 64, 64, 81, 82, 82, 82, 82, + 82, 82, 64, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 64, 84, 85, 64, 64, 86, 86, 87, 64, 88, 89, 89, 89, 89, 88, 89, 89, 89, + 90, 88, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 89, 89, 88, 89, + 89, 90, 91, 89, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 107, 89, 88, 107, 100, 64, 64, 64, 64, 64, + 64, 64, 64, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 64, + 64, 64, 64, 64, 110, 110, 110, 107, 107, 64, 64, 64, 111, 111, 111, 111, + 111, 112, 113, 113, 114, 115, 115, 116, 117, 118, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 121, 122, 123, 124, 125, 64, 118, 124, 126, + 126, 126, 126, 126, 126, 126, 126, 127, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 128, 129, 130, 131, 132, 133, 134, 135, 78, 78, 136, + 137, 120, 120, 120, 120, 120, 137, 120, 120, 137, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 115, 139, 139, 118, 126, 126, 140, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 118, 126, 120, 120, + 120, 120, 120, 120, 120, 112, 119, 120, 120, 120, 120, 137, 120, 141, + 141, 120, 120, 119, 137, 120, 120, 137, 126, 126, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 126, 126, 126, 143, 143, 126, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 64, 145, 146, + 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 148, 149, 148, 148, 149, 148, 148, 149, 149, 149, 148, 149, 149, + 148, 149, 148, 148, 148, 149, 148, 149, 148, 149, 148, 149, 148, 148, 64, + 64, 146, 146, 146, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 150, 64, 64, 64, 64, 64, 64, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 155, 154, 156, + 156, 157, 158, 158, 158, 156, 64, 64, 64, 64, 64, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, + 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 160, 160, 160, + 161, 160, 160, 160, 160, 160, 64, 64, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 64, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 164, 164, 164, 64, 64, 165, 64, 126, 126, 126, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 120, 120, 137, 120, 120, 137, 120, 120, 120, 137, + 137, 137, 166, 167, 168, 120, 120, 120, 137, 120, 120, 137, 137, 120, + 120, 120, 120, 120, 169, 169, 169, 170, 171, 171, 171, 171, 171, 171, + 171, 171, 171, 171, 171, 171, 171, 171, 169, 170, 172, 171, 170, 170, + 170, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 173, + 170, 170, 171, 78, 136, 174, 174, 169, 169, 169, 171, 171, 169, 169, 84, + 84, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 177, 171, 171, + 171, 171, 171, 171, 178, 179, 180, 180, 64, 178, 178, 178, 178, 178, 178, + 178, 178, 64, 64, 178, 178, 64, 64, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 64, 178, 178, 178, 178, 178, 178, 178, + 64, 178, 64, 64, 64, 178, 178, 178, 178, 64, 64, 181, 178, 180, 180, 180, + 179, 179, 179, 179, 64, 64, 180, 180, 64, 64, 180, 180, 182, 178, 64, 64, + 64, 64, 64, 64, 64, 64, 180, 64, 64, 64, 64, 178, 178, 64, 178, 178, 178, + 179, 179, 64, 64, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 178, + 178, 184, 184, 185, 185, 185, 185, 185, 185, 186, 184, 64, 64, 64, 64, + 64, 187, 187, 188, 64, 189, 189, 189, 189, 189, 189, 64, 64, 64, 64, 189, + 189, 64, 64, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 189, 64, 189, 189, 189, 189, 189, 189, 189, 64, 189, 189, 64, 189, + 189, 64, 189, 189, 64, 64, 190, 64, 188, 188, 188, 187, 187, 64, 64, 64, + 64, 187, 187, 64, 64, 187, 187, 191, 64, 64, 64, 187, 64, 64, 64, 64, 64, + 64, 64, 189, 189, 189, 189, 64, 189, 64, 64, 64, 64, 64, 64, 64, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 187, 187, 189, 189, 189, + 187, 64, 64, 64, 193, 193, 194, 64, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 64, 195, 195, 195, 64, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 64, 195, 195, 195, 195, 195, 195, 195, 64, + 195, 195, 64, 195, 195, 195, 195, 195, 64, 64, 196, 195, 194, 194, 194, + 193, 193, 193, 193, 193, 64, 193, 193, 194, 64, 194, 194, 197, 64, 64, + 195, 64, 64, 64, 64, 64, 64, 64, 195, 195, 193, 193, 64, 64, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 199, 200, 64, 64, 64, 64, 64, 64, + 64, 201, 202, 202, 64, 203, 203, 203, 203, 203, 203, 203, 203, 64, 64, + 203, 203, 64, 64, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 64, 203, 203, 203, 203, 203, 203, 203, 64, 203, 203, 64, + 203, 203, 203, 203, 203, 64, 64, 204, 203, 202, 201, 202, 201, 201, 201, + 201, 64, 64, 202, 202, 64, 64, 202, 202, 205, 64, 64, 64, 64, 64, 64, 64, + 64, 201, 202, 64, 64, 64, 64, 203, 203, 64, 203, 203, 203, 201, 201, 64, + 64, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 207, 203, 208, 208, + 208, 208, 208, 208, 64, 64, 209, 210, 64, 210, 210, 210, 210, 210, 210, + 64, 64, 64, 210, 210, 210, 64, 210, 210, 210, 210, 64, 64, 64, 210, 210, + 64, 210, 64, 210, 210, 64, 64, 64, 210, 210, 64, 64, 64, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 64, 64, 64, 64, 211, 211, 209, 211, + 211, 64, 64, 64, 211, 211, 211, 64, 211, 211, 211, 212, 64, 64, 210, 64, + 64, 64, 64, 64, 64, 211, 64, 64, 64, 64, 64, 64, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 214, 214, 214, 215, 215, 215, 215, 215, 215, + 216, 215, 64, 64, 64, 64, 64, 217, 218, 218, 218, 64, 219, 219, 219, 219, + 219, 219, 219, 219, 64, 219, 219, 219, 64, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 64, 64, 64, 219, 217, + 217, 217, 218, 218, 218, 218, 64, 217, 217, 217, 64, 217, 217, 217, 220, + 64, 64, 64, 64, 64, 64, 64, 221, 222, 64, 219, 219, 64, 64, 64, 64, 64, + 64, 219, 219, 217, 217, 64, 64, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 224, 224, 224, 224, 224, 224, 224, 225, 64, 226, 227, 227, 64, + 228, 228, 228, 228, 228, 228, 228, 228, 64, 228, 228, 228, 64, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 64, 228, 228, 228, 228, 228, 64, 64, 229, 228, 227, 230, 227, + 227, 227, 227, 227, 64, 230, 227, 227, 64, 227, 227, 226, 231, 64, 64, + 64, 64, 64, 64, 64, 227, 227, 64, 64, 64, 64, 64, 64, 64, 228, 64, 228, + 228, 226, 226, 64, 64, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 64, 228, 228, 64, 64, 64, 64, 64, 64, 233, 234, 234, 64, 235, 235, 235, + 235, 235, 235, 235, 235, 64, 235, 235, 235, 64, 235, 235, 235, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 64, 64, 235, + 234, 234, 234, 233, 233, 233, 233, 64, 234, 234, 234, 64, 234, 234, 234, + 236, 235, 64, 64, 64, 64, 64, 64, 64, 64, 234, 235, 235, 233, 233, 64, + 64, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 238, 238, 238, 238, + 238, 238, 64, 64, 64, 239, 235, 235, 235, 235, 235, 235, 64, 64, 240, + 240, 64, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, + 241, 241, 241, 241, 241, 64, 64, 64, 241, 241, 241, 241, 241, 241, 241, + 241, 64, 241, 241, 241, 241, 241, 241, 241, 241, 241, 64, 241, 64, 64, + 64, 64, 242, 64, 64, 64, 64, 240, 240, 240, 243, 243, 243, 64, 243, 64, + 240, 240, 240, 240, 240, 240, 240, 240, 64, 64, 64, 64, 64, 64, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 64, 64, 240, 240, 245, 64, 64, + 64, 64, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 247, 246, 246, 247, 247, 247, 247, 248, 248, 249, 64, 64, + 64, 64, 250, 246, 246, 246, 246, 246, 246, 251, 247, 252, 252, 252, 252, + 247, 247, 247, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 253, 253, 64, 64, 64, 64, 64, 255, 255, 64, 255, 64, 64, 255, 255, 64, + 255, 64, 64, 255, 64, 64, 64, 64, 64, 64, 255, 255, 255, 255, 64, 255, + 255, 255, 255, 255, 255, 255, 64, 255, 255, 255, 64, 255, 64, 255, 64, + 64, 255, 255, 64, 255, 255, 255, 255, 256, 255, 255, 256, 256, 256, 256, + 257, 257, 64, 256, 256, 255, 64, 64, 255, 255, 255, 255, 255, 64, 258, + 64, 259, 259, 259, 259, 256, 256, 64, 64, 260, 260, 260, 260, 260, 260, + 260, 260, 260, 260, 64, 64, 255, 255, 255, 255, 261, 262, 262, 262, 263, + 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, + 262, 263, 262, 262, 262, 264, 264, 262, 262, 262, 262, 262, 262, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 262, 264, 262, 264, 262, 267, 268, 269, 268, + 269, 270, 270, 261, 261, 261, 261, 261, 261, 261, 261, 64, 261, 261, 261, + 261, 261, 261, 261, 261, 261, 261, 261, 261, 64, 64, 64, 64, 271, 272, + 273, 274, 273, 273, 273, 273, 273, 272, 272, 272, 272, 273, 270, 272, + 273, 275, 275, 276, 263, 275, 275, 261, 261, 261, 261, 261, 273, 273, + 273, 273, 273, 273, 273, 273, 273, 273, 273, 64, 273, 273, 273, 273, 273, + 273, 273, 273, 273, 273, 273, 273, 64, 262, 262, 262, 262, 262, 262, 262, + 262, 264, 262, 262, 262, 262, 262, 262, 64, 262, 262, 263, 263, 263, 263, + 263, 277, 277, 277, 277, 263, 263, 64, 64, 64, 64, 64, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 280, 280, 280, 280, + 279, 280, 280, 280, 280, 280, 281, 279, 282, 282, 279, 279, 280, 280, + 278, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, + 284, 284, 284, 278, 278, 278, 278, 278, 278, 279, 279, 280, 280, 278, + 278, 278, 278, 280, 280, 280, 278, 279, 279, 279, 278, 278, 279, 279, + 279, 279, 279, 279, 279, 278, 278, 278, 280, 280, 280, 280, 278, 278, + 278, 278, 278, 280, 279, 279, 280, 280, 279, 279, 279, 279, 279, 279, + 285, 278, 279, 283, 283, 279, 279, 279, 280, 286, 286, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 64, 287, 64, 64, + 64, 64, 64, 287, 64, 64, 288, 288, 288, 288, 288, 288, 288, 288, 288, + 288, 288, 84, 289, 288, 288, 288, 290, 290, 290, 290, 290, 290, 290, 290, + 291, 291, 291, 291, 291, 291, 291, 291, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 64, 292, 292, 292, 292, 64, 64, 292, 292, 292, 292, 292, + 292, 292, 64, 292, 292, 292, 64, 64, 293, 293, 293, 294, 294, 294, 294, + 294, 294, 294, 294, 294, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 64, 64, 64, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 64, 64, 64, 64, 64, 64, 297, + 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, 64, 64, 64, + 298, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, + 299, 299, 299, 299, 299, 299, 299, 300, 300, 299, 301, 302, 302, 302, + 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, + 302, 303, 304, 64, 64, 64, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 84, 84, 84, 306, 306, 306, 305, 305, 305, 305, 305, 305, 305, + 305, 64, 64, 64, 64, 64, 64, 64, 307, 307, 307, 307, 307, 307, 307, 307, + 307, 307, 307, 307, 307, 64, 307, 307, 307, 307, 308, 308, 309, 64, 64, + 64, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 311, 311, 312, 84, + 84, 64, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 314, 314, 64, + 64, 64, 64, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 64, 315, 315, 315, 64, 316, 316, 64, 64, 64, 64, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 318, 318, 319, 318, 318, 318, + 318, 318, 318, 318, 319, 319, 319, 319, 319, 319, 319, 319, 318, 319, + 319, 318, 318, 318, 318, 318, 318, 318, 318, 318, 320, 318, 321, 321, + 321, 322, 321, 321, 321, 323, 317, 324, 64, 64, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 64, 64, 64, 64, 64, 64, 326, 326, 326, 326, 326, + 326, 326, 326, 326, 326, 64, 64, 64, 64, 64, 64, 327, 327, 66, 66, 327, + 66, 328, 327, 327, 327, 327, 329, 329, 329, 330, 64, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 64, 64, 64, 64, 64, 64, 332, 332, 332, 332, + 332, 332, 332, 332, 332, 332, 332, 333, 332, 332, 332, 332, 332, 334, + 332, 64, 64, 64, 64, 64, 299, 299, 299, 299, 299, 299, 64, 64, 335, 335, + 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 64, 336, + 336, 336, 337, 337, 337, 337, 336, 336, 337, 337, 337, 64, 64, 64, 64, + 337, 337, 336, 337, 337, 337, 337, 337, 337, 338, 339, 340, 64, 64, 64, + 64, 341, 64, 64, 64, 342, 342, 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, + 344, 344, 64, 64, 344, 344, 344, 344, 344, 64, 64, 64, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, 345, 64, 64, 64, 64, 346, 346, + 346, 346, 346, 346, 346, 346, 346, 345, 345, 345, 345, 345, 345, 345, + 346, 346, 64, 64, 64, 64, 64, 64, 347, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 348, 64, 64, 64, 349, 349, 350, 350, 350, 350, 350, 350, 350, + 350, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, + 351, 351, 352, 353, 354, 354, 355, 64, 64, 356, 356, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 358, 359, 358, 359, 359, + 359, 359, 359, 359, 359, 64, 360, 358, 359, 358, 358, 359, 359, 359, 359, + 359, 359, 359, 359, 358, 358, 358, 358, 358, 358, 359, 359, 361, 361, + 361, 361, 361, 361, 361, 361, 64, 64, 362, 363, 363, 363, 363, 363, 363, + 363, 363, 363, 363, 64, 64, 64, 64, 64, 64, 364, 364, 364, 364, 364, 364, + 364, 365, 364, 364, 364, 364, 364, 364, 64, 64, 78, 78, 78, 78, 78, 136, + 136, 136, 136, 136, 136, 78, 78, 136, 366, 64, 367, 367, 367, 367, 368, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 370, 368, 367, 367, 367, 367, 367, 368, 367, 368, 368, 368, 368, + 368, 367, 368, 371, 369, 369, 369, 369, 369, 369, 369, 64, 64, 64, 64, + 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 373, 373, 373, 373, + 373, 373, 373, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 375, + 376, 375, 375, 375, 375, 375, 375, 375, 374, 374, 374, 374, 374, 374, + 374, 374, 374, 64, 64, 64, 377, 377, 378, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 378, 377, 377, 377, 377, 378, + 378, 377, 377, 380, 381, 377, 377, 379, 379, 382, 382, 382, 382, 382, + 382, 382, 382, 382, 382, 379, 379, 379, 379, 379, 379, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 385, 386, + 386, 385, 385, 385, 386, 385, 386, 386, 386, 387, 387, 64, 64, 64, 64, + 64, 64, 64, 64, 388, 388, 388, 388, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 390, 390, 390, 390, 390, 390, 390, 390, 391, + 391, 391, 391, 391, 391, 391, 391, 390, 390, 391, 392, 64, 64, 64, 393, + 393, 393, 393, 393, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 64, + 64, 64, 389, 389, 389, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, + 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, + 397, 397, 397, 397, 397, 397, 398, 398, 399, 399, 399, 399, 399, 399, + 399, 399, 78, 78, 78, 84, 400, 136, 136, 136, 136, 136, 78, 78, 136, 136, + 136, 136, 78, 401, 400, 400, 400, 400, 400, 400, 400, 402, 402, 402, 402, + 136, 402, 402, 402, 402, 401, 401, 78, 402, 402, 64, 78, 78, 64, 64, 64, + 64, 64, 64, 41, 41, 41, 41, 41, 41, 62, 62, 62, 62, 62, 75, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 65, 65, 65, 65, 65, 44, 44, 44, 44, 65, 65, 65, + 65, 65, 41, 41, 41, 41, 41, 403, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 65, 78, 78, 136, 78, 78, + 78, 78, 78, 78, 78, 136, 78, 78, 404, 405, 136, 406, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 64, 64, + 64, 64, 64, 64, 407, 136, 78, 136, 37, 41, 37, 41, 37, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 37, 41, 62, 62, 62, 62, 62, 62, 62, 62, 61, 61, 61, + 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 64, 64, 61, 61, 61, 61, 61, + 61, 64, 64, 64, 61, 64, 61, 64, 61, 64, 61, 408, 408, 408, 408, 408, 408, + 408, 408, 62, 62, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 408, 63, 62, + 63, 63, 63, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 408, 63, 63, 63, 62, + 62, 62, 62, 64, 64, 62, 62, 61, 61, 61, 61, 64, 63, 63, 63, 61, 61, 61, + 61, 61, 63, 63, 63, 64, 64, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 408, + 63, 63, 64, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 410, + 411, 411, 412, 413, 414, 415, 415, 414, 414, 414, 22, 66, 416, 417, 418, + 419, 416, 417, 418, 419, 22, 22, 22, 66, 22, 22, 22, 22, 420, 421, 422, + 423, 424, 425, 426, 21, 427, 428, 427, 427, 428, 22, 66, 66, 66, 28, 35, + 22, 66, 66, 22, 429, 429, 66, 66, 66, 430, 431, 432, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 433, 66, 429, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 409, 410, 410, 410, 410, 410, 64, 434, 435, 436, 437, 410, 410, 410, + 410, 410, 410, 438, 44, 64, 64, 33, 438, 438, 438, 438, 438, 439, 439, + 433, 431, 432, 440, 438, 33, 33, 33, 33, 438, 438, 438, 438, 438, 439, + 439, 433, 431, 432, 64, 44, 44, 44, 44, 44, 64, 64, 64, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 441, 250, 250, 23, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 64, 64, 78, 78, 400, 400, 78, 78, 78, 78, 400, 400, + 400, 78, 78, 366, 366, 366, 366, 78, 366, 366, 366, 400, 400, 78, 136, + 78, 400, 400, 136, 136, 136, 136, 78, 64, 64, 64, 64, 64, 64, 64, 26, 26, + 442, 30, 26, 30, 26, 442, 26, 30, 34, 442, 442, 442, 34, 34, 442, 442, + 442, 443, 26, 442, 30, 26, 433, 442, 442, 442, 442, 442, 26, 26, 26, 30, + 30, 26, 442, 26, 67, 26, 442, 26, 37, 38, 442, 442, 444, 34, 442, 442, + 37, 442, 34, 402, 402, 402, 402, 34, 26, 26, 34, 34, 442, 442, 445, 433, + 433, 433, 433, 442, 34, 34, 34, 34, 26, 433, 26, 26, 41, 277, 446, 446, + 446, 36, 36, 446, 446, 446, 446, 446, 446, 36, 36, 36, 36, 446, 447, 447, + 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 448, 448, 448, 448, + 447, 447, 448, 448, 448, 448, 448, 448, 448, 448, 448, 37, 41, 448, 448, + 448, 448, 36, 64, 64, 64, 64, 64, 64, 39, 39, 39, 39, 39, 30, 30, 30, 30, + 30, 433, 433, 26, 26, 26, 26, 433, 26, 26, 433, 26, 26, 433, 26, 26, 26, + 26, 26, 26, 26, 433, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 30, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 433, 433, 26, 26, 39, 26, 39, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, 26, 26, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, 433, 39, 445, 449, 449, 445, 433, + 433, 39, 449, 445, 445, 449, 445, 445, 433, 39, 433, 449, 439, 450, 433, + 449, 445, 433, 433, 433, 449, 445, 445, 449, 39, 449, 449, 445, 445, 39, + 445, 39, 445, 39, 39, 39, 39, 449, 449, 445, 449, 445, 445, 445, 445, + 445, 39, 39, 39, 39, 433, 445, 433, 445, 449, 449, 445, 445, 445, 445, + 445, 445, 445, 445, 445, 445, 449, 445, 445, 445, 449, 433, 433, 433, + 433, 433, 449, 445, 445, 445, 433, 433, 433, 433, 433, 433, 433, 433, + 433, 445, 449, 39, 445, 433, 449, 449, 449, 449, 445, 445, 449, 449, 433, + 433, 449, 449, 445, 445, 449, 449, 445, 445, 449, 449, 445, 445, 445, + 445, 445, 433, 433, 445, 445, 445, 445, 433, 433, 39, 433, 433, 445, 39, + 433, 433, 433, 433, 433, 433, 433, 433, 445, 445, 433, 39, 445, 445, 445, + 433, 433, 433, 433, 433, 445, 449, 433, 445, 445, 445, 445, 445, 433, + 433, 445, 445, 433, 433, 433, 433, 445, 445, 445, 445, 445, 445, 445, + 445, 433, 433, 431, 432, 431, 432, 26, 26, 26, 26, 26, 26, 30, 26, 26, + 26, 26, 26, 445, 445, 26, 26, 26, 26, 26, 26, 26, 451, 452, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 26, 433, 26, 26, 26, 26, 26, 26, 26, 26, 277, + 26, 26, 26, 26, 26, 433, 433, 433, 433, 433, 433, 433, 433, 433, 26, 26, + 26, 26, 433, 433, 26, 26, 26, 26, 26, 26, 26, 26, 26, 64, 64, 64, 64, 64, + 26, 26, 26, 26, 26, 26, 26, 64, 36, 36, 36, 36, 36, 36, 36, 36, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 453, 453, 453, 453, 453, 453, + 453, 453, 453, 453, 453, 453, 453, 453, 446, 36, 36, 36, 36, 36, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 26, 26, 26, 26, 26, 30, 30, + 30, 30, 26, 26, 30, 30, 26, 30, 30, 30, 30, 30, 26, 26, 30, 30, 26, 26, + 30, 39, 26, 26, 26, 26, 30, 30, 26, 26, 30, 39, 26, 26, 26, 26, 30, 30, + 30, 26, 26, 30, 26, 26, 30, 30, 26, 26, 26, 26, 26, 30, 30, 26, 26, 30, + 26, 26, 26, 26, 30, 30, 26, 26, 26, 26, 30, 26, 30, 26, 30, 26, 30, 26, + 26, 26, 26, 26, 30, 30, 26, 30, 30, 30, 26, 30, 30, 30, 30, 26, 30, 30, + 26, 39, 26, 26, 26, 26, 26, 26, 30, 30, 26, 26, 26, 26, 277, 26, 26, 26, + 26, 26, 26, 26, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 30, 30, 30, + 26, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, 431, 432, 431, + 432, 431, 432, 431, 432, 431, 432, 431, 432, 431, 432, 36, 36, 446, 446, + 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 26, 26, 26, 26, 445, + 433, 433, 445, 445, 431, 432, 433, 445, 445, 433, 445, 445, 445, 433, + 433, 433, 433, 433, 445, 445, 445, 445, 433, 433, 433, 433, 433, 445, + 445, 445, 433, 433, 433, 445, 445, 445, 445, 9, 10, 9, 10, 9, 10, 9, 10, + 431, 432, 454, 454, 454, 454, 454, 454, 454, 454, 433, 433, 433, 431, + 432, 9, 10, 431, 432, 431, 432, 431, 432, 431, 432, 431, 432, 433, 433, + 445, 445, 445, 445, 445, 445, 433, 433, 433, 433, 433, 433, 433, 433, + 445, 433, 433, 433, 433, 445, 445, 445, 445, 445, 433, 445, 445, 433, + 433, 431, 432, 431, 432, 445, 433, 433, 433, 433, 445, 433, 445, 445, + 445, 433, 433, 445, 445, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 433, 445, 445, 445, 445, 445, 445, 433, 433, 431, 432, 433, 433, 433, + 433, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 433, 445, + 445, 445, 445, 433, 433, 445, 433, 445, 433, 433, 445, 433, 445, 445, + 445, 445, 433, 433, 433, 433, 433, 445, 445, 433, 433, 433, 433, 445, + 445, 445, 445, 433, 445, 445, 433, 433, 445, 445, 433, 433, 433, 433, + 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 433, 433, 445, + 445, 445, 445, 445, 445, 445, 445, 433, 445, 445, 445, 445, 445, 445, + 445, 445, 433, 433, 433, 433, 433, 445, 433, 445, 433, 433, 433, 445, + 445, 445, 445, 445, 433, 433, 433, 433, 445, 433, 433, 433, 445, 445, + 445, 445, 445, 433, 445, 433, 433, 433, 433, 433, 433, 433, 26, 26, 433, + 433, 433, 433, 433, 433, 26, 26, 26, 26, 26, 26, 26, 26, 30, 30, 30, 26, + 26, 26, 26, 64, 64, 26, 26, 26, 26, 26, 26, 26, 26, 64, 64, 26, 26, 64, + 64, 64, 26, 26, 26, 26, 64, 26, 26, 26, 26, 26, 26, 26, 26, 64, 64, 64, + 64, 64, 64, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, + 455, 455, 455, 64, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, + 456, 456, 456, 456, 64, 37, 41, 37, 37, 37, 41, 41, 37, 41, 37, 41, 37, + 41, 37, 37, 37, 37, 41, 37, 41, 41, 37, 41, 41, 41, 41, 41, 41, 44, 44, + 37, 37, 69, 70, 69, 70, 70, 457, 457, 457, 457, 457, 457, 69, 70, 69, 70, + 458, 458, 458, 69, 70, 64, 64, 64, 64, 64, 459, 459, 459, 459, 460, 459, + 459, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, + 461, 64, 461, 64, 64, 64, 64, 64, 461, 64, 64, 462, 462, 462, 462, 462, + 462, 462, 462, 64, 64, 64, 64, 64, 64, 64, 463, 464, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 465, 77, 77, 77, 77, 77, 77, 77, 77, + 66, 66, 28, 35, 28, 35, 66, 66, 66, 28, 35, 66, 28, 35, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 415, 66, 66, 415, 66, 28, 35, 66, 66, 28, 35, 431, + 432, 431, 432, 431, 432, 431, 432, 66, 66, 66, 66, 66, 45, 66, 66, 415, + 415, 66, 66, 66, 66, 415, 66, 418, 64, 64, 64, 64, 64, 466, 466, 466, + 466, 466, 466, 466, 466, 466, 466, 64, 466, 466, 466, 466, 466, 466, 466, + 466, 466, 64, 64, 64, 64, 466, 466, 466, 466, 466, 466, 64, 64, 467, 467, + 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 64, 64, 64, 64, 468, + 469, 469, 469, 467, 470, 471, 472, 451, 452, 451, 452, 451, 452, 451, + 452, 451, 452, 467, 467, 451, 452, 451, 452, 451, 452, 451, 452, 473, + 474, 475, 475, 467, 472, 472, 472, 472, 472, 472, 472, 472, 472, 476, + 477, 478, 479, 480, 480, 473, 481, 481, 481, 481, 481, 467, 467, 472, + 472, 472, 470, 471, 469, 467, 26, 64, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 64, 64, 483, 483, 484, 484, 485, 485, 482, 473, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 469, 481, 487, 487, 486, 64, 64, 64, 64, 64, 488, 488, 488, 488, 488, + 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, 64, 64, 64, + 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 64, + 489, 489, 490, 490, 490, 490, 489, 489, 489, 489, 489, 489, 489, 489, + 489, 489, 488, 488, 488, 64, 64, 64, 64, 64, 491, 491, 491, 491, 491, + 491, 491, 491, 491, 491, 491, 491, 491, 492, 492, 64, 490, 490, 490, 490, + 490, 490, 490, 490, 490, 490, 489, 489, 489, 489, 489, 489, 493, 493, + 493, 493, 493, 493, 493, 493, 467, 494, 494, 494, 494, 494, 494, 494, + 494, 494, 494, 494, 494, 494, 494, 494, 491, 491, 491, 491, 492, 492, + 492, 489, 489, 494, 494, 494, 494, 494, 494, 494, 489, 489, 489, 489, + 467, 467, 467, 467, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 64, 489, 489, 489, 489, 489, 489, 489, 467, 467, + 467, 467, 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 467, + 467, 496, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, + 497, 497, 497, 497, 497, 497, 497, 497, 496, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 498, 497, 497, 497, 497, 496, 498, 498, 498, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 500, 499, + 499, 499, 499, 499, 499, 499, 64, 64, 64, 501, 501, 501, 501, 501, 501, + 501, 501, 501, 501, 501, 501, 501, 501, 501, 64, 502, 502, 502, 502, 502, + 502, 502, 502, 503, 503, 503, 503, 503, 503, 504, 504, 505, 505, 505, + 505, 505, 505, 505, 505, 505, 505, 505, 505, 506, 507, 507, 507, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 505, 505, 64, 64, 64, 64, + 72, 75, 72, 75, 72, 75, 509, 77, 79, 79, 79, 510, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 510, 511, 72, 75, 72, 75, 403, 403, 64, 77, 512, 512, + 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 514, 514, 515, 515, 515, 515, + 515, 515, 47, 47, 47, 47, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 47, 47, 37, 41, 37, 41, 37, 41, 41, 41, 37, 41, 37, 41, 37, 41, 44, 41, + 41, 41, 41, 41, 41, 41, 41, 37, 41, 37, 41, 37, 37, 41, 45, 516, 516, 37, + 41, 37, 41, 64, 37, 41, 37, 41, 41, 41, 37, 41, 37, 41, 37, 37, 37, 37, + 64, 64, 37, 37, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 42, + 44, 44, 41, 42, 42, 42, 42, 42, 517, 517, 518, 517, 517, 517, 519, 517, + 517, 517, 517, 518, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 520, 520, 518, 518, 520, 521, 521, 521, 521, 64, + 64, 64, 64, 522, 522, 522, 522, 522, 522, 277, 277, 250, 444, 64, 64, 64, + 64, 64, 64, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + 524, 524, 524, 524, 525, 525, 526, 526, 526, 526, 526, 526, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 527, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 528, 528, 529, 529, 529, 529, 529, 529, 529, + 529, 529, 529, 64, 64, 64, 64, 64, 64, 174, 174, 174, 174, 174, 174, 174, + 174, 174, 174, 171, 171, 171, 171, 171, 171, 176, 176, 176, 171, 64, 64, + 64, 64, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 531, 531, 531, + 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, + 531, 531, 531, 532, 532, 532, 532, 532, 533, 533, 533, 84, 534, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 536, + 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 537, 538, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 539, 290, 290, 290, 290, 290, 64, 64, 64, + 540, 540, 540, 541, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, + 542, 542, 542, 542, 542, 543, 541, 541, 540, 540, 540, 540, 541, 541, + 540, 541, 541, 541, 544, 545, 545, 545, 545, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 64, 46, 546, 546, 546, 546, 546, 546, 546, 546, 546, + 546, 64, 64, 64, 64, 545, 545, 278, 278, 278, 278, 278, 280, 547, 278, + 283, 283, 278, 278, 278, 278, 278, 64, 548, 548, 548, 548, 548, 548, 548, + 548, 548, 549, 549, 549, 549, 549, 549, 550, 550, 549, 549, 550, 550, + 549, 549, 64, 548, 548, 548, 549, 548, 548, 548, 548, 548, 548, 548, 548, + 549, 550, 64, 64, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 64, + 64, 552, 552, 552, 552, 547, 278, 278, 278, 278, 278, 278, 286, 286, 286, + 278, 279, 280, 279, 278, 278, 553, 553, 553, 553, 553, 553, 553, 553, + 554, 553, 554, 554, 555, 553, 553, 554, 554, 553, 553, 553, 553, 553, + 554, 554, 553, 554, 553, 64, 64, 64, 64, 64, 64, 64, 64, 553, 553, 556, + 557, 557, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 559, + 560, 560, 559, 559, 561, 561, 558, 562, 562, 559, 563, 64, 64, 292, 292, + 292, 292, 292, 292, 64, 41, 41, 41, 516, 44, 44, 44, 44, 64, 64, 64, 64, + 41, 62, 64, 64, 558, 558, 558, 559, 559, 560, 559, 559, 560, 559, 559, + 561, 559, 563, 64, 64, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, + 64, 64, 64, 64, 64, 64, 290, 565, 565, 565, 565, 565, 565, 565, 565, 565, + 565, 565, 565, 565, 565, 565, 565, 565, 565, 290, 64, 64, 64, 64, 291, + 291, 291, 291, 291, 291, 291, 64, 64, 64, 64, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 64, 64, 64, 64, 566, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 566, 567, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 567, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, + 496, 498, 498, 496, 496, 498, 498, 498, 498, 498, 498, 41, 41, 41, 41, + 41, 41, 41, 64, 64, 64, 64, 83, 83, 83, 83, 83, 64, 64, 64, 64, 64, 110, + 569, 110, 110, 570, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 64, 110, 110, 110, 110, 110, 64, 110, 64, 110, 110, 64, + 110, 110, 64, 110, 110, 126, 126, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 571, 571, 571, 571, 571, 571, 571, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 572, 418, 64, + 64, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 116, 119, 64, 64, + 58, 58, 58, 58, 58, 58, 58, 58, 469, 469, 469, 469, 469, 469, 469, 474, + 475, 469, 64, 64, 64, 64, 64, 64, 78, 78, 78, 78, 78, 78, 78, 136, 136, + 136, 136, 136, 136, 136, 64, 64, 469, 473, 473, 573, 573, 474, 475, 474, + 475, 474, 475, 474, 475, 474, 475, 474, 475, 474, 475, 474, 475, 469, + 469, 474, 475, 469, 469, 469, 469, 573, 573, 573, 574, 469, 574, 64, 469, + 574, 469, 469, 473, 451, 452, 451, 452, 451, 452, 575, 469, 469, 576, + 577, 578, 578, 579, 64, 469, 580, 575, 469, 64, 64, 64, 64, 126, 126, + 126, 126, 126, 64, 126, 126, 126, 126, 126, 126, 126, 64, 64, 410, 64, + 581, 581, 582, 583, 582, 581, 581, 584, 585, 581, 586, 587, 588, 587, + 587, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 587, 581, 590, + 591, 590, 581, 581, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 592, 592, 592, 584, 581, 585, 593, 594, 593, + 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, + 595, 595, 595, 595, 584, 591, 585, 591, 584, 585, 596, 597, 598, 596, + 596, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 600, 599, 599, + 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 600, 600, 601, + 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 64, + 64, 64, 601, 601, 601, 601, 601, 601, 64, 64, 601, 601, 601, 64, 64, 64, + 583, 583, 591, 593, 602, 583, 583, 64, 603, 604, 604, 604, 604, 603, 603, + 64, 64, 605, 605, 605, 26, 30, 64, 64, 606, 606, 606, 606, 606, 606, 606, + 606, 606, 606, 606, 606, 64, 606, 606, 606, 606, 606, 606, 606, 606, 606, + 606, 64, 606, 606, 606, 64, 606, 606, 64, 606, 606, 606, 606, 606, 606, + 606, 64, 64, 606, 606, 606, 64, 64, 64, 64, 64, 84, 66, 84, 64, 64, 64, + 64, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 64, + 64, 64, 277, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, + 607, 608, 608, 608, 608, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 609, 608, 608, 609, 64, 64, 64, 26, + 26, 26, 26, 64, 64, 64, 64, 609, 64, 64, 64, 64, 64, 64, 64, 277, 277, + 277, 277, 277, 136, 64, 64, 610, 610, 610, 610, 610, 610, 610, 610, 610, + 610, 610, 610, 610, 64, 64, 64, 611, 611, 611, 611, 611, 611, 611, 611, + 611, 64, 64, 64, 64, 64, 64, 64, 136, 438, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 64, 64, 64, + 64, 612, 612, 612, 612, 612, 612, 612, 612, 613, 613, 613, 613, 64, 64, + 64, 64, 614, 614, 614, 614, 614, 614, 614, 614, 614, 615, 614, 614, 614, + 614, 614, 614, 614, 614, 615, 64, 64, 64, 64, 64, 616, 616, 616, 616, + 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 617, 617, 617, + 617, 64, 64, 64, 64, 64, 618, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 618, 618, 618, 618, 64, 619, 620, 620, 620, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 64, 64, 64, 64, 621, 622, 622, 622, 622, 622, 64, 64, + 623, 623, 623, 623, 623, 623, 623, 623, 624, 624, 624, 624, 624, 624, + 624, 624, 625, 625, 625, 625, 625, 625, 625, 625, 626, 626, 626, 626, + 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, 64, 64, 627, 627, 627, + 627, 627, 627, 627, 627, 627, 627, 64, 64, 64, 64, 64, 64, 628, 628, 628, + 628, 628, 628, 628, 628, 629, 629, 629, 629, 629, 629, 629, 629, 629, + 629, 629, 629, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 630, 631, 631, + 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 64, 631, + 631, 631, 631, 631, 631, 64, 64, 632, 632, 632, 632, 632, 632, 64, 64, + 632, 64, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, + 632, 632, 632, 632, 632, 632, 632, 64, 632, 632, 64, 64, 64, 632, 64, 64, + 632, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 64, 634, 635, 635, 635, 635, 635, 635, 635, 635, 636, 636, 636, 636, + 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 637, 638, + 638, 638, 638, 638, 638, 638, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 64, 64, 64, 64, 64, 64, 64, 64, 640, + 640, 640, 640, 640, 640, 640, 640, 640, 641, 641, 641, 641, 641, 641, + 641, 641, 641, 641, 641, 641, 641, 641, 642, 642, 642, 642, 642, 642, 64, + 64, 64, 643, 644, 644, 644, 644, 644, 644, 644, 644, 644, 644, 64, 64, + 64, 64, 64, 645, 646, 646, 646, 646, 646, 646, 646, 646, 647, 647, 647, + 647, 647, 647, 647, 647, 64, 64, 64, 64, 64, 64, 647, 647, 648, 649, 649, + 649, 64, 649, 649, 64, 64, 64, 64, 64, 649, 650, 649, 651, 648, 648, 648, + 648, 64, 648, 648, 648, 64, 648, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 64, 64, 64, 64, 651, + 652, 650, 64, 64, 64, 64, 653, 654, 654, 654, 654, 654, 654, 654, 654, + 655, 655, 655, 655, 655, 655, 655, 655, 655, 64, 64, 64, 64, 64, 64, 64, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, 657, + 657, 658, 659, 659, 659, 659, 659, 659, 659, 659, 659, 659, 659, 659, + 659, 660, 660, 660, 661, 661, 661, 661, 661, 661, 661, 661, 662, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, 663, 664, 64, 64, + 64, 64, 665, 665, 665, 665, 665, 666, 666, 666, 666, 666, 666, 666, 64, + 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, 64, + 64, 64, 668, 668, 668, 668, 668, 668, 668, 669, 669, 669, 669, 669, 669, + 669, 669, 669, 669, 669, 669, 669, 669, 64, 64, 670, 670, 670, 670, 670, + 670, 670, 670, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 64, + 64, 64, 64, 64, 672, 672, 672, 672, 672, 672, 672, 672, 673, 673, 673, + 673, 673, 673, 673, 673, 673, 673, 64, 64, 64, 64, 64, 64, 64, 674, 674, + 674, 674, 64, 64, 64, 64, 675, 675, 675, 675, 675, 675, 675, 676, 676, + 676, 676, 676, 676, 676, 676, 676, 64, 64, 64, 64, 64, 64, 64, 677, 677, + 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 64, 678, + 679, 678, 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, + 680, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, + 679, 681, 682, 682, 682, 682, 682, 682, 682, 64, 64, 64, 64, 683, 683, + 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, + 683, 683, 683, 683, 684, 684, 684, 684, 684, 684, 684, 684, 684, 684, 64, + 64, 64, 64, 64, 64, 64, 681, 685, 685, 686, 687, 687, 687, 687, 687, 687, + 687, 687, 687, 687, 687, 687, 687, 686, 686, 686, 685, 685, 685, 685, + 686, 686, 688, 689, 690, 690, 691, 690, 690, 690, 690, 64, 64, 64, 64, + 64, 64, 692, 692, 692, 692, 692, 692, 692, 692, 692, 64, 64, 64, 64, 64, + 64, 64, 693, 693, 693, 693, 693, 693, 693, 693, 693, 693, 64, 64, 64, 64, + 64, 64, 694, 694, 694, 695, 695, 695, 695, 695, 695, 695, 695, 695, 695, + 695, 695, 695, 695, 695, 695, 695, 695, 695, 695, 696, 696, 696, 696, + 696, 697, 696, 696, 696, 696, 696, 696, 698, 698, 64, 699, 699, 699, 699, + 699, 699, 699, 699, 699, 699, 700, 700, 700, 700, 64, 64, 64, 64, 701, + 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, 702, 703, 703, 701, 64, + 704, 704, 705, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, + 706, 706, 706, 706, 706, 705, 705, 705, 704, 704, 704, 704, 704, 704, + 704, 704, 704, 705, 707, 706, 706, 706, 706, 708, 708, 708, 708, 64, 64, + 64, 64, 708, 64, 64, 709, 709, 709, 709, 709, 709, 709, 709, 709, 709, + 706, 64, 64, 64, 64, 64, 64, 710, 710, 710, 710, 710, 710, 710, 710, 710, + 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 64, 64, 64, 711, + 711, 711, 711, 711, 711, 711, 711, 711, 711, 64, 711, 711, 711, 711, 711, + 711, 711, 711, 711, 712, 712, 712, 713, 713, 713, 712, 712, 713, 714, + 715, 713, 716, 716, 716, 716, 716, 716, 64, 64, 717, 717, 717, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 718, 719, 719, 719, + 718, 718, 718, 718, 718, 718, 720, 721, 64, 64, 64, 64, 64, 722, 722, + 722, 722, 722, 722, 722, 722, 722, 722, 64, 64, 64, 64, 64, 64, 64, 723, + 724, 724, 64, 725, 725, 725, 725, 725, 725, 725, 725, 64, 64, 725, 725, + 64, 64, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, + 725, 64, 725, 725, 725, 725, 725, 725, 725, 64, 725, 725, 64, 725, 725, + 725, 725, 725, 64, 64, 726, 725, 724, 724, 723, 724, 724, 724, 724, 64, + 64, 724, 724, 64, 64, 724, 724, 727, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 724, 64, 64, 64, 64, 64, 725, 725, 725, 725, 725, 724, 724, 64, 64, 728, + 728, 728, 728, 728, 728, 728, 64, 64, 64, 729, 729, 729, 729, 729, 729, + 729, 729, 730, 730, 730, 731, 731, 731, 731, 731, 731, 730, 731, 730, + 730, 730, 730, 731, 731, 730, 732, 733, 729, 729, 734, 729, 735, 735, + 735, 735, 735, 735, 735, 735, 735, 735, 64, 64, 64, 64, 64, 64, 736, 736, + 736, 736, 736, 736, 736, 736, 736, 736, 736, 736, 736, 736, 736, 737, + 737, 737, 738, 738, 738, 738, 64, 64, 737, 737, 737, 737, 738, 738, 737, + 739, 740, 741, 741, 741, 741, 741, 741, 741, 741, 741, 64, 64, 64, 64, + 64, 64, 742, 742, 742, 742, 742, 742, 742, 742, 743, 743, 743, 744, 744, + 744, 744, 744, 744, 744, 744, 743, 743, 744, 743, 745, 744, 746, 746, + 746, 742, 64, 64, 64, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, + 64, 64, 64, 64, 64, 64, 748, 748, 748, 748, 748, 748, 748, 748, 748, 748, + 748, 749, 750, 749, 750, 750, 749, 749, 749, 749, 749, 749, 751, 752, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 64, 64, 64, 64, 64, 64, + 754, 754, 754, 754, 754, 754, 754, 754, 755, 755, 755, 755, 755, 755, + 755, 755, 756, 756, 756, 756, 756, 756, 756, 756, 756, 756, 757, 757, + 757, 757, 757, 757, 757, 757, 757, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 758, 759, 759, 759, 759, 759, 759, 759, 759, 759, 64, 64, 64, + 64, 64, 64, 64, 760, 760, 760, 760, 760, 760, 760, 760, 760, 64, 64, 64, + 64, 64, 64, 64, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 761, 64, 762, 762, 762, 762, 762, 64, 64, 64, 763, 763, + 763, 763, 763, 763, 763, 763, 763, 763, 763, 763, 763, 763, 763, 64, 512, + 64, 64, 64, 64, 64, 64, 64, 764, 764, 764, 764, 764, 764, 764, 764, 764, + 764, 764, 764, 764, 764, 764, 64, 765, 765, 765, 765, 765, 765, 765, 765, + 765, 765, 64, 64, 64, 64, 766, 766, 767, 767, 767, 767, 767, 767, 767, + 767, 767, 767, 767, 767, 767, 767, 64, 64, 768, 768, 768, 768, 768, 769, + 64, 64, 770, 770, 770, 770, 770, 770, 770, 770, 771, 771, 771, 771, 771, + 771, 771, 772, 772, 772, 772, 772, 773, 773, 773, 773, 774, 774, 774, + 774, 772, 773, 64, 64, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, + 64, 776, 776, 776, 776, 776, 776, 776, 64, 770, 770, 770, 770, 770, 64, + 64, 64, 64, 64, 770, 770, 770, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 64, 64, 64, 777, 778, 778, 778, 778, 778, 778, + 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, + 778, 778, 64, 64, 64, 64, 64, 64, 64, 64, 779, 779, 779, 779, 780, 780, + 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 486, 482, 64, 64, + 64, 64, 64, 64, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, + 64, 64, 64, 64, 64, 781, 781, 781, 781, 781, 64, 64, 64, 781, 64, 64, 64, + 64, 64, 64, 64, 781, 781, 64, 64, 782, 783, 784, 785, 410, 410, 410, 410, + 64, 64, 64, 64, 277, 277, 277, 277, 277, 277, 64, 64, 277, 277, 277, 277, + 277, 277, 277, 64, 64, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 786, 786, 400, 400, 400, 277, 277, 277, 787, 786, 786, 786, + 786, 786, 410, 410, 410, 410, 410, 410, 410, 410, 136, 136, 136, 136, + 136, 136, 136, 136, 277, 277, 78, 78, 78, 78, 78, 136, 136, 277, 277, + 277, 277, 277, 277, 78, 78, 78, 78, 277, 277, 609, 609, 788, 788, 788, + 609, 64, 64, 522, 522, 64, 64, 64, 64, 64, 64, 442, 442, 442, 442, 442, + 442, 442, 442, 442, 442, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 442, 442, 442, 442, 442, 442, 442, 442, 442, 442, + 34, 34, 34, 34, 34, 34, 34, 64, 34, 34, 34, 34, 34, 34, 442, 64, 442, + 442, 64, 64, 442, 64, 64, 442, 442, 64, 64, 442, 442, 442, 442, 64, 442, + 442, 34, 34, 64, 34, 64, 34, 34, 34, 34, 34, 34, 34, 64, 34, 34, 34, 34, + 34, 34, 34, 442, 442, 64, 442, 442, 442, 442, 64, 64, 442, 442, 442, 442, + 442, 442, 442, 442, 64, 442, 442, 442, 442, 442, 442, 442, 64, 34, 34, + 442, 442, 64, 442, 442, 442, 442, 64, 442, 442, 442, 442, 442, 64, 442, + 64, 64, 64, 442, 442, 442, 442, 442, 442, 442, 64, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 64, 64, 442, 789, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 445, 34, 34, 34, 34, 34, 34, 442, 442, 442, 442, 442, 442, 442, + 442, 442, 789, 34, 34, 34, 34, 34, 34, 34, 34, 34, 445, 34, 34, 442, 442, + 442, 442, 442, 789, 34, 34, 34, 34, 34, 34, 34, 34, 34, 445, 34, 34, 34, + 34, 34, 34, 442, 442, 442, 442, 442, 442, 442, 442, 442, 789, 34, 445, + 34, 34, 34, 34, 34, 34, 34, 34, 442, 34, 64, 64, 790, 790, 790, 790, 790, + 790, 790, 790, 790, 790, 791, 791, 791, 791, 791, 791, 791, 791, 791, + 791, 791, 791, 791, 64, 64, 792, 792, 792, 792, 792, 792, 792, 792, 792, + 793, 793, 793, 793, 793, 793, 793, 64, 126, 126, 126, 126, 64, 126, 126, + 126, 64, 126, 126, 64, 126, 64, 64, 126, 64, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 64, 126, 126, 126, 126, 64, 126, 64, 126, 64, + 64, 64, 64, 64, 64, 126, 64, 64, 64, 64, 126, 64, 126, 64, 126, 64, 126, + 126, 126, 64, 126, 64, 126, 64, 126, 64, 126, 64, 126, 126, 126, 126, 64, + 126, 64, 126, 126, 64, 126, 126, 126, 126, 126, 126, 126, 126, 126, 64, + 64, 64, 64, 64, 126, 126, 126, 64, 126, 126, 126, 113, 113, 64, 64, 64, + 64, 64, 64, 64, 26, 26, 26, 26, 26, 26, 26, 33, 33, 33, 446, 446, 64, 64, + 64, 453, 453, 453, 453, 453, 453, 277, 64, 453, 453, 26, 26, 64, 64, 64, + 64, 453, 453, 453, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 277, 277, + 794, 489, 489, 64, 64, 64, 64, 64, 489, 489, 489, 64, 64, 64, 64, 64, + 489, 64, 64, 64, 64, 64, 64, 64, 489, 489, 64, 64, 64, 64, 64, 64, 26, + 26, 26, 26, 26, 64, 64, 64, 64, 64, 64, 64, 26, 26, 26, 26, 26, 26, 64, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 64, 26, 26, 26, 26, 26, 26, 64, 64, + 26, 26, 26, 497, 497, 497, 497, 497, 497, 496, 498, 498, 498, 498, 498, + 498, 498, 64, 64, 64, 410, 64, 64, 64, 64, 64, 64, 410, 410, 410, 410, + 410, 410, 410, 410, 568, 568, 568, 568, 568, 567, 64, 64, +}; + +/* decomposition data */ +static const unsigned short decomp_data[] = { + 0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, + 32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, + 772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, + 65, 770, 512, 65, 771, 512, 65, 776, 512, 65, 778, 512, 67, 807, 512, 69, + 768, 512, 69, 769, 512, 69, 770, 512, 69, 776, 512, 73, 768, 512, 73, + 769, 512, 73, 770, 512, 73, 776, 512, 78, 771, 512, 79, 768, 512, 79, + 769, 512, 79, 770, 512, 79, 771, 512, 79, 776, 512, 85, 768, 512, 85, + 769, 512, 85, 770, 512, 85, 776, 512, 89, 769, 512, 97, 768, 512, 97, + 769, 512, 97, 770, 512, 97, 771, 512, 97, 776, 512, 97, 778, 512, 99, + 807, 512, 101, 768, 512, 101, 769, 512, 101, 770, 512, 101, 776, 512, + 105, 768, 512, 105, 769, 512, 105, 770, 512, 105, 776, 512, 110, 771, + 512, 111, 768, 512, 111, 769, 512, 111, 770, 512, 111, 771, 512, 111, + 776, 512, 117, 768, 512, 117, 769, 512, 117, 770, 512, 117, 776, 512, + 121, 769, 512, 121, 776, 512, 65, 772, 512, 97, 772, 512, 65, 774, 512, + 97, 774, 512, 65, 808, 512, 97, 808, 512, 67, 769, 512, 99, 769, 512, 67, + 770, 512, 99, 770, 512, 67, 775, 512, 99, 775, 512, 67, 780, 512, 99, + 780, 512, 68, 780, 512, 100, 780, 512, 69, 772, 512, 101, 772, 512, 69, + 774, 512, 101, 774, 512, 69, 775, 512, 101, 775, 512, 69, 808, 512, 101, + 808, 512, 69, 780, 512, 101, 780, 512, 71, 770, 512, 103, 770, 512, 71, + 774, 512, 103, 774, 512, 71, 775, 512, 103, 775, 512, 71, 807, 512, 103, + 807, 512, 72, 770, 512, 104, 770, 512, 73, 771, 512, 105, 771, 512, 73, + 772, 512, 105, 772, 512, 73, 774, 512, 105, 774, 512, 73, 808, 512, 105, + 808, 512, 73, 775, 514, 73, 74, 514, 105, 106, 512, 74, 770, 512, 106, + 770, 512, 75, 807, 512, 107, 807, 512, 76, 769, 512, 108, 769, 512, 76, + 807, 512, 108, 807, 512, 76, 780, 512, 108, 780, 514, 76, 183, 514, 108, + 183, 512, 78, 769, 512, 110, 769, 512, 78, 807, 512, 110, 807, 512, 78, + 780, 512, 110, 780, 514, 700, 110, 512, 79, 772, 512, 111, 772, 512, 79, + 774, 512, 111, 774, 512, 79, 779, 512, 111, 779, 512, 82, 769, 512, 114, + 769, 512, 82, 807, 512, 114, 807, 512, 82, 780, 512, 114, 780, 512, 83, + 769, 512, 115, 769, 512, 83, 770, 512, 115, 770, 512, 83, 807, 512, 115, + 807, 512, 83, 780, 512, 115, 780, 512, 84, 807, 512, 116, 807, 512, 84, + 780, 512, 116, 780, 512, 85, 771, 512, 117, 771, 512, 85, 772, 512, 117, + 772, 512, 85, 774, 512, 117, 774, 512, 85, 778, 512, 117, 778, 512, 85, + 779, 512, 117, 779, 512, 85, 808, 512, 117, 808, 512, 87, 770, 512, 119, + 770, 512, 89, 770, 512, 121, 770, 512, 89, 776, 512, 90, 769, 512, 122, + 769, 512, 90, 775, 512, 122, 775, 512, 90, 780, 512, 122, 780, 258, 115, + 512, 79, 795, 512, 111, 795, 512, 85, 795, 512, 117, 795, 514, 68, 381, + 514, 68, 382, 514, 100, 382, 514, 76, 74, 514, 76, 106, 514, 108, 106, + 514, 78, 74, 514, 78, 106, 514, 110, 106, 512, 65, 780, 512, 97, 780, + 512, 73, 780, 512, 105, 780, 512, 79, 780, 512, 111, 780, 512, 85, 780, + 512, 117, 780, 512, 220, 772, 512, 252, 772, 512, 220, 769, 512, 252, + 769, 512, 220, 780, 512, 252, 780, 512, 220, 768, 512, 252, 768, 512, + 196, 772, 512, 228, 772, 512, 550, 772, 512, 551, 772, 512, 198, 772, + 512, 230, 772, 512, 71, 780, 512, 103, 780, 512, 75, 780, 512, 107, 780, + 512, 79, 808, 512, 111, 808, 512, 490, 772, 512, 491, 772, 512, 439, 780, + 512, 658, 780, 512, 106, 780, 514, 68, 90, 514, 68, 122, 514, 100, 122, + 512, 71, 769, 512, 103, 769, 512, 78, 768, 512, 110, 768, 512, 197, 769, + 512, 229, 769, 512, 198, 769, 512, 230, 769, 512, 216, 769, 512, 248, + 769, 512, 65, 783, 512, 97, 783, 512, 65, 785, 512, 97, 785, 512, 69, + 783, 512, 101, 783, 512, 69, 785, 512, 101, 785, 512, 73, 783, 512, 105, + 783, 512, 73, 785, 512, 105, 785, 512, 79, 783, 512, 111, 783, 512, 79, + 785, 512, 111, 785, 512, 82, 783, 512, 114, 783, 512, 82, 785, 512, 114, + 785, 512, 85, 783, 512, 117, 783, 512, 85, 785, 512, 117, 785, 512, 83, + 806, 512, 115, 806, 512, 84, 806, 512, 116, 806, 512, 72, 780, 512, 104, + 780, 512, 65, 775, 512, 97, 775, 512, 69, 807, 512, 101, 807, 512, 214, + 772, 512, 246, 772, 512, 213, 772, 512, 245, 772, 512, 79, 775, 512, 111, + 775, 512, 558, 772, 512, 559, 772, 512, 89, 772, 512, 121, 772, 259, 104, + 259, 614, 259, 106, 259, 114, 259, 633, 259, 635, 259, 641, 259, 119, + 259, 121, 514, 32, 774, 514, 32, 775, 514, 32, 778, 514, 32, 808, 514, + 32, 771, 514, 32, 779, 259, 611, 259, 108, 259, 115, 259, 120, 259, 661, + 256, 768, 256, 769, 256, 787, 512, 776, 769, 256, 697, 514, 32, 837, 256, + 59, 514, 32, 769, 512, 168, 769, 512, 913, 769, 256, 183, 512, 917, 769, + 512, 919, 769, 512, 921, 769, 512, 927, 769, 512, 933, 769, 512, 937, + 769, 512, 970, 769, 512, 921, 776, 512, 933, 776, 512, 945, 769, 512, + 949, 769, 512, 951, 769, 512, 953, 769, 512, 971, 769, 512, 953, 776, + 512, 965, 776, 512, 959, 769, 512, 965, 769, 512, 969, 769, 258, 946, + 258, 952, 258, 933, 512, 978, 769, 512, 978, 776, 258, 966, 258, 960, + 258, 954, 258, 961, 258, 962, 258, 920, 258, 949, 258, 931, 512, 1045, + 768, 512, 1045, 776, 512, 1043, 769, 512, 1030, 776, 512, 1050, 769, 512, + 1048, 768, 512, 1059, 774, 512, 1048, 774, 512, 1080, 774, 512, 1077, + 768, 512, 1077, 776, 512, 1075, 769, 512, 1110, 776, 512, 1082, 769, 512, + 1080, 768, 512, 1091, 774, 512, 1140, 783, 512, 1141, 783, 512, 1046, + 774, 512, 1078, 774, 512, 1040, 774, 512, 1072, 774, 512, 1040, 776, 512, + 1072, 776, 512, 1045, 774, 512, 1077, 774, 512, 1240, 776, 512, 1241, + 776, 512, 1046, 776, 512, 1078, 776, 512, 1047, 776, 512, 1079, 776, 512, + 1048, 772, 512, 1080, 772, 512, 1048, 776, 512, 1080, 776, 512, 1054, + 776, 512, 1086, 776, 512, 1256, 776, 512, 1257, 776, 512, 1069, 776, 512, + 1101, 776, 512, 1059, 772, 512, 1091, 772, 512, 1059, 776, 512, 1091, + 776, 512, 1059, 779, 512, 1091, 779, 512, 1063, 776, 512, 1095, 776, 512, + 1067, 776, 512, 1099, 776, 514, 1381, 1410, 512, 1575, 1619, 512, 1575, + 1620, 512, 1608, 1620, 512, 1575, 1621, 512, 1610, 1620, 514, 1575, 1652, + 514, 1608, 1652, 514, 1735, 1652, 514, 1610, 1652, 512, 1749, 1620, 512, + 1729, 1620, 512, 1746, 1620, 512, 2344, 2364, 512, 2352, 2364, 512, 2355, + 2364, 512, 2325, 2364, 512, 2326, 2364, 512, 2327, 2364, 512, 2332, 2364, + 512, 2337, 2364, 512, 2338, 2364, 512, 2347, 2364, 512, 2351, 2364, 512, + 2503, 2494, 512, 2503, 2519, 512, 2465, 2492, 512, 2466, 2492, 512, 2479, + 2492, 512, 2610, 2620, 512, 2616, 2620, 512, 2582, 2620, 512, 2583, 2620, + 512, 2588, 2620, 512, 2603, 2620, 512, 2887, 2902, 512, 2887, 2878, 512, + 2887, 2903, 512, 2849, 2876, 512, 2850, 2876, 512, 2962, 3031, 512, 3014, + 3006, 512, 3015, 3006, 512, 3014, 3031, 512, 3142, 3158, 512, 3263, 3285, + 512, 3270, 3285, 512, 3270, 3286, 512, 3270, 3266, 512, 3274, 3285, 512, + 3398, 3390, 512, 3399, 3390, 512, 3398, 3415, 512, 3545, 3530, 512, 3545, + 3535, 512, 3548, 3530, 512, 3545, 3551, 514, 3661, 3634, 514, 3789, 3762, + 514, 3755, 3737, 514, 3755, 3745, 257, 3851, 512, 3906, 4023, 512, 3916, + 4023, 512, 3921, 4023, 512, 3926, 4023, 512, 3931, 4023, 512, 3904, 4021, + 512, 3953, 3954, 512, 3953, 3956, 512, 4018, 3968, 514, 4018, 3969, 512, + 4019, 3968, 514, 4019, 3969, 512, 3953, 3968, 512, 3986, 4023, 512, 3996, + 4023, 512, 4001, 4023, 512, 4006, 4023, 512, 4011, 4023, 512, 3984, 4021, + 512, 4133, 4142, 259, 4316, 512, 6917, 6965, 512, 6919, 6965, 512, 6921, + 6965, 512, 6923, 6965, 512, 6925, 6965, 512, 6929, 6965, 512, 6970, 6965, + 512, 6972, 6965, 512, 6974, 6965, 512, 6975, 6965, 512, 6978, 6965, 259, + 65, 259, 198, 259, 66, 259, 68, 259, 69, 259, 398, 259, 71, 259, 72, 259, + 73, 259, 74, 259, 75, 259, 76, 259, 77, 259, 78, 259, 79, 259, 546, 259, + 80, 259, 82, 259, 84, 259, 85, 259, 87, 259, 97, 259, 592, 259, 593, 259, + 7426, 259, 98, 259, 100, 259, 101, 259, 601, 259, 603, 259, 604, 259, + 103, 259, 107, 259, 109, 259, 331, 259, 111, 259, 596, 259, 7446, 259, + 7447, 259, 112, 259, 116, 259, 117, 259, 7453, 259, 623, 259, 118, 259, + 7461, 259, 946, 259, 947, 259, 948, 259, 966, 259, 967, 261, 105, 261, + 114, 261, 117, 261, 118, 261, 946, 261, 947, 261, 961, 261, 966, 261, + 967, 259, 1085, 259, 594, 259, 99, 259, 597, 259, 240, 259, 604, 259, + 102, 259, 607, 259, 609, 259, 613, 259, 616, 259, 617, 259, 618, 259, + 7547, 259, 669, 259, 621, 259, 7557, 259, 671, 259, 625, 259, 624, 259, + 626, 259, 627, 259, 628, 259, 629, 259, 632, 259, 642, 259, 643, 259, + 427, 259, 649, 259, 650, 259, 7452, 259, 651, 259, 652, 259, 122, 259, + 656, 259, 657, 259, 658, 259, 952, 512, 65, 805, 512, 97, 805, 512, 66, + 775, 512, 98, 775, 512, 66, 803, 512, 98, 803, 512, 66, 817, 512, 98, + 817, 512, 199, 769, 512, 231, 769, 512, 68, 775, 512, 100, 775, 512, 68, + 803, 512, 100, 803, 512, 68, 817, 512, 100, 817, 512, 68, 807, 512, 100, + 807, 512, 68, 813, 512, 100, 813, 512, 274, 768, 512, 275, 768, 512, 274, + 769, 512, 275, 769, 512, 69, 813, 512, 101, 813, 512, 69, 816, 512, 101, + 816, 512, 552, 774, 512, 553, 774, 512, 70, 775, 512, 102, 775, 512, 71, + 772, 512, 103, 772, 512, 72, 775, 512, 104, 775, 512, 72, 803, 512, 104, + 803, 512, 72, 776, 512, 104, 776, 512, 72, 807, 512, 104, 807, 512, 72, + 814, 512, 104, 814, 512, 73, 816, 512, 105, 816, 512, 207, 769, 512, 239, + 769, 512, 75, 769, 512, 107, 769, 512, 75, 803, 512, 107, 803, 512, 75, + 817, 512, 107, 817, 512, 76, 803, 512, 108, 803, 512, 7734, 772, 512, + 7735, 772, 512, 76, 817, 512, 108, 817, 512, 76, 813, 512, 108, 813, 512, + 77, 769, 512, 109, 769, 512, 77, 775, 512, 109, 775, 512, 77, 803, 512, + 109, 803, 512, 78, 775, 512, 110, 775, 512, 78, 803, 512, 110, 803, 512, + 78, 817, 512, 110, 817, 512, 78, 813, 512, 110, 813, 512, 213, 769, 512, + 245, 769, 512, 213, 776, 512, 245, 776, 512, 332, 768, 512, 333, 768, + 512, 332, 769, 512, 333, 769, 512, 80, 769, 512, 112, 769, 512, 80, 775, + 512, 112, 775, 512, 82, 775, 512, 114, 775, 512, 82, 803, 512, 114, 803, + 512, 7770, 772, 512, 7771, 772, 512, 82, 817, 512, 114, 817, 512, 83, + 775, 512, 115, 775, 512, 83, 803, 512, 115, 803, 512, 346, 775, 512, 347, + 775, 512, 352, 775, 512, 353, 775, 512, 7778, 775, 512, 7779, 775, 512, + 84, 775, 512, 116, 775, 512, 84, 803, 512, 116, 803, 512, 84, 817, 512, + 116, 817, 512, 84, 813, 512, 116, 813, 512, 85, 804, 512, 117, 804, 512, + 85, 816, 512, 117, 816, 512, 85, 813, 512, 117, 813, 512, 360, 769, 512, + 361, 769, 512, 362, 776, 512, 363, 776, 512, 86, 771, 512, 118, 771, 512, + 86, 803, 512, 118, 803, 512, 87, 768, 512, 119, 768, 512, 87, 769, 512, + 119, 769, 512, 87, 776, 512, 119, 776, 512, 87, 775, 512, 119, 775, 512, + 87, 803, 512, 119, 803, 512, 88, 775, 512, 120, 775, 512, 88, 776, 512, + 120, 776, 512, 89, 775, 512, 121, 775, 512, 90, 770, 512, 122, 770, 512, + 90, 803, 512, 122, 803, 512, 90, 817, 512, 122, 817, 512, 104, 817, 512, + 116, 776, 512, 119, 778, 512, 121, 778, 514, 97, 702, 512, 383, 775, 512, + 65, 803, 512, 97, 803, 512, 65, 777, 512, 97, 777, 512, 194, 769, 512, + 226, 769, 512, 194, 768, 512, 226, 768, 512, 194, 777, 512, 226, 777, + 512, 194, 771, 512, 226, 771, 512, 7840, 770, 512, 7841, 770, 512, 258, + 769, 512, 259, 769, 512, 258, 768, 512, 259, 768, 512, 258, 777, 512, + 259, 777, 512, 258, 771, 512, 259, 771, 512, 7840, 774, 512, 7841, 774, + 512, 69, 803, 512, 101, 803, 512, 69, 777, 512, 101, 777, 512, 69, 771, + 512, 101, 771, 512, 202, 769, 512, 234, 769, 512, 202, 768, 512, 234, + 768, 512, 202, 777, 512, 234, 777, 512, 202, 771, 512, 234, 771, 512, + 7864, 770, 512, 7865, 770, 512, 73, 777, 512, 105, 777, 512, 73, 803, + 512, 105, 803, 512, 79, 803, 512, 111, 803, 512, 79, 777, 512, 111, 777, + 512, 212, 769, 512, 244, 769, 512, 212, 768, 512, 244, 768, 512, 212, + 777, 512, 244, 777, 512, 212, 771, 512, 244, 771, 512, 7884, 770, 512, + 7885, 770, 512, 416, 769, 512, 417, 769, 512, 416, 768, 512, 417, 768, + 512, 416, 777, 512, 417, 777, 512, 416, 771, 512, 417, 771, 512, 416, + 803, 512, 417, 803, 512, 85, 803, 512, 117, 803, 512, 85, 777, 512, 117, + 777, 512, 431, 769, 512, 432, 769, 512, 431, 768, 512, 432, 768, 512, + 431, 777, 512, 432, 777, 512, 431, 771, 512, 432, 771, 512, 431, 803, + 512, 432, 803, 512, 89, 768, 512, 121, 768, 512, 89, 803, 512, 121, 803, + 512, 89, 777, 512, 121, 777, 512, 89, 771, 512, 121, 771, 512, 945, 787, + 512, 945, 788, 512, 7936, 768, 512, 7937, 768, 512, 7936, 769, 512, 7937, + 769, 512, 7936, 834, 512, 7937, 834, 512, 913, 787, 512, 913, 788, 512, + 7944, 768, 512, 7945, 768, 512, 7944, 769, 512, 7945, 769, 512, 7944, + 834, 512, 7945, 834, 512, 949, 787, 512, 949, 788, 512, 7952, 768, 512, + 7953, 768, 512, 7952, 769, 512, 7953, 769, 512, 917, 787, 512, 917, 788, + 512, 7960, 768, 512, 7961, 768, 512, 7960, 769, 512, 7961, 769, 512, 951, + 787, 512, 951, 788, 512, 7968, 768, 512, 7969, 768, 512, 7968, 769, 512, + 7969, 769, 512, 7968, 834, 512, 7969, 834, 512, 919, 787, 512, 919, 788, + 512, 7976, 768, 512, 7977, 768, 512, 7976, 769, 512, 7977, 769, 512, + 7976, 834, 512, 7977, 834, 512, 953, 787, 512, 953, 788, 512, 7984, 768, + 512, 7985, 768, 512, 7984, 769, 512, 7985, 769, 512, 7984, 834, 512, + 7985, 834, 512, 921, 787, 512, 921, 788, 512, 7992, 768, 512, 7993, 768, + 512, 7992, 769, 512, 7993, 769, 512, 7992, 834, 512, 7993, 834, 512, 959, + 787, 512, 959, 788, 512, 8000, 768, 512, 8001, 768, 512, 8000, 769, 512, + 8001, 769, 512, 927, 787, 512, 927, 788, 512, 8008, 768, 512, 8009, 768, + 512, 8008, 769, 512, 8009, 769, 512, 965, 787, 512, 965, 788, 512, 8016, + 768, 512, 8017, 768, 512, 8016, 769, 512, 8017, 769, 512, 8016, 834, 512, + 8017, 834, 512, 933, 788, 512, 8025, 768, 512, 8025, 769, 512, 8025, 834, + 512, 969, 787, 512, 969, 788, 512, 8032, 768, 512, 8033, 768, 512, 8032, + 769, 512, 8033, 769, 512, 8032, 834, 512, 8033, 834, 512, 937, 787, 512, + 937, 788, 512, 8040, 768, 512, 8041, 768, 512, 8040, 769, 512, 8041, 769, + 512, 8040, 834, 512, 8041, 834, 512, 945, 768, 256, 940, 512, 949, 768, + 256, 941, 512, 951, 768, 256, 942, 512, 953, 768, 256, 943, 512, 959, + 768, 256, 972, 512, 965, 768, 256, 973, 512, 969, 768, 256, 974, 512, + 7936, 837, 512, 7937, 837, 512, 7938, 837, 512, 7939, 837, 512, 7940, + 837, 512, 7941, 837, 512, 7942, 837, 512, 7943, 837, 512, 7944, 837, 512, + 7945, 837, 512, 7946, 837, 512, 7947, 837, 512, 7948, 837, 512, 7949, + 837, 512, 7950, 837, 512, 7951, 837, 512, 7968, 837, 512, 7969, 837, 512, + 7970, 837, 512, 7971, 837, 512, 7972, 837, 512, 7973, 837, 512, 7974, + 837, 512, 7975, 837, 512, 7976, 837, 512, 7977, 837, 512, 7978, 837, 512, + 7979, 837, 512, 7980, 837, 512, 7981, 837, 512, 7982, 837, 512, 7983, + 837, 512, 8032, 837, 512, 8033, 837, 512, 8034, 837, 512, 8035, 837, 512, + 8036, 837, 512, 8037, 837, 512, 8038, 837, 512, 8039, 837, 512, 8040, + 837, 512, 8041, 837, 512, 8042, 837, 512, 8043, 837, 512, 8044, 837, 512, + 8045, 837, 512, 8046, 837, 512, 8047, 837, 512, 945, 774, 512, 945, 772, + 512, 8048, 837, 512, 945, 837, 512, 940, 837, 512, 945, 834, 512, 8118, + 837, 512, 913, 774, 512, 913, 772, 512, 913, 768, 256, 902, 512, 913, + 837, 514, 32, 787, 256, 953, 514, 32, 787, 514, 32, 834, 512, 168, 834, + 512, 8052, 837, 512, 951, 837, 512, 942, 837, 512, 951, 834, 512, 8134, + 837, 512, 917, 768, 256, 904, 512, 919, 768, 256, 905, 512, 919, 837, + 512, 8127, 768, 512, 8127, 769, 512, 8127, 834, 512, 953, 774, 512, 953, + 772, 512, 970, 768, 256, 912, 512, 953, 834, 512, 970, 834, 512, 921, + 774, 512, 921, 772, 512, 921, 768, 256, 906, 512, 8190, 768, 512, 8190, + 769, 512, 8190, 834, 512, 965, 774, 512, 965, 772, 512, 971, 768, 256, + 944, 512, 961, 787, 512, 961, 788, 512, 965, 834, 512, 971, 834, 512, + 933, 774, 512, 933, 772, 512, 933, 768, 256, 910, 512, 929, 788, 512, + 168, 768, 256, 901, 256, 96, 512, 8060, 837, 512, 969, 837, 512, 974, + 837, 512, 969, 834, 512, 8182, 837, 512, 927, 768, 256, 908, 512, 937, + 768, 256, 911, 512, 937, 837, 256, 180, 514, 32, 788, 256, 8194, 256, + 8195, 258, 32, 258, 32, 258, 32, 258, 32, 258, 32, 257, 32, 258, 32, 258, + 32, 258, 32, 257, 8208, 514, 32, 819, 258, 46, 514, 46, 46, 770, 46, 46, + 46, 257, 32, 514, 8242, 8242, 770, 8242, 8242, 8242, 514, 8245, 8245, + 770, 8245, 8245, 8245, 514, 33, 33, 514, 32, 773, 514, 63, 63, 514, 63, + 33, 514, 33, 63, 1026, 8242, 8242, 8242, 8242, 258, 32, 259, 48, 259, + 105, 259, 52, 259, 53, 259, 54, 259, 55, 259, 56, 259, 57, 259, 43, 259, + 8722, 259, 61, 259, 40, 259, 41, 259, 110, 261, 48, 261, 49, 261, 50, + 261, 51, 261, 52, 261, 53, 261, 54, 261, 55, 261, 56, 261, 57, 261, 43, + 261, 8722, 261, 61, 261, 40, 261, 41, 261, 97, 261, 101, 261, 111, 261, + 120, 261, 601, 261, 104, 261, 107, 261, 108, 261, 109, 261, 110, 261, + 112, 261, 115, 261, 116, 514, 82, 115, 770, 97, 47, 99, 770, 97, 47, 115, + 262, 67, 514, 176, 67, 770, 99, 47, 111, 770, 99, 47, 117, 258, 400, 514, + 176, 70, 262, 103, 262, 72, 262, 72, 262, 72, 262, 104, 262, 295, 262, + 73, 262, 73, 262, 76, 262, 108, 262, 78, 514, 78, 111, 262, 80, 262, 81, + 262, 82, 262, 82, 262, 82, 515, 83, 77, 770, 84, 69, 76, 515, 84, 77, + 262, 90, 256, 937, 262, 90, 256, 75, 256, 197, 262, 66, 262, 67, 262, + 101, 262, 69, 262, 70, 262, 77, 262, 111, 258, 1488, 258, 1489, 258, + 1490, 258, 1491, 262, 105, 770, 70, 65, 88, 262, 960, 262, 947, 262, 915, + 262, 928, 262, 8721, 262, 68, 262, 100, 262, 101, 262, 105, 262, 106, + 772, 49, 8260, 55, 772, 49, 8260, 57, 1028, 49, 8260, 49, 48, 772, 49, + 8260, 51, 772, 50, 8260, 51, 772, 49, 8260, 53, 772, 50, 8260, 53, 772, + 51, 8260, 53, 772, 52, 8260, 53, 772, 49, 8260, 54, 772, 53, 8260, 54, + 772, 49, 8260, 56, 772, 51, 8260, 56, 772, 53, 8260, 56, 772, 55, 8260, + 56, 516, 49, 8260, 258, 73, 514, 73, 73, 770, 73, 73, 73, 514, 73, 86, + 258, 86, 514, 86, 73, 770, 86, 73, 73, 1026, 86, 73, 73, 73, 514, 73, 88, + 258, 88, 514, 88, 73, 770, 88, 73, 73, 258, 76, 258, 67, 258, 68, 258, + 77, 258, 105, 514, 105, 105, 770, 105, 105, 105, 514, 105, 118, 258, 118, + 514, 118, 105, 770, 118, 105, 105, 1026, 118, 105, 105, 105, 514, 105, + 120, 258, 120, 514, 120, 105, 770, 120, 105, 105, 258, 108, 258, 99, 258, + 100, 258, 109, 772, 48, 8260, 51, 512, 8592, 824, 512, 8594, 824, 512, + 8596, 824, 512, 8656, 824, 512, 8660, 824, 512, 8658, 824, 512, 8707, + 824, 512, 8712, 824, 512, 8715, 824, 512, 8739, 824, 512, 8741, 824, 514, + 8747, 8747, 770, 8747, 8747, 8747, 514, 8750, 8750, 770, 8750, 8750, + 8750, 512, 8764, 824, 512, 8771, 824, 512, 8773, 824, 512, 8776, 824, + 512, 61, 824, 512, 8801, 824, 512, 8781, 824, 512, 60, 824, 512, 62, 824, + 512, 8804, 824, 512, 8805, 824, 512, 8818, 824, 512, 8819, 824, 512, + 8822, 824, 512, 8823, 824, 512, 8826, 824, 512, 8827, 824, 512, 8834, + 824, 512, 8835, 824, 512, 8838, 824, 512, 8839, 824, 512, 8866, 824, 512, + 8872, 824, 512, 8873, 824, 512, 8875, 824, 512, 8828, 824, 512, 8829, + 824, 512, 8849, 824, 512, 8850, 824, 512, 8882, 824, 512, 8883, 824, 512, + 8884, 824, 512, 8885, 824, 256, 12296, 256, 12297, 263, 49, 263, 50, 263, + 51, 263, 52, 263, 53, 263, 54, 263, 55, 263, 56, 263, 57, 519, 49, 48, + 519, 49, 49, 519, 49, 50, 519, 49, 51, 519, 49, 52, 519, 49, 53, 519, 49, + 54, 519, 49, 55, 519, 49, 56, 519, 49, 57, 519, 50, 48, 770, 40, 49, 41, + 770, 40, 50, 41, 770, 40, 51, 41, 770, 40, 52, 41, 770, 40, 53, 41, 770, + 40, 54, 41, 770, 40, 55, 41, 770, 40, 56, 41, 770, 40, 57, 41, 1026, 40, + 49, 48, 41, 1026, 40, 49, 49, 41, 1026, 40, 49, 50, 41, 1026, 40, 49, 51, + 41, 1026, 40, 49, 52, 41, 1026, 40, 49, 53, 41, 1026, 40, 49, 54, 41, + 1026, 40, 49, 55, 41, 1026, 40, 49, 56, 41, 1026, 40, 49, 57, 41, 1026, + 40, 50, 48, 41, 514, 49, 46, 514, 50, 46, 514, 51, 46, 514, 52, 46, 514, + 53, 46, 514, 54, 46, 514, 55, 46, 514, 56, 46, 514, 57, 46, 770, 49, 48, + 46, 770, 49, 49, 46, 770, 49, 50, 46, 770, 49, 51, 46, 770, 49, 52, 46, + 770, 49, 53, 46, 770, 49, 54, 46, 770, 49, 55, 46, 770, 49, 56, 46, 770, + 49, 57, 46, 770, 50, 48, 46, 770, 40, 97, 41, 770, 40, 98, 41, 770, 40, + 99, 41, 770, 40, 100, 41, 770, 40, 101, 41, 770, 40, 102, 41, 770, 40, + 103, 41, 770, 40, 104, 41, 770, 40, 105, 41, 770, 40, 106, 41, 770, 40, + 107, 41, 770, 40, 108, 41, 770, 40, 109, 41, 770, 40, 110, 41, 770, 40, + 111, 41, 770, 40, 112, 41, 770, 40, 113, 41, 770, 40, 114, 41, 770, 40, + 115, 41, 770, 40, 116, 41, 770, 40, 117, 41, 770, 40, 118, 41, 770, 40, + 119, 41, 770, 40, 120, 41, 770, 40, 121, 41, 770, 40, 122, 41, 263, 65, + 263, 66, 263, 67, 263, 68, 263, 69, 263, 70, 263, 71, 263, 72, 263, 73, + 263, 74, 263, 75, 263, 76, 263, 77, 263, 78, 263, 79, 263, 80, 263, 81, + 263, 82, 263, 83, 263, 84, 263, 85, 263, 86, 263, 87, 263, 88, 263, 89, + 263, 90, 263, 97, 263, 98, 263, 99, 263, 100, 263, 101, 263, 102, 263, + 103, 263, 104, 263, 105, 263, 106, 263, 107, 263, 108, 263, 109, 263, + 110, 263, 111, 263, 112, 263, 113, 263, 114, 263, 115, 263, 116, 263, + 117, 263, 118, 263, 119, 263, 120, 263, 121, 263, 122, 263, 48, 1026, + 8747, 8747, 8747, 8747, 770, 58, 58, 61, 514, 61, 61, 770, 61, 61, 61, + 512, 10973, 824, 261, 106, 259, 86, 259, 11617, 258, 27597, 258, 40863, + 258, 19968, 258, 20008, 258, 20022, 258, 20031, 258, 20057, 258, 20101, + 258, 20108, 258, 20128, 258, 20154, 258, 20799, 258, 20837, 258, 20843, + 258, 20866, 258, 20886, 258, 20907, 258, 20960, 258, 20981, 258, 20992, + 258, 21147, 258, 21241, 258, 21269, 258, 21274, 258, 21304, 258, 21313, + 258, 21340, 258, 21353, 258, 21378, 258, 21430, 258, 21448, 258, 21475, + 258, 22231, 258, 22303, 258, 22763, 258, 22786, 258, 22794, 258, 22805, + 258, 22823, 258, 22899, 258, 23376, 258, 23424, 258, 23544, 258, 23567, + 258, 23586, 258, 23608, 258, 23662, 258, 23665, 258, 24027, 258, 24037, + 258, 24049, 258, 24062, 258, 24178, 258, 24186, 258, 24191, 258, 24308, + 258, 24318, 258, 24331, 258, 24339, 258, 24400, 258, 24417, 258, 24435, + 258, 24515, 258, 25096, 258, 25142, 258, 25163, 258, 25903, 258, 25908, + 258, 25991, 258, 26007, 258, 26020, 258, 26041, 258, 26080, 258, 26085, + 258, 26352, 258, 26376, 258, 26408, 258, 27424, 258, 27490, 258, 27513, + 258, 27571, 258, 27595, 258, 27604, 258, 27611, 258, 27663, 258, 27668, + 258, 27700, 258, 28779, 258, 29226, 258, 29238, 258, 29243, 258, 29247, + 258, 29255, 258, 29273, 258, 29275, 258, 29356, 258, 29572, 258, 29577, + 258, 29916, 258, 29926, 258, 29976, 258, 29983, 258, 29992, 258, 30000, + 258, 30091, 258, 30098, 258, 30326, 258, 30333, 258, 30382, 258, 30399, + 258, 30446, 258, 30683, 258, 30690, 258, 30707, 258, 31034, 258, 31160, + 258, 31166, 258, 31348, 258, 31435, 258, 31481, 258, 31859, 258, 31992, + 258, 32566, 258, 32593, 258, 32650, 258, 32701, 258, 32769, 258, 32780, + 258, 32786, 258, 32819, 258, 32895, 258, 32905, 258, 33251, 258, 33258, + 258, 33267, 258, 33276, 258, 33292, 258, 33307, 258, 33311, 258, 33390, + 258, 33394, 258, 33400, 258, 34381, 258, 34411, 258, 34880, 258, 34892, + 258, 34915, 258, 35198, 258, 35211, 258, 35282, 258, 35328, 258, 35895, + 258, 35910, 258, 35925, 258, 35960, 258, 35997, 258, 36196, 258, 36208, + 258, 36275, 258, 36523, 258, 36554, 258, 36763, 258, 36784, 258, 36789, + 258, 37009, 258, 37193, 258, 37318, 258, 37324, 258, 37329, 258, 38263, + 258, 38272, 258, 38428, 258, 38582, 258, 38585, 258, 38632, 258, 38737, + 258, 38750, 258, 38754, 258, 38761, 258, 38859, 258, 38893, 258, 38899, + 258, 38913, 258, 39080, 258, 39131, 258, 39135, 258, 39318, 258, 39321, + 258, 39340, 258, 39592, 258, 39640, 258, 39647, 258, 39717, 258, 39727, + 258, 39730, 258, 39740, 258, 39770, 258, 40165, 258, 40565, 258, 40575, + 258, 40613, 258, 40635, 258, 40643, 258, 40653, 258, 40657, 258, 40697, + 258, 40701, 258, 40718, 258, 40723, 258, 40736, 258, 40763, 258, 40778, + 258, 40786, 258, 40845, 258, 40860, 258, 40864, 264, 32, 258, 12306, 258, + 21313, 258, 21316, 258, 21317, 512, 12363, 12441, 512, 12365, 12441, 512, + 12367, 12441, 512, 12369, 12441, 512, 12371, 12441, 512, 12373, 12441, + 512, 12375, 12441, 512, 12377, 12441, 512, 12379, 12441, 512, 12381, + 12441, 512, 12383, 12441, 512, 12385, 12441, 512, 12388, 12441, 512, + 12390, 12441, 512, 12392, 12441, 512, 12399, 12441, 512, 12399, 12442, + 512, 12402, 12441, 512, 12402, 12442, 512, 12405, 12441, 512, 12405, + 12442, 512, 12408, 12441, 512, 12408, 12442, 512, 12411, 12441, 512, + 12411, 12442, 512, 12358, 12441, 514, 32, 12441, 514, 32, 12442, 512, + 12445, 12441, 521, 12424, 12426, 512, 12459, 12441, 512, 12461, 12441, + 512, 12463, 12441, 512, 12465, 12441, 512, 12467, 12441, 512, 12469, + 12441, 512, 12471, 12441, 512, 12473, 12441, 512, 12475, 12441, 512, + 12477, 12441, 512, 12479, 12441, 512, 12481, 12441, 512, 12484, 12441, + 512, 12486, 12441, 512, 12488, 12441, 512, 12495, 12441, 512, 12495, + 12442, 512, 12498, 12441, 512, 12498, 12442, 512, 12501, 12441, 512, + 12501, 12442, 512, 12504, 12441, 512, 12504, 12442, 512, 12507, 12441, + 512, 12507, 12442, 512, 12454, 12441, 512, 12527, 12441, 512, 12528, + 12441, 512, 12529, 12441, 512, 12530, 12441, 512, 12541, 12441, 521, + 12467, 12488, 258, 4352, 258, 4353, 258, 4522, 258, 4354, 258, 4524, 258, + 4525, 258, 4355, 258, 4356, 258, 4357, 258, 4528, 258, 4529, 258, 4530, + 258, 4531, 258, 4532, 258, 4533, 258, 4378, 258, 4358, 258, 4359, 258, + 4360, 258, 4385, 258, 4361, 258, 4362, 258, 4363, 258, 4364, 258, 4365, + 258, 4366, 258, 4367, 258, 4368, 258, 4369, 258, 4370, 258, 4449, 258, + 4450, 258, 4451, 258, 4452, 258, 4453, 258, 4454, 258, 4455, 258, 4456, + 258, 4457, 258, 4458, 258, 4459, 258, 4460, 258, 4461, 258, 4462, 258, + 4463, 258, 4464, 258, 4465, 258, 4466, 258, 4467, 258, 4468, 258, 4469, + 258, 4448, 258, 4372, 258, 4373, 258, 4551, 258, 4552, 258, 4556, 258, + 4558, 258, 4563, 258, 4567, 258, 4569, 258, 4380, 258, 4573, 258, 4575, + 258, 4381, 258, 4382, 258, 4384, 258, 4386, 258, 4387, 258, 4391, 258, + 4393, 258, 4395, 258, 4396, 258, 4397, 258, 4398, 258, 4399, 258, 4402, + 258, 4406, 258, 4416, 258, 4423, 258, 4428, 258, 4593, 258, 4594, 258, + 4439, 258, 4440, 258, 4441, 258, 4484, 258, 4485, 258, 4488, 258, 4497, + 258, 4498, 258, 4500, 258, 4510, 258, 4513, 259, 19968, 259, 20108, 259, + 19977, 259, 22235, 259, 19978, 259, 20013, 259, 19979, 259, 30002, 259, + 20057, 259, 19993, 259, 19969, 259, 22825, 259, 22320, 259, 20154, 770, + 40, 4352, 41, 770, 40, 4354, 41, 770, 40, 4355, 41, 770, 40, 4357, 41, + 770, 40, 4358, 41, 770, 40, 4359, 41, 770, 40, 4361, 41, 770, 40, 4363, + 41, 770, 40, 4364, 41, 770, 40, 4366, 41, 770, 40, 4367, 41, 770, 40, + 4368, 41, 770, 40, 4369, 41, 770, 40, 4370, 41, 1026, 40, 4352, 4449, 41, + 1026, 40, 4354, 4449, 41, 1026, 40, 4355, 4449, 41, 1026, 40, 4357, 4449, + 41, 1026, 40, 4358, 4449, 41, 1026, 40, 4359, 4449, 41, 1026, 40, 4361, + 4449, 41, 1026, 40, 4363, 4449, 41, 1026, 40, 4364, 4449, 41, 1026, 40, + 4366, 4449, 41, 1026, 40, 4367, 4449, 41, 1026, 40, 4368, 4449, 41, 1026, + 40, 4369, 4449, 41, 1026, 40, 4370, 4449, 41, 1026, 40, 4364, 4462, 41, + 1794, 40, 4363, 4457, 4364, 4453, 4523, 41, 1538, 40, 4363, 4457, 4370, + 4462, 41, 770, 40, 19968, 41, 770, 40, 20108, 41, 770, 40, 19977, 41, + 770, 40, 22235, 41, 770, 40, 20116, 41, 770, 40, 20845, 41, 770, 40, + 19971, 41, 770, 40, 20843, 41, 770, 40, 20061, 41, 770, 40, 21313, 41, + 770, 40, 26376, 41, 770, 40, 28779, 41, 770, 40, 27700, 41, 770, 40, + 26408, 41, 770, 40, 37329, 41, 770, 40, 22303, 41, 770, 40, 26085, 41, + 770, 40, 26666, 41, 770, 40, 26377, 41, 770, 40, 31038, 41, 770, 40, + 21517, 41, 770, 40, 29305, 41, 770, 40, 36001, 41, 770, 40, 31069, 41, + 770, 40, 21172, 41, 770, 40, 20195, 41, 770, 40, 21628, 41, 770, 40, + 23398, 41, 770, 40, 30435, 41, 770, 40, 20225, 41, 770, 40, 36039, 41, + 770, 40, 21332, 41, 770, 40, 31085, 41, 770, 40, 20241, 41, 770, 40, + 33258, 41, 770, 40, 33267, 41, 263, 21839, 263, 24188, 263, 25991, 263, + 31631, 778, 80, 84, 69, 519, 50, 49, 519, 50, 50, 519, 50, 51, 519, 50, + 52, 519, 50, 53, 519, 50, 54, 519, 50, 55, 519, 50, 56, 519, 50, 57, 519, + 51, 48, 519, 51, 49, 519, 51, 50, 519, 51, 51, 519, 51, 52, 519, 51, 53, + 263, 4352, 263, 4354, 263, 4355, 263, 4357, 263, 4358, 263, 4359, 263, + 4361, 263, 4363, 263, 4364, 263, 4366, 263, 4367, 263, 4368, 263, 4369, + 263, 4370, 519, 4352, 4449, 519, 4354, 4449, 519, 4355, 4449, 519, 4357, + 4449, 519, 4358, 4449, 519, 4359, 4449, 519, 4361, 4449, 519, 4363, 4449, + 519, 4364, 4449, 519, 4366, 4449, 519, 4367, 4449, 519, 4368, 4449, 519, + 4369, 4449, 519, 4370, 4449, 1287, 4366, 4449, 4535, 4352, 4457, 1031, + 4364, 4462, 4363, 4468, 519, 4363, 4462, 263, 19968, 263, 20108, 263, + 19977, 263, 22235, 263, 20116, 263, 20845, 263, 19971, 263, 20843, 263, + 20061, 263, 21313, 263, 26376, 263, 28779, 263, 27700, 263, 26408, 263, + 37329, 263, 22303, 263, 26085, 263, 26666, 263, 26377, 263, 31038, 263, + 21517, 263, 29305, 263, 36001, 263, 31069, 263, 21172, 263, 31192, 263, + 30007, 263, 22899, 263, 36969, 263, 20778, 263, 21360, 263, 27880, 263, + 38917, 263, 20241, 263, 20889, 263, 27491, 263, 19978, 263, 20013, 263, + 19979, 263, 24038, 263, 21491, 263, 21307, 263, 23447, 263, 23398, 263, + 30435, 263, 20225, 263, 36039, 263, 21332, 263, 22812, 519, 51, 54, 519, + 51, 55, 519, 51, 56, 519, 51, 57, 519, 52, 48, 519, 52, 49, 519, 52, 50, + 519, 52, 51, 519, 52, 52, 519, 52, 53, 519, 52, 54, 519, 52, 55, 519, 52, + 56, 519, 52, 57, 519, 53, 48, 514, 49, 26376, 514, 50, 26376, 514, 51, + 26376, 514, 52, 26376, 514, 53, 26376, 514, 54, 26376, 514, 55, 26376, + 514, 56, 26376, 514, 57, 26376, 770, 49, 48, 26376, 770, 49, 49, 26376, + 770, 49, 50, 26376, 522, 72, 103, 778, 101, 114, 103, 522, 101, 86, 778, + 76, 84, 68, 263, 12450, 263, 12452, 263, 12454, 263, 12456, 263, 12458, + 263, 12459, 263, 12461, 263, 12463, 263, 12465, 263, 12467, 263, 12469, + 263, 12471, 263, 12473, 263, 12475, 263, 12477, 263, 12479, 263, 12481, + 263, 12484, 263, 12486, 263, 12488, 263, 12490, 263, 12491, 263, 12492, + 263, 12493, 263, 12494, 263, 12495, 263, 12498, 263, 12501, 263, 12504, + 263, 12507, 263, 12510, 263, 12511, 263, 12512, 263, 12513, 263, 12514, + 263, 12516, 263, 12518, 263, 12520, 263, 12521, 263, 12522, 263, 12523, + 263, 12524, 263, 12525, 263, 12527, 263, 12528, 263, 12529, 263, 12530, + 1034, 12450, 12497, 12540, 12488, 1034, 12450, 12523, 12501, 12449, 1034, + 12450, 12531, 12506, 12450, 778, 12450, 12540, 12523, 1034, 12452, 12491, + 12531, 12464, 778, 12452, 12531, 12481, 778, 12454, 12457, 12531, 1290, + 12456, 12473, 12463, 12540, 12489, 1034, 12456, 12540, 12459, 12540, 778, + 12458, 12531, 12473, 778, 12458, 12540, 12512, 778, 12459, 12452, 12522, + 1034, 12459, 12521, 12483, 12488, 1034, 12459, 12525, 12522, 12540, 778, + 12460, 12525, 12531, 778, 12460, 12531, 12510, 522, 12462, 12460, 778, + 12462, 12491, 12540, 1034, 12461, 12517, 12522, 12540, 1034, 12462, + 12523, 12480, 12540, 522, 12461, 12525, 1290, 12461, 12525, 12464, 12521, + 12512, 1546, 12461, 12525, 12513, 12540, 12488, 12523, 1290, 12461, + 12525, 12527, 12483, 12488, 778, 12464, 12521, 12512, 1290, 12464, 12521, + 12512, 12488, 12531, 1290, 12463, 12523, 12476, 12452, 12525, 1034, + 12463, 12525, 12540, 12493, 778, 12465, 12540, 12473, 778, 12467, 12523, + 12490, 778, 12467, 12540, 12509, 1034, 12469, 12452, 12463, 12523, 1290, + 12469, 12531, 12481, 12540, 12512, 1034, 12471, 12522, 12531, 12464, 778, + 12475, 12531, 12481, 778, 12475, 12531, 12488, 778, 12480, 12540, 12473, + 522, 12487, 12471, 522, 12489, 12523, 522, 12488, 12531, 522, 12490, + 12494, 778, 12494, 12483, 12488, 778, 12495, 12452, 12484, 1290, 12497, + 12540, 12475, 12531, 12488, 778, 12497, 12540, 12484, 1034, 12496, 12540, + 12524, 12523, 1290, 12500, 12450, 12473, 12488, 12523, 778, 12500, 12463, + 12523, 522, 12500, 12467, 522, 12499, 12523, 1290, 12501, 12449, 12521, + 12483, 12489, 1034, 12501, 12451, 12540, 12488, 1290, 12502, 12483, + 12471, 12455, 12523, 778, 12501, 12521, 12531, 1290, 12504, 12463, 12479, + 12540, 12523, 522, 12506, 12477, 778, 12506, 12491, 12498, 778, 12504, + 12523, 12484, 778, 12506, 12531, 12473, 778, 12506, 12540, 12472, 778, + 12505, 12540, 12479, 1034, 12509, 12452, 12531, 12488, 778, 12508, 12523, + 12488, 522, 12507, 12531, 778, 12509, 12531, 12489, 778, 12507, 12540, + 12523, 778, 12507, 12540, 12531, 1034, 12510, 12452, 12463, 12525, 778, + 12510, 12452, 12523, 778, 12510, 12483, 12495, 778, 12510, 12523, 12463, + 1290, 12510, 12531, 12471, 12519, 12531, 1034, 12511, 12463, 12525, + 12531, 522, 12511, 12522, 1290, 12511, 12522, 12496, 12540, 12523, 522, + 12513, 12460, 1034, 12513, 12460, 12488, 12531, 1034, 12513, 12540, + 12488, 12523, 778, 12516, 12540, 12489, 778, 12516, 12540, 12523, 778, + 12518, 12450, 12531, 1034, 12522, 12483, 12488, 12523, 522, 12522, 12521, + 778, 12523, 12500, 12540, 1034, 12523, 12540, 12502, 12523, 522, 12524, + 12512, 1290, 12524, 12531, 12488, 12466, 12531, 778, 12527, 12483, 12488, + 514, 48, 28857, 514, 49, 28857, 514, 50, 28857, 514, 51, 28857, 514, 52, + 28857, 514, 53, 28857, 514, 54, 28857, 514, 55, 28857, 514, 56, 28857, + 514, 57, 28857, 770, 49, 48, 28857, 770, 49, 49, 28857, 770, 49, 50, + 28857, 770, 49, 51, 28857, 770, 49, 52, 28857, 770, 49, 53, 28857, 770, + 49, 54, 28857, 770, 49, 55, 28857, 770, 49, 56, 28857, 770, 49, 57, + 28857, 770, 50, 48, 28857, 770, 50, 49, 28857, 770, 50, 50, 28857, 770, + 50, 51, 28857, 770, 50, 52, 28857, 778, 104, 80, 97, 522, 100, 97, 522, + 65, 85, 778, 98, 97, 114, 522, 111, 86, 522, 112, 99, 522, 100, 109, 778, + 100, 109, 178, 778, 100, 109, 179, 522, 73, 85, 522, 24179, 25104, 522, + 26157, 21644, 522, 22823, 27491, 522, 26126, 27835, 1034, 26666, 24335, + 20250, 31038, 522, 112, 65, 522, 110, 65, 522, 956, 65, 522, 109, 65, + 522, 107, 65, 522, 75, 66, 522, 77, 66, 522, 71, 66, 778, 99, 97, 108, + 1034, 107, 99, 97, 108, 522, 112, 70, 522, 110, 70, 522, 956, 70, 522, + 956, 103, 522, 109, 103, 522, 107, 103, 522, 72, 122, 778, 107, 72, 122, + 778, 77, 72, 122, 778, 71, 72, 122, 778, 84, 72, 122, 522, 956, 8467, + 522, 109, 8467, 522, 100, 8467, 522, 107, 8467, 522, 102, 109, 522, 110, + 109, 522, 956, 109, 522, 109, 109, 522, 99, 109, 522, 107, 109, 778, 109, + 109, 178, 778, 99, 109, 178, 522, 109, 178, 778, 107, 109, 178, 778, 109, + 109, 179, 778, 99, 109, 179, 522, 109, 179, 778, 107, 109, 179, 778, 109, + 8725, 115, 1034, 109, 8725, 115, 178, 522, 80, 97, 778, 107, 80, 97, 778, + 77, 80, 97, 778, 71, 80, 97, 778, 114, 97, 100, 1290, 114, 97, 100, 8725, + 115, 1546, 114, 97, 100, 8725, 115, 178, 522, 112, 115, 522, 110, 115, + 522, 956, 115, 522, 109, 115, 522, 112, 86, 522, 110, 86, 522, 956, 86, + 522, 109, 86, 522, 107, 86, 522, 77, 86, 522, 112, 87, 522, 110, 87, 522, + 956, 87, 522, 109, 87, 522, 107, 87, 522, 77, 87, 522, 107, 937, 522, 77, + 937, 1034, 97, 46, 109, 46, 522, 66, 113, 522, 99, 99, 522, 99, 100, + 1034, 67, 8725, 107, 103, 778, 67, 111, 46, 522, 100, 66, 522, 71, 121, + 522, 104, 97, 522, 72, 80, 522, 105, 110, 522, 75, 75, 522, 75, 77, 522, + 107, 116, 522, 108, 109, 522, 108, 110, 778, 108, 111, 103, 522, 108, + 120, 522, 109, 98, 778, 109, 105, 108, 778, 109, 111, 108, 522, 80, 72, + 1034, 112, 46, 109, 46, 778, 80, 80, 77, 522, 80, 82, 522, 115, 114, 522, + 83, 118, 522, 87, 98, 778, 86, 8725, 109, 778, 65, 8725, 109, 514, 49, + 26085, 514, 50, 26085, 514, 51, 26085, 514, 52, 26085, 514, 53, 26085, + 514, 54, 26085, 514, 55, 26085, 514, 56, 26085, 514, 57, 26085, 770, 49, + 48, 26085, 770, 49, 49, 26085, 770, 49, 50, 26085, 770, 49, 51, 26085, + 770, 49, 52, 26085, 770, 49, 53, 26085, 770, 49, 54, 26085, 770, 49, 55, + 26085, 770, 49, 56, 26085, 770, 49, 57, 26085, 770, 50, 48, 26085, 770, + 50, 49, 26085, 770, 50, 50, 26085, 770, 50, 51, 26085, 770, 50, 52, + 26085, 770, 50, 53, 26085, 770, 50, 54, 26085, 770, 50, 55, 26085, 770, + 50, 56, 26085, 770, 50, 57, 26085, 770, 51, 48, 26085, 770, 51, 49, + 26085, 778, 103, 97, 108, 259, 1098, 259, 1100, 259, 42863, 259, 294, + 259, 339, 259, 42791, 259, 43831, 259, 619, 259, 43858, 256, 35912, 256, + 26356, 256, 36554, 256, 36040, 256, 28369, 256, 20018, 256, 21477, 256, + 40860, 256, 40860, 256, 22865, 256, 37329, 256, 21895, 256, 22856, 256, + 25078, 256, 30313, 256, 32645, 256, 34367, 256, 34746, 256, 35064, 256, + 37007, 256, 27138, 256, 27931, 256, 28889, 256, 29662, 256, 33853, 256, + 37226, 256, 39409, 256, 20098, 256, 21365, 256, 27396, 256, 29211, 256, + 34349, 256, 40478, 256, 23888, 256, 28651, 256, 34253, 256, 35172, 256, + 25289, 256, 33240, 256, 34847, 256, 24266, 256, 26391, 256, 28010, 256, + 29436, 256, 37070, 256, 20358, 256, 20919, 256, 21214, 256, 25796, 256, + 27347, 256, 29200, 256, 30439, 256, 32769, 256, 34310, 256, 34396, 256, + 36335, 256, 38706, 256, 39791, 256, 40442, 256, 30860, 256, 31103, 256, + 32160, 256, 33737, 256, 37636, 256, 40575, 256, 35542, 256, 22751, 256, + 24324, 256, 31840, 256, 32894, 256, 29282, 256, 30922, 256, 36034, 256, + 38647, 256, 22744, 256, 23650, 256, 27155, 256, 28122, 256, 28431, 256, + 32047, 256, 32311, 256, 38475, 256, 21202, 256, 32907, 256, 20956, 256, + 20940, 256, 31260, 256, 32190, 256, 33777, 256, 38517, 256, 35712, 256, + 25295, 256, 27138, 256, 35582, 256, 20025, 256, 23527, 256, 24594, 256, + 29575, 256, 30064, 256, 21271, 256, 30971, 256, 20415, 256, 24489, 256, + 19981, 256, 27852, 256, 25976, 256, 32034, 256, 21443, 256, 22622, 256, + 30465, 256, 33865, 256, 35498, 256, 27578, 256, 36784, 256, 27784, 256, + 25342, 256, 33509, 256, 25504, 256, 30053, 256, 20142, 256, 20841, 256, + 20937, 256, 26753, 256, 31975, 256, 33391, 256, 35538, 256, 37327, 256, + 21237, 256, 21570, 256, 22899, 256, 24300, 256, 26053, 256, 28670, 256, + 31018, 256, 38317, 256, 39530, 256, 40599, 256, 40654, 256, 21147, 256, + 26310, 256, 27511, 256, 36706, 256, 24180, 256, 24976, 256, 25088, 256, + 25754, 256, 28451, 256, 29001, 256, 29833, 256, 31178, 256, 32244, 256, + 32879, 256, 36646, 256, 34030, 256, 36899, 256, 37706, 256, 21015, 256, + 21155, 256, 21693, 256, 28872, 256, 35010, 256, 35498, 256, 24265, 256, + 24565, 256, 25467, 256, 27566, 256, 31806, 256, 29557, 256, 20196, 256, + 22265, 256, 23527, 256, 23994, 256, 24604, 256, 29618, 256, 29801, 256, + 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, 38936, 256, + 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, 20102, 256, + 20698, 256, 23534, 256, 23615, 256, 26009, 256, 27138, 256, 29134, 256, + 30274, 256, 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, + 21129, 256, 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, + 30041, 256, 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, + 38520, 256, 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, + 24900, 256, 26647, 256, 29575, 256, 38534, 256, 21033, 256, 21519, 256, + 23653, 256, 26131, 256, 26446, 256, 26792, 256, 27877, 256, 29702, 256, + 30178, 256, 32633, 256, 35023, 256, 35041, 256, 37324, 256, 38626, 256, + 21311, 256, 28346, 256, 21533, 256, 29136, 256, 29848, 256, 34298, 256, + 38563, 256, 40023, 256, 40607, 256, 26519, 256, 28107, 256, 33256, 256, + 31435, 256, 31520, 256, 31890, 256, 29376, 256, 28825, 256, 35672, 256, + 20160, 256, 33590, 256, 21050, 256, 20999, 256, 24230, 256, 25299, 256, + 31958, 256, 23429, 256, 27934, 256, 26292, 256, 36667, 256, 34892, 256, + 38477, 256, 35211, 256, 24275, 256, 20800, 256, 21952, 256, 22618, 256, + 26228, 256, 20958, 256, 29482, 256, 30410, 256, 31036, 256, 31070, 256, + 31077, 256, 31119, 256, 38742, 256, 31934, 256, 32701, 256, 34322, 256, + 35576, 256, 36920, 256, 37117, 256, 39151, 256, 39164, 256, 39208, 256, + 40372, 256, 37086, 256, 38583, 256, 20398, 256, 20711, 256, 20813, 256, + 21193, 256, 21220, 256, 21329, 256, 21917, 256, 22022, 256, 22120, 256, + 22592, 256, 22696, 256, 23652, 256, 23662, 256, 24724, 256, 24936, 256, + 24974, 256, 25074, 256, 25935, 256, 26082, 256, 26257, 256, 26757, 256, + 28023, 256, 28186, 256, 28450, 256, 29038, 256, 29227, 256, 29730, 256, + 30865, 256, 31038, 256, 31049, 256, 31048, 256, 31056, 256, 31062, 256, + 31069, 256, 31117, 256, 31118, 256, 31296, 256, 31361, 256, 31680, 256, + 32244, 256, 32265, 256, 32321, 256, 32626, 256, 32773, 256, 33261, 256, + 33401, 256, 33401, 256, 33879, 256, 35088, 256, 35222, 256, 35585, 256, + 35641, 256, 36051, 256, 36104, 256, 36790, 256, 36920, 256, 38627, 256, + 38911, 256, 38971, 256, 24693, 256, 55376, 57070, 256, 33304, 256, 20006, + 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, 20864, 256, 21191, + 256, 21242, 256, 21917, 256, 21845, 256, 21913, 256, 21986, 256, 22618, + 256, 22707, 256, 22852, 256, 22868, 256, 23138, 256, 23336, 256, 24274, + 256, 24281, 256, 24425, 256, 24493, 256, 24792, 256, 24910, 256, 24840, + 256, 24974, 256, 24928, 256, 25074, 256, 25140, 256, 25540, 256, 25628, + 256, 25682, 256, 25942, 256, 26228, 256, 26391, 256, 26395, 256, 26454, + 256, 27513, 256, 27578, 256, 27969, 256, 28379, 256, 28363, 256, 28450, + 256, 28702, 256, 29038, 256, 30631, 256, 29237, 256, 29359, 256, 29482, + 256, 29809, 256, 29958, 256, 30011, 256, 30237, 256, 30239, 256, 30410, + 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, 30924, 256, 31409, + 256, 31680, 256, 31867, 256, 32091, 256, 32244, 256, 32574, 256, 32773, + 256, 33618, 256, 33775, 256, 34681, 256, 35137, 256, 35206, 256, 35222, + 256, 35519, 256, 35576, 256, 35531, 256, 35585, 256, 35582, 256, 35565, + 256, 35641, 256, 35722, 256, 36104, 256, 36664, 256, 36978, 256, 37273, + 256, 37494, 256, 38524, 256, 38627, 256, 38742, 256, 38875, 256, 38911, + 256, 38923, 256, 38971, 256, 39698, 256, 40860, 256, 55370, 56394, 256, + 55370, 56388, 256, 55372, 57301, 256, 15261, 256, 16408, 256, 16441, 256, + 55380, 56905, 256, 55383, 56528, 256, 55391, 57043, 256, 40771, 256, + 40846, 514, 102, 102, 514, 102, 105, 514, 102, 108, 770, 102, 102, 105, + 770, 102, 102, 108, 514, 383, 116, 514, 115, 116, 514, 1396, 1398, 514, + 1396, 1381, 514, 1396, 1387, 514, 1406, 1398, 514, 1396, 1389, 512, 1497, + 1460, 512, 1522, 1463, 262, 1506, 262, 1488, 262, 1491, 262, 1492, 262, + 1499, 262, 1500, 262, 1501, 262, 1512, 262, 1514, 262, 43, 512, 1513, + 1473, 512, 1513, 1474, 512, 64329, 1473, 512, 64329, 1474, 512, 1488, + 1463, 512, 1488, 1464, 512, 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, + 512, 1491, 1468, 512, 1492, 1468, 512, 1493, 1468, 512, 1494, 1468, 512, + 1496, 1468, 512, 1497, 1468, 512, 1498, 1468, 512, 1499, 1468, 512, 1500, + 1468, 512, 1502, 1468, 512, 1504, 1468, 512, 1505, 1468, 512, 1507, 1468, + 512, 1508, 1468, 512, 1510, 1468, 512, 1511, 1468, 512, 1512, 1468, 512, + 1513, 1468, 512, 1514, 1468, 512, 1493, 1465, 512, 1489, 1471, 512, 1499, + 1471, 512, 1508, 1471, 514, 1488, 1500, 267, 1649, 268, 1649, 267, 1659, + 268, 1659, 269, 1659, 270, 1659, 267, 1662, 268, 1662, 269, 1662, 270, + 1662, 267, 1664, 268, 1664, 269, 1664, 270, 1664, 267, 1658, 268, 1658, + 269, 1658, 270, 1658, 267, 1663, 268, 1663, 269, 1663, 270, 1663, 267, + 1657, 268, 1657, 269, 1657, 270, 1657, 267, 1700, 268, 1700, 269, 1700, + 270, 1700, 267, 1702, 268, 1702, 269, 1702, 270, 1702, 267, 1668, 268, + 1668, 269, 1668, 270, 1668, 267, 1667, 268, 1667, 269, 1667, 270, 1667, + 267, 1670, 268, 1670, 269, 1670, 270, 1670, 267, 1671, 268, 1671, 269, + 1671, 270, 1671, 267, 1677, 268, 1677, 267, 1676, 268, 1676, 267, 1678, + 268, 1678, 267, 1672, 268, 1672, 267, 1688, 268, 1688, 267, 1681, 268, + 1681, 267, 1705, 268, 1705, 269, 1705, 270, 1705, 267, 1711, 268, 1711, + 269, 1711, 270, 1711, 267, 1715, 268, 1715, 269, 1715, 270, 1715, 267, + 1713, 268, 1713, 269, 1713, 270, 1713, 267, 1722, 268, 1722, 267, 1723, + 268, 1723, 269, 1723, 270, 1723, 267, 1728, 268, 1728, 267, 1729, 268, + 1729, 269, 1729, 270, 1729, 267, 1726, 268, 1726, 269, 1726, 270, 1726, + 267, 1746, 268, 1746, 267, 1747, 268, 1747, 267, 1709, 268, 1709, 269, + 1709, 270, 1709, 267, 1735, 268, 1735, 267, 1734, 268, 1734, 267, 1736, + 268, 1736, 267, 1655, 267, 1739, 268, 1739, 267, 1733, 268, 1733, 267, + 1737, 268, 1737, 267, 1744, 268, 1744, 269, 1744, 270, 1744, 269, 1609, + 270, 1609, 523, 1574, 1575, 524, 1574, 1575, 523, 1574, 1749, 524, 1574, + 1749, 523, 1574, 1608, 524, 1574, 1608, 523, 1574, 1735, 524, 1574, 1735, + 523, 1574, 1734, 524, 1574, 1734, 523, 1574, 1736, 524, 1574, 1736, 523, + 1574, 1744, 524, 1574, 1744, 525, 1574, 1744, 523, 1574, 1609, 524, 1574, + 1609, 525, 1574, 1609, 267, 1740, 268, 1740, 269, 1740, 270, 1740, 523, + 1574, 1580, 523, 1574, 1581, 523, 1574, 1605, 523, 1574, 1609, 523, 1574, + 1610, 523, 1576, 1580, 523, 1576, 1581, 523, 1576, 1582, 523, 1576, 1605, + 523, 1576, 1609, 523, 1576, 1610, 523, 1578, 1580, 523, 1578, 1581, 523, + 1578, 1582, 523, 1578, 1605, 523, 1578, 1609, 523, 1578, 1610, 523, 1579, + 1580, 523, 1579, 1605, 523, 1579, 1609, 523, 1579, 1610, 523, 1580, 1581, + 523, 1580, 1605, 523, 1581, 1580, 523, 1581, 1605, 523, 1582, 1580, 523, + 1582, 1581, 523, 1582, 1605, 523, 1587, 1580, 523, 1587, 1581, 523, 1587, + 1582, 523, 1587, 1605, 523, 1589, 1581, 523, 1589, 1605, 523, 1590, 1580, + 523, 1590, 1581, 523, 1590, 1582, 523, 1590, 1605, 523, 1591, 1581, 523, + 1591, 1605, 523, 1592, 1605, 523, 1593, 1580, 523, 1593, 1605, 523, 1594, + 1580, 523, 1594, 1605, 523, 1601, 1580, 523, 1601, 1581, 523, 1601, 1582, + 523, 1601, 1605, 523, 1601, 1609, 523, 1601, 1610, 523, 1602, 1581, 523, + 1602, 1605, 523, 1602, 1609, 523, 1602, 1610, 523, 1603, 1575, 523, 1603, + 1580, 523, 1603, 1581, 523, 1603, 1582, 523, 1603, 1604, 523, 1603, 1605, + 523, 1603, 1609, 523, 1603, 1610, 523, 1604, 1580, 523, 1604, 1581, 523, + 1604, 1582, 523, 1604, 1605, 523, 1604, 1609, 523, 1604, 1610, 523, 1605, + 1580, 523, 1605, 1581, 523, 1605, 1582, 523, 1605, 1605, 523, 1605, 1609, + 523, 1605, 1610, 523, 1606, 1580, 523, 1606, 1581, 523, 1606, 1582, 523, + 1606, 1605, 523, 1606, 1609, 523, 1606, 1610, 523, 1607, 1580, 523, 1607, + 1605, 523, 1607, 1609, 523, 1607, 1610, 523, 1610, 1580, 523, 1610, 1581, + 523, 1610, 1582, 523, 1610, 1605, 523, 1610, 1609, 523, 1610, 1610, 523, + 1584, 1648, 523, 1585, 1648, 523, 1609, 1648, 779, 32, 1612, 1617, 779, + 32, 1613, 1617, 779, 32, 1614, 1617, 779, 32, 1615, 1617, 779, 32, 1616, + 1617, 779, 32, 1617, 1648, 524, 1574, 1585, 524, 1574, 1586, 524, 1574, + 1605, 524, 1574, 1606, 524, 1574, 1609, 524, 1574, 1610, 524, 1576, 1585, + 524, 1576, 1586, 524, 1576, 1605, 524, 1576, 1606, 524, 1576, 1609, 524, + 1576, 1610, 524, 1578, 1585, 524, 1578, 1586, 524, 1578, 1605, 524, 1578, + 1606, 524, 1578, 1609, 524, 1578, 1610, 524, 1579, 1585, 524, 1579, 1586, + 524, 1579, 1605, 524, 1579, 1606, 524, 1579, 1609, 524, 1579, 1610, 524, + 1601, 1609, 524, 1601, 1610, 524, 1602, 1609, 524, 1602, 1610, 524, 1603, + 1575, 524, 1603, 1604, 524, 1603, 1605, 524, 1603, 1609, 524, 1603, 1610, + 524, 1604, 1605, 524, 1604, 1609, 524, 1604, 1610, 524, 1605, 1575, 524, + 1605, 1605, 524, 1606, 1585, 524, 1606, 1586, 524, 1606, 1605, 524, 1606, + 1606, 524, 1606, 1609, 524, 1606, 1610, 524, 1609, 1648, 524, 1610, 1585, + 524, 1610, 1586, 524, 1610, 1605, 524, 1610, 1606, 524, 1610, 1609, 524, + 1610, 1610, 525, 1574, 1580, 525, 1574, 1581, 525, 1574, 1582, 525, 1574, + 1605, 525, 1574, 1607, 525, 1576, 1580, 525, 1576, 1581, 525, 1576, 1582, + 525, 1576, 1605, 525, 1576, 1607, 525, 1578, 1580, 525, 1578, 1581, 525, + 1578, 1582, 525, 1578, 1605, 525, 1578, 1607, 525, 1579, 1605, 525, 1580, + 1581, 525, 1580, 1605, 525, 1581, 1580, 525, 1581, 1605, 525, 1582, 1580, + 525, 1582, 1605, 525, 1587, 1580, 525, 1587, 1581, 525, 1587, 1582, 525, + 1587, 1605, 525, 1589, 1581, 525, 1589, 1582, 525, 1589, 1605, 525, 1590, + 1580, 525, 1590, 1581, 525, 1590, 1582, 525, 1590, 1605, 525, 1591, 1581, + 525, 1592, 1605, 525, 1593, 1580, 525, 1593, 1605, 525, 1594, 1580, 525, + 1594, 1605, 525, 1601, 1580, 525, 1601, 1581, 525, 1601, 1582, 525, 1601, + 1605, 525, 1602, 1581, 525, 1602, 1605, 525, 1603, 1580, 525, 1603, 1581, + 525, 1603, 1582, 525, 1603, 1604, 525, 1603, 1605, 525, 1604, 1580, 525, + 1604, 1581, 525, 1604, 1582, 525, 1604, 1605, 525, 1604, 1607, 525, 1605, + 1580, 525, 1605, 1581, 525, 1605, 1582, 525, 1605, 1605, 525, 1606, 1580, + 525, 1606, 1581, 525, 1606, 1582, 525, 1606, 1605, 525, 1606, 1607, 525, + 1607, 1580, 525, 1607, 1605, 525, 1607, 1648, 525, 1610, 1580, 525, 1610, + 1581, 525, 1610, 1582, 525, 1610, 1605, 525, 1610, 1607, 526, 1574, 1605, + 526, 1574, 1607, 526, 1576, 1605, 526, 1576, 1607, 526, 1578, 1605, 526, + 1578, 1607, 526, 1579, 1605, 526, 1579, 1607, 526, 1587, 1605, 526, 1587, + 1607, 526, 1588, 1605, 526, 1588, 1607, 526, 1603, 1604, 526, 1603, 1605, + 526, 1604, 1605, 526, 1606, 1605, 526, 1606, 1607, 526, 1610, 1605, 526, + 1610, 1607, 782, 1600, 1614, 1617, 782, 1600, 1615, 1617, 782, 1600, + 1616, 1617, 523, 1591, 1609, 523, 1591, 1610, 523, 1593, 1609, 523, 1593, + 1610, 523, 1594, 1609, 523, 1594, 1610, 523, 1587, 1609, 523, 1587, 1610, + 523, 1588, 1609, 523, 1588, 1610, 523, 1581, 1609, 523, 1581, 1610, 523, + 1580, 1609, 523, 1580, 1610, 523, 1582, 1609, 523, 1582, 1610, 523, 1589, + 1609, 523, 1589, 1610, 523, 1590, 1609, 523, 1590, 1610, 523, 1588, 1580, + 523, 1588, 1581, 523, 1588, 1582, 523, 1588, 1605, 523, 1588, 1585, 523, + 1587, 1585, 523, 1589, 1585, 523, 1590, 1585, 524, 1591, 1609, 524, 1591, + 1610, 524, 1593, 1609, 524, 1593, 1610, 524, 1594, 1609, 524, 1594, 1610, + 524, 1587, 1609, 524, 1587, 1610, 524, 1588, 1609, 524, 1588, 1610, 524, + 1581, 1609, 524, 1581, 1610, 524, 1580, 1609, 524, 1580, 1610, 524, 1582, + 1609, 524, 1582, 1610, 524, 1589, 1609, 524, 1589, 1610, 524, 1590, 1609, + 524, 1590, 1610, 524, 1588, 1580, 524, 1588, 1581, 524, 1588, 1582, 524, + 1588, 1605, 524, 1588, 1585, 524, 1587, 1585, 524, 1589, 1585, 524, 1590, + 1585, 525, 1588, 1580, 525, 1588, 1581, 525, 1588, 1582, 525, 1588, 1605, + 525, 1587, 1607, 525, 1588, 1607, 525, 1591, 1605, 526, 1587, 1580, 526, + 1587, 1581, 526, 1587, 1582, 526, 1588, 1580, 526, 1588, 1581, 526, 1588, + 1582, 526, 1591, 1605, 526, 1592, 1605, 524, 1575, 1611, 523, 1575, 1611, + 781, 1578, 1580, 1605, 780, 1578, 1581, 1580, 781, 1578, 1581, 1580, 781, + 1578, 1581, 1605, 781, 1578, 1582, 1605, 781, 1578, 1605, 1580, 781, + 1578, 1605, 1581, 781, 1578, 1605, 1582, 780, 1580, 1605, 1581, 781, + 1580, 1605, 1581, 780, 1581, 1605, 1610, 780, 1581, 1605, 1609, 781, + 1587, 1581, 1580, 781, 1587, 1580, 1581, 780, 1587, 1580, 1609, 780, + 1587, 1605, 1581, 781, 1587, 1605, 1581, 781, 1587, 1605, 1580, 780, + 1587, 1605, 1605, 781, 1587, 1605, 1605, 780, 1589, 1581, 1581, 781, + 1589, 1581, 1581, 780, 1589, 1605, 1605, 780, 1588, 1581, 1605, 781, + 1588, 1581, 1605, 780, 1588, 1580, 1610, 780, 1588, 1605, 1582, 781, + 1588, 1605, 1582, 780, 1588, 1605, 1605, 781, 1588, 1605, 1605, 780, + 1590, 1581, 1609, 780, 1590, 1582, 1605, 781, 1590, 1582, 1605, 780, + 1591, 1605, 1581, 781, 1591, 1605, 1581, 781, 1591, 1605, 1605, 780, + 1591, 1605, 1610, 780, 1593, 1580, 1605, 780, 1593, 1605, 1605, 781, + 1593, 1605, 1605, 780, 1593, 1605, 1609, 780, 1594, 1605, 1605, 780, + 1594, 1605, 1610, 780, 1594, 1605, 1609, 780, 1601, 1582, 1605, 781, + 1601, 1582, 1605, 780, 1602, 1605, 1581, 780, 1602, 1605, 1605, 780, + 1604, 1581, 1605, 780, 1604, 1581, 1610, 780, 1604, 1581, 1609, 781, + 1604, 1580, 1580, 780, 1604, 1580, 1580, 780, 1604, 1582, 1605, 781, + 1604, 1582, 1605, 780, 1604, 1605, 1581, 781, 1604, 1605, 1581, 781, + 1605, 1581, 1580, 781, 1605, 1581, 1605, 780, 1605, 1581, 1610, 781, + 1605, 1580, 1581, 781, 1605, 1580, 1605, 781, 1605, 1582, 1580, 781, + 1605, 1582, 1605, 781, 1605, 1580, 1582, 781, 1607, 1605, 1580, 781, + 1607, 1605, 1605, 781, 1606, 1581, 1605, 780, 1606, 1581, 1609, 780, + 1606, 1580, 1605, 781, 1606, 1580, 1605, 780, 1606, 1580, 1609, 780, + 1606, 1605, 1610, 780, 1606, 1605, 1609, 780, 1610, 1605, 1605, 781, + 1610, 1605, 1605, 780, 1576, 1582, 1610, 780, 1578, 1580, 1610, 780, + 1578, 1580, 1609, 780, 1578, 1582, 1610, 780, 1578, 1582, 1609, 780, + 1578, 1605, 1610, 780, 1578, 1605, 1609, 780, 1580, 1605, 1610, 780, + 1580, 1581, 1609, 780, 1580, 1605, 1609, 780, 1587, 1582, 1609, 780, + 1589, 1581, 1610, 780, 1588, 1581, 1610, 780, 1590, 1581, 1610, 780, + 1604, 1580, 1610, 780, 1604, 1605, 1610, 780, 1610, 1581, 1610, 780, + 1610, 1580, 1610, 780, 1610, 1605, 1610, 780, 1605, 1605, 1610, 780, + 1602, 1605, 1610, 780, 1606, 1581, 1610, 781, 1602, 1605, 1581, 781, + 1604, 1581, 1605, 780, 1593, 1605, 1610, 780, 1603, 1605, 1610, 781, + 1606, 1580, 1581, 780, 1605, 1582, 1610, 781, 1604, 1580, 1605, 780, + 1603, 1605, 1605, 780, 1604, 1580, 1605, 780, 1606, 1580, 1581, 780, + 1580, 1581, 1610, 780, 1581, 1580, 1610, 780, 1605, 1580, 1610, 780, + 1601, 1605, 1610, 780, 1576, 1581, 1610, 781, 1603, 1605, 1605, 781, + 1593, 1580, 1605, 781, 1589, 1605, 1605, 780, 1587, 1582, 1610, 780, + 1606, 1580, 1610, 779, 1589, 1604, 1746, 779, 1602, 1604, 1746, 1035, + 1575, 1604, 1604, 1607, 1035, 1575, 1603, 1576, 1585, 1035, 1605, 1581, + 1605, 1583, 1035, 1589, 1604, 1593, 1605, 1035, 1585, 1587, 1608, 1604, + 1035, 1593, 1604, 1610, 1607, 1035, 1608, 1587, 1604, 1605, 779, 1589, + 1604, 1609, 4619, 1589, 1604, 1609, 32, 1575, 1604, 1604, 1607, 32, 1593, + 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, 2059, 1580, 1604, 32, 1580, + 1604, 1575, 1604, 1607, 1035, 1585, 1740, 1575, 1604, 265, 44, 265, + 12289, 265, 12290, 265, 58, 265, 59, 265, 33, 265, 63, 265, 12310, 265, + 12311, 265, 8230, 265, 8229, 265, 8212, 265, 8211, 265, 95, 265, 95, 265, + 40, 265, 41, 265, 123, 265, 125, 265, 12308, 265, 12309, 265, 12304, 265, + 12305, 265, 12298, 265, 12299, 265, 12296, 265, 12297, 265, 12300, 265, + 12301, 265, 12302, 265, 12303, 265, 91, 265, 93, 258, 8254, 258, 8254, + 258, 8254, 258, 8254, 258, 95, 258, 95, 258, 95, 271, 44, 271, 12289, + 271, 46, 271, 59, 271, 58, 271, 63, 271, 33, 271, 8212, 271, 40, 271, 41, + 271, 123, 271, 125, 271, 12308, 271, 12309, 271, 35, 271, 38, 271, 42, + 271, 43, 271, 45, 271, 60, 271, 62, 271, 61, 271, 92, 271, 36, 271, 37, + 271, 64, 523, 32, 1611, 526, 1600, 1611, 523, 32, 1612, 523, 32, 1613, + 523, 32, 1614, 526, 1600, 1614, 523, 32, 1615, 526, 1600, 1615, 523, 32, + 1616, 526, 1600, 1616, 523, 32, 1617, 526, 1600, 1617, 523, 32, 1618, + 526, 1600, 1618, 267, 1569, 267, 1570, 268, 1570, 267, 1571, 268, 1571, + 267, 1572, 268, 1572, 267, 1573, 268, 1573, 267, 1574, 268, 1574, 269, + 1574, 270, 1574, 267, 1575, 268, 1575, 267, 1576, 268, 1576, 269, 1576, + 270, 1576, 267, 1577, 268, 1577, 267, 1578, 268, 1578, 269, 1578, 270, + 1578, 267, 1579, 268, 1579, 269, 1579, 270, 1579, 267, 1580, 268, 1580, + 269, 1580, 270, 1580, 267, 1581, 268, 1581, 269, 1581, 270, 1581, 267, + 1582, 268, 1582, 269, 1582, 270, 1582, 267, 1583, 268, 1583, 267, 1584, + 268, 1584, 267, 1585, 268, 1585, 267, 1586, 268, 1586, 267, 1587, 268, + 1587, 269, 1587, 270, 1587, 267, 1588, 268, 1588, 269, 1588, 270, 1588, + 267, 1589, 268, 1589, 269, 1589, 270, 1589, 267, 1590, 268, 1590, 269, + 1590, 270, 1590, 267, 1591, 268, 1591, 269, 1591, 270, 1591, 267, 1592, + 268, 1592, 269, 1592, 270, 1592, 267, 1593, 268, 1593, 269, 1593, 270, + 1593, 267, 1594, 268, 1594, 269, 1594, 270, 1594, 267, 1601, 268, 1601, + 269, 1601, 270, 1601, 267, 1602, 268, 1602, 269, 1602, 270, 1602, 267, + 1603, 268, 1603, 269, 1603, 270, 1603, 267, 1604, 268, 1604, 269, 1604, + 270, 1604, 267, 1605, 268, 1605, 269, 1605, 270, 1605, 267, 1606, 268, + 1606, 269, 1606, 270, 1606, 267, 1607, 268, 1607, 269, 1607, 270, 1607, + 267, 1608, 268, 1608, 267, 1609, 268, 1609, 267, 1610, 268, 1610, 269, + 1610, 270, 1610, 523, 1604, 1570, 524, 1604, 1570, 523, 1604, 1571, 524, + 1604, 1571, 523, 1604, 1573, 524, 1604, 1573, 523, 1604, 1575, 524, 1604, + 1575, 264, 33, 264, 34, 264, 35, 264, 36, 264, 37, 264, 38, 264, 39, 264, + 40, 264, 41, 264, 42, 264, 43, 264, 44, 264, 45, 264, 46, 264, 47, 264, + 48, 264, 49, 264, 50, 264, 51, 264, 52, 264, 53, 264, 54, 264, 55, 264, + 56, 264, 57, 264, 58, 264, 59, 264, 60, 264, 61, 264, 62, 264, 63, 264, + 64, 264, 65, 264, 66, 264, 67, 264, 68, 264, 69, 264, 70, 264, 71, 264, + 72, 264, 73, 264, 74, 264, 75, 264, 76, 264, 77, 264, 78, 264, 79, 264, + 80, 264, 81, 264, 82, 264, 83, 264, 84, 264, 85, 264, 86, 264, 87, 264, + 88, 264, 89, 264, 90, 264, 91, 264, 92, 264, 93, 264, 94, 264, 95, 264, + 96, 264, 97, 264, 98, 264, 99, 264, 100, 264, 101, 264, 102, 264, 103, + 264, 104, 264, 105, 264, 106, 264, 107, 264, 108, 264, 109, 264, 110, + 264, 111, 264, 112, 264, 113, 264, 114, 264, 115, 264, 116, 264, 117, + 264, 118, 264, 119, 264, 120, 264, 121, 264, 122, 264, 123, 264, 124, + 264, 125, 264, 126, 264, 10629, 264, 10630, 272, 12290, 272, 12300, 272, + 12301, 272, 12289, 272, 12539, 272, 12530, 272, 12449, 272, 12451, 272, + 12453, 272, 12455, 272, 12457, 272, 12515, 272, 12517, 272, 12519, 272, + 12483, 272, 12540, 272, 12450, 272, 12452, 272, 12454, 272, 12456, 272, + 12458, 272, 12459, 272, 12461, 272, 12463, 272, 12465, 272, 12467, 272, + 12469, 272, 12471, 272, 12473, 272, 12475, 272, 12477, 272, 12479, 272, + 12481, 272, 12484, 272, 12486, 272, 12488, 272, 12490, 272, 12491, 272, + 12492, 272, 12493, 272, 12494, 272, 12495, 272, 12498, 272, 12501, 272, + 12504, 272, 12507, 272, 12510, 272, 12511, 272, 12512, 272, 12513, 272, + 12514, 272, 12516, 272, 12518, 272, 12520, 272, 12521, 272, 12522, 272, + 12523, 272, 12524, 272, 12525, 272, 12527, 272, 12531, 272, 12441, 272, + 12442, 272, 12644, 272, 12593, 272, 12594, 272, 12595, 272, 12596, 272, + 12597, 272, 12598, 272, 12599, 272, 12600, 272, 12601, 272, 12602, 272, + 12603, 272, 12604, 272, 12605, 272, 12606, 272, 12607, 272, 12608, 272, + 12609, 272, 12610, 272, 12611, 272, 12612, 272, 12613, 272, 12614, 272, + 12615, 272, 12616, 272, 12617, 272, 12618, 272, 12619, 272, 12620, 272, + 12621, 272, 12622, 272, 12623, 272, 12624, 272, 12625, 272, 12626, 272, + 12627, 272, 12628, 272, 12629, 272, 12630, 272, 12631, 272, 12632, 272, + 12633, 272, 12634, 272, 12635, 272, 12636, 272, 12637, 272, 12638, 272, + 12639, 272, 12640, 272, 12641, 272, 12642, 272, 12643, 264, 162, 264, + 163, 264, 172, 264, 175, 264, 166, 264, 165, 264, 8361, 272, 9474, 272, + 8592, 272, 8593, 272, 8594, 272, 8595, 272, 9632, 272, 9675, 512, 55300, + 56473, 55300, 56506, 512, 55300, 56475, 55300, 56506, 512, 55300, 56485, + 55300, 56506, 512, 55300, 56625, 55300, 56615, 512, 55300, 56626, 55300, + 56615, 512, 55300, 57159, 55300, 57150, 512, 55300, 57159, 55300, 57175, + 512, 55301, 56505, 55301, 56506, 512, 55301, 56505, 55301, 56496, 512, + 55301, 56505, 55301, 56509, 512, 55301, 56760, 55301, 56751, 512, 55301, + 56761, 55301, 56751, 512, 55348, 56663, 55348, 56677, 512, 55348, 56664, + 55348, 56677, 512, 55348, 56671, 55348, 56686, 512, 55348, 56671, 55348, + 56687, 512, 55348, 56671, 55348, 56688, 512, 55348, 56671, 55348, 56689, + 512, 55348, 56671, 55348, 56690, 512, 55348, 56761, 55348, 56677, 512, + 55348, 56762, 55348, 56677, 512, 55348, 56763, 55348, 56686, 512, 55348, + 56764, 55348, 56686, 512, 55348, 56763, 55348, 56687, 512, 55348, 56764, + 55348, 56687, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, + 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, + 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, + 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, + 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, + 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, + 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, + 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, + 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, + 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, + 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, + 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, + 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, + 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, + 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, + 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, + 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, + 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, + 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, + 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 67, 262, 68, + 262, 71, 262, 74, 262, 75, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, + 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 102, 262, 104, 262, 105, 262, 106, 262, + 107, 262, 108, 262, 109, 262, 110, 262, 112, 262, 113, 262, 114, 262, + 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, + 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, + 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, + 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, + 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, + 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 74, 262, 75, 262, 76, + 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, 262, 85, + 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, + 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, + 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, + 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, + 262, 122, 262, 65, 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 79, 262, 83, 262, 84, 262, 85, + 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, + 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, + 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, + 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, + 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, + 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, + 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, + 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, + 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, + 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, + 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, + 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, + 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, + 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, + 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, + 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, + 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, + 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, + 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, + 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, + 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, + 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, + 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, + 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, + 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, + 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, + 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, + 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, + 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, + 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, + 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, + 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, + 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, + 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, + 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, + 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, + 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, + 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, + 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, + 120, 262, 121, 262, 122, 262, 305, 262, 567, 262, 913, 262, 914, 262, + 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, + 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, + 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, + 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, + 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, + 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, + 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, + 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, + 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, + 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, + 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, + 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, + 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, + 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, + 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, + 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, + 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, + 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, + 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, + 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, + 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, + 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, + 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, + 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, + 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, + 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, + 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, + 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, + 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, + 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, + 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, + 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, + 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, + 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, + 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, + 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, + 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, + 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, + 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, + 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, + 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, + 982, 262, 988, 262, 989, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, + 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, + 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, + 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, + 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, + 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, + 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 1575, 262, 1576, 262, + 1580, 262, 1583, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, + 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, + 1601, 262, 1589, 262, 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, + 262, 1582, 262, 1584, 262, 1590, 262, 1592, 262, 1594, 262, 1646, 262, + 1722, 262, 1697, 262, 1647, 262, 1576, 262, 1580, 262, 1607, 262, 1581, + 262, 1610, 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, + 1593, 262, 1601, 262, 1589, 262, 1602, 262, 1588, 262, 1578, 262, 1579, + 262, 1582, 262, 1590, 262, 1594, 262, 1580, 262, 1581, 262, 1610, 262, + 1604, 262, 1606, 262, 1587, 262, 1593, 262, 1589, 262, 1602, 262, 1588, + 262, 1582, 262, 1590, 262, 1594, 262, 1722, 262, 1647, 262, 1576, 262, + 1580, 262, 1607, 262, 1581, 262, 1591, 262, 1610, 262, 1603, 262, 1605, + 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, + 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1590, 262, 1592, 262, 1594, + 262, 1646, 262, 1697, 262, 1575, 262, 1576, 262, 1580, 262, 1583, 262, + 1607, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, + 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, + 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, + 262, 1590, 262, 1592, 262, 1594, 262, 1576, 262, 1580, 262, 1583, 262, + 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, 262, 1605, + 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, + 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, 262, 1590, + 262, 1592, 262, 1594, 514, 48, 46, 514, 48, 44, 514, 49, 44, 514, 50, 44, + 514, 51, 44, 514, 52, 44, 514, 53, 44, 514, 54, 44, 514, 55, 44, 514, 56, + 44, 514, 57, 44, 770, 40, 65, 41, 770, 40, 66, 41, 770, 40, 67, 41, 770, + 40, 68, 41, 770, 40, 69, 41, 770, 40, 70, 41, 770, 40, 71, 41, 770, 40, + 72, 41, 770, 40, 73, 41, 770, 40, 74, 41, 770, 40, 75, 41, 770, 40, 76, + 41, 770, 40, 77, 41, 770, 40, 78, 41, 770, 40, 79, 41, 770, 40, 80, 41, + 770, 40, 81, 41, 770, 40, 82, 41, 770, 40, 83, 41, 770, 40, 84, 41, 770, + 40, 85, 41, 770, 40, 86, 41, 770, 40, 87, 41, 770, 40, 88, 41, 770, 40, + 89, 41, 770, 40, 90, 41, 770, 12308, 83, 12309, 263, 67, 263, 82, 519, + 67, 68, 519, 87, 90, 266, 65, 266, 66, 266, 67, 266, 68, 266, 69, 266, + 70, 266, 71, 266, 72, 266, 73, 266, 74, 266, 75, 266, 76, 266, 77, 266, + 78, 266, 79, 266, 80, 266, 81, 266, 82, 266, 83, 266, 84, 266, 85, 266, + 86, 266, 87, 266, 88, 266, 89, 266, 90, 522, 72, 86, 522, 77, 86, 522, + 83, 68, 522, 83, 83, 778, 80, 80, 86, 522, 87, 67, 515, 77, 67, 515, 77, + 68, 522, 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, 12469, 266, + 25163, 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, 22810, 266, + 35299, 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, 26009, 266, + 21069, 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, 32066, 266, + 29983, 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, 25237, 266, + 25429, 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, 20013, 266, + 21491, 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, 31354, 266, + 21512, 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, 21106, 266, + 21942, 770, 12308, 26412, 12309, 770, 12308, 19977, 12309, 770, 12308, + 20108, 12309, 770, 12308, 23433, 12309, 770, 12308, 28857, 12309, 770, + 12308, 25171, 12309, 770, 12308, 30423, 12309, 770, 12308, 21213, 12309, + 770, 12308, 25943, 12309, 263, 24471, 263, 21487, 256, 20029, 256, 20024, + 256, 20033, 256, 55360, 56610, 256, 20320, 256, 20398, 256, 20411, 256, + 20482, 256, 20602, 256, 20633, 256, 20711, 256, 20687, 256, 13470, 256, + 55361, 56890, 256, 20813, 256, 20820, 256, 20836, 256, 20855, 256, 55361, + 56604, 256, 13497, 256, 20839, 256, 20877, 256, 55361, 56651, 256, 20887, + 256, 20900, 256, 20172, 256, 20908, 256, 20917, 256, 55396, 56799, 256, + 20981, 256, 20995, 256, 13535, 256, 21051, 256, 21062, 256, 21106, 256, + 21111, 256, 13589, 256, 21191, 256, 21193, 256, 21220, 256, 21242, 256, + 21253, 256, 21254, 256, 21271, 256, 21321, 256, 21329, 256, 21338, 256, + 21363, 256, 21373, 256, 21375, 256, 21375, 256, 21375, 256, 55362, 56876, + 256, 28784, 256, 21450, 256, 21471, 256, 55362, 57187, 256, 21483, 256, + 21489, 256, 21510, 256, 21662, 256, 21560, 256, 21576, 256, 21608, 256, + 21666, 256, 21750, 256, 21776, 256, 21843, 256, 21859, 256, 21892, 256, + 21892, 256, 21913, 256, 21931, 256, 21939, 256, 21954, 256, 22294, 256, + 22022, 256, 22295, 256, 22097, 256, 22132, 256, 20999, 256, 22766, 256, + 22478, 256, 22516, 256, 22541, 256, 22411, 256, 22578, 256, 22577, 256, + 22700, 256, 55365, 56548, 256, 22770, 256, 22775, 256, 22790, 256, 22810, + 256, 22818, 256, 22882, 256, 55365, 57000, 256, 55365, 57066, 256, 23020, + 256, 23067, 256, 23079, 256, 23000, 256, 23142, 256, 14062, 256, 14076, + 256, 23304, 256, 23358, 256, 23358, 256, 55366, 56776, 256, 23491, 256, + 23512, 256, 23527, 256, 23539, 256, 55366, 57112, 256, 23551, 256, 23558, + 256, 24403, 256, 23586, 256, 14209, 256, 23648, 256, 23662, 256, 23744, + 256, 23693, 256, 55367, 56804, 256, 23875, 256, 55367, 56806, 256, 23918, + 256, 23915, 256, 23932, 256, 24033, 256, 24034, 256, 14383, 256, 24061, + 256, 24104, 256, 24125, 256, 24169, 256, 14434, 256, 55368, 56707, 256, + 14460, 256, 24240, 256, 24243, 256, 24246, 256, 24266, 256, 55400, 57234, + 256, 24318, 256, 55368, 57137, 256, 55368, 57137, 256, 33281, 256, 24354, + 256, 24354, 256, 14535, 256, 55372, 57016, 256, 55384, 56794, 256, 24418, + 256, 24427, 256, 14563, 256, 24474, 256, 24525, 256, 24535, 256, 24569, + 256, 24705, 256, 14650, 256, 14620, 256, 24724, 256, 55369, 57044, 256, + 24775, 256, 24904, 256, 24908, 256, 24910, 256, 24908, 256, 24954, 256, + 24974, 256, 25010, 256, 24996, 256, 25007, 256, 25054, 256, 25074, 256, + 25078, 256, 25104, 256, 25115, 256, 25181, 256, 25265, 256, 25300, 256, + 25424, 256, 55370, 57100, 256, 25405, 256, 25340, 256, 25448, 256, 25475, + 256, 25572, 256, 55370, 57329, 256, 25634, 256, 25541, 256, 25513, 256, + 14894, 256, 25705, 256, 25726, 256, 25757, 256, 25719, 256, 14956, 256, + 25935, 256, 25964, 256, 55372, 56330, 256, 26083, 256, 26360, 256, 26185, + 256, 15129, 256, 26257, 256, 15112, 256, 15076, 256, 20882, 256, 20885, + 256, 26368, 256, 26268, 256, 32941, 256, 17369, 256, 26391, 256, 26395, + 256, 26401, 256, 26462, 256, 26451, 256, 55372, 57283, 256, 15177, 256, + 26618, 256, 26501, 256, 26706, 256, 26757, 256, 55373, 56429, 256, 26766, + 256, 26655, 256, 26900, 256, 15261, 256, 26946, 256, 27043, 256, 27114, + 256, 27304, 256, 55373, 56995, 256, 27355, 256, 15384, 256, 27425, 256, + 55374, 56487, 256, 27476, 256, 15438, 256, 27506, 256, 27551, 256, 27578, + 256, 27579, 256, 55374, 56973, 256, 55367, 56587, 256, 55374, 57082, 256, + 27726, 256, 55375, 56508, 256, 27839, 256, 27853, 256, 27751, 256, 27926, + 256, 27966, 256, 28023, 256, 27969, 256, 28009, 256, 28024, 256, 28037, + 256, 55375, 56606, 256, 27956, 256, 28207, 256, 28270, 256, 15667, 256, + 28363, 256, 28359, 256, 55375, 57041, 256, 28153, 256, 28526, 256, 55375, + 57182, 256, 55375, 57230, 256, 28614, 256, 28729, 256, 28702, 256, 28699, + 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, 28845, 256, 55361, + 56613, 256, 28997, 256, 55376, 56931, 256, 29084, 256, 55376, 57259, 256, + 29224, 256, 29237, 256, 29264, 256, 55377, 56840, 256, 29312, 256, 29333, + 256, 55377, 57141, 256, 55378, 56340, 256, 29562, 256, 29579, 256, 16044, + 256, 29605, 256, 16056, 256, 16056, 256, 29767, 256, 29788, 256, 29809, + 256, 29829, 256, 29898, 256, 16155, 256, 29988, 256, 55379, 56374, 256, + 30014, 256, 55379, 56466, 256, 30064, 256, 55368, 56735, 256, 30224, 256, + 55379, 57249, 256, 55379, 57272, 256, 55380, 56388, 256, 16380, 256, + 16392, 256, 30452, 256, 55380, 56563, 256, 55380, 56562, 256, 55380, + 56601, 256, 55380, 56627, 256, 30494, 256, 30495, 256, 30495, 256, 30538, + 256, 16441, 256, 30603, 256, 16454, 256, 16534, 256, 55381, 56349, 256, + 30798, 256, 30860, 256, 30924, 256, 16611, 256, 55381, 56870, 256, 31062, + 256, 55381, 56986, 256, 55381, 57029, 256, 31119, 256, 31211, 256, 16687, + 256, 31296, 256, 31306, 256, 31311, 256, 55382, 56700, 256, 55382, 56999, + 256, 55382, 56999, 256, 31470, 256, 16898, 256, 55382, 57259, 256, 31686, + 256, 31689, 256, 16935, 256, 55383, 56448, 256, 31954, 256, 17056, 256, + 31976, 256, 31971, 256, 32000, 256, 55383, 57222, 256, 32099, 256, 17153, + 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, 55384, 56872, 256, + 55384, 56903, 256, 17241, 256, 55384, 57049, 256, 32634, 256, 55384, + 57150, 256, 32661, 256, 32762, 256, 32773, 256, 55385, 56538, 256, 55385, + 56611, 256, 32864, 256, 55385, 56744, 256, 32880, 256, 55372, 57183, 256, + 17365, 256, 32946, 256, 33027, 256, 17419, 256, 33086, 256, 23221, 256, + 55385, 57255, 256, 55385, 57269, 256, 55372, 57235, 256, 55372, 57244, + 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, 256, 33419, + 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, 256, 33510, + 256, 55386, 57148, 256, 33509, 256, 33565, 256, 33635, 256, 33709, 256, + 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, 33738, 256, + 33740, 256, 33756, 256, 55387, 56374, 256, 55387, 56683, 256, 55387, + 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, 256, 55388, 57290, + 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, 256, 55387, 57265, + 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, 256, 34407, 256, + 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, 256, 34681, 256, + 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, 256, 34817, 256, + 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, 35031, 256, 35038, + 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, 256, 55390, 56678, + 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, 35722, 256, 35925, + 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, 256, 36215, 256, + 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, 256, 36336, 256, + 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, 256, 55393, + 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, 256, 37147, + 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, + 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, 55395, 56695, + 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, 56645, 256, + 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, 56330, 256, + 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, 256, 55397, + 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, 39362, 256, + 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, 256, 40189, + 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, 19704, 256, + 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, 40635, 256, + 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, + 40763, 256, 55401, 56832, +}; + +/* index tables for the decomposition data */ +#define DECOMP_SHIFT1 6 +#define DECOMP_SHIFT2 4 +static const unsigned char decomp_index0[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, 14, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 16, 5, 5, 5, 5, 17, 18, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 20, + 5, 5, 5, 5, 5, 21, 22, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 23, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static const unsigned short decomp_index1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 26, 27, 0, 0, 0, 0, 0, 28, 0, 0, 29, 30, 31, 32, 33, 34, 35, 0, + 36, 37, 38, 0, 39, 0, 40, 0, 41, 0, 0, 0, 0, 42, 43, 44, 45, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 48, 0, 0, 0, + 0, 49, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 53, 0, 0, 0, 0, + 0, 0, 54, 55, 0, 0, 0, 0, 0, 56, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 58, 59, 0, 0, 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, + 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 70, + 71, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 0, + 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 86, 87, 88, 89, 0, 90, 91, 92, 0, 0, 0, 0, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 0, 131, 132, 133, 134, 0, 0, 0, + 0, 0, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 0, + 0, 0, 147, 0, 148, 149, 150, 0, 151, 152, 153, 0, 154, 0, 0, 0, 155, 0, + 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, + 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 171, 0, 0, 0, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 0, 0, 187, 0, 0, 188, 189, 190, 191, 192, 0, + 193, 194, 195, 196, 197, 0, 198, 0, 0, 0, 199, 200, 201, 202, 203, 204, + 205, 0, 0, 0, 0, 0, 0, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, + 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 0, 0, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 0, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 0, 307, 308, 309, 310, 311, 312, 313, 314, 0, 0, 315, + 0, 316, 0, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, + 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 0, + 347, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351, 352, 0, 0, 0, 0, 353, 354, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, + 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, + 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 431, 432, 433, 434, 435, 0, 436, 0, + 0, 437, 0, 0, 0, 0, 0, 0, 438, 439, 440, 441, 442, 443, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 445, + 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 476, 477, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const unsigned short decomp_index2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, + 31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, + 0, 84, 87, 90, 93, 96, 99, 0, 0, 102, 105, 108, 111, 114, 0, 0, 117, 120, + 123, 126, 129, 132, 0, 135, 138, 141, 144, 147, 150, 153, 156, 159, 0, + 162, 165, 168, 171, 174, 177, 0, 0, 180, 183, 186, 189, 192, 0, 195, 198, + 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, + 243, 0, 0, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, + 282, 285, 288, 291, 294, 297, 300, 303, 0, 0, 306, 309, 312, 315, 318, + 321, 324, 327, 330, 0, 333, 336, 339, 342, 345, 348, 0, 351, 354, 357, + 360, 363, 366, 369, 372, 0, 0, 375, 378, 381, 384, 387, 390, 393, 0, 0, + 396, 399, 402, 405, 408, 411, 0, 0, 414, 417, 420, 423, 426, 429, 432, + 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 0, 0, 468, 471, + 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, + 516, 519, 522, 525, 528, 531, 534, 537, 539, 542, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 545, 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 551, 554, 557, 560, 563, 566, 569, 572, 575, 578, 581, 584, 587, + 590, 593, 596, 599, 602, 605, 608, 611, 614, 617, 620, 623, 0, 626, 629, + 632, 635, 638, 641, 0, 0, 644, 647, 650, 653, 656, 659, 662, 665, 668, + 671, 674, 677, 680, 683, 686, 689, 0, 0, 692, 695, 698, 701, 704, 707, + 710, 713, 716, 719, 722, 725, 728, 731, 734, 737, 740, 743, 746, 749, + 752, 755, 758, 761, 764, 767, 770, 773, 776, 779, 782, 785, 788, 791, + 794, 797, 0, 0, 800, 803, 0, 0, 0, 0, 0, 0, 806, 809, 812, 815, 818, 821, + 824, 827, 830, 833, 836, 839, 842, 845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 848, 850, 852, 854, 856, 858, 860, 862, 864, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 866, 869, 872, 875, 878, 881, 0, 0, 884, 886, 888, + 890, 892, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 894, 896, 0, 898, 900, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 903, 0, 0, 0, 0, 0, 905, 0, 0, 0, + 908, 0, 0, 0, 0, 0, 910, 913, 916, 919, 921, 924, 927, 0, 930, 0, 933, + 936, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 942, 945, 948, 951, 954, 957, 960, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 963, 966, 969, 972, 975, + 0, 978, 980, 982, 984, 987, 990, 992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, + 996, 998, 0, 1000, 1002, 0, 0, 0, 1004, 0, 0, 0, 0, 0, 0, 1006, 1009, 0, + 1012, 0, 0, 0, 1015, 0, 0, 0, 0, 1018, 1021, 1024, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1030, 0, 0, + 0, 0, 0, 0, 1033, 1036, 0, 1039, 0, 0, 0, 1042, 0, 0, 0, 0, 1045, 1048, + 1051, 0, 0, 0, 0, 0, 0, 0, 1054, 1057, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1060, + 1063, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1066, 1069, 1072, 1075, 0, + 0, 1078, 1081, 0, 0, 1084, 1087, 1090, 1093, 1096, 1099, 0, 0, 1102, + 1105, 1108, 1111, 1114, 1117, 0, 0, 1120, 1123, 1126, 1129, 1132, 1135, + 1138, 1141, 1144, 1147, 1150, 1153, 0, 0, 1156, 1159, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1165, 1168, + 1171, 1174, 1177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1180, 1183, + 1186, 1189, 0, 0, 0, 0, 0, 0, 0, 1192, 0, 1195, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1201, 0, 0, 0, 0, 0, 0, 0, 1204, 0, 0, 1207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1210, 1213, 1216, + 1219, 1222, 1225, 1228, 1231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1234, + 1237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1240, 1243, 0, 1246, + 0, 0, 0, 1249, 0, 0, 1252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1255, 1258, 1261, 0, 0, 1264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1267, + 0, 0, 1270, 1273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1276, + 1279, 0, 0, 0, 0, 0, 0, 1282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1285, 1288, 1291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1294, 0, 0, 0, 0, 0, 0, 0, 1297, 0, 0, 0, 0, 0, 0, 1300, 1303, 0, 1306, + 1309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1312, 1315, 1318, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1321, 0, 1324, 1327, 1330, 0, 0, 0, 0, + 1333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1336, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1342, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, 0, 0, 0, 0, 0, 0, 1347, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1350, 0, 0, 0, 0, 1353, 0, 0, 0, 0, 1356, 0, 0, + 0, 0, 1359, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1362, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1365, 0, 1368, 1371, 1374, 1377, 1380, 0, 0, 0, 0, 0, 0, 0, + 1383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1389, 0, 0, 0, 0, 1392, 0, 0, 0, 0, 1395, 0, 0, 0, 0, + 1398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1401, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1409, 0, 1412, 0, 1415, 0, + 1418, 0, 1421, 0, 0, 0, 1424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1427, 0, 1430, 0, 0, 1433, 1436, 0, 1439, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1442, 1444, 1446, 0, 1448, 1450, 1452, 1454, 1456, 1458, 1460, 1462, + 1464, 1466, 1468, 0, 1470, 1472, 1474, 1476, 1478, 1480, 1482, 1484, + 1486, 1488, 1490, 1492, 1494, 1496, 1498, 1500, 1502, 1504, 0, 1506, + 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1528, 1530, + 1532, 1534, 1536, 1538, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, + 1556, 1558, 1560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1562, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1564, 1566, 1568, 1570, + 1572, 1574, 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, + 1596, 1598, 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, 1616, 1618, + 1620, 1622, 1624, 1626, 1628, 1630, 1632, 1634, 1636, 1638, 1641, 1644, + 1647, 1650, 1653, 1656, 1659, 1662, 1665, 1668, 1671, 1674, 1677, 1680, + 1683, 1686, 1689, 1692, 1695, 1698, 1701, 1704, 1707, 1710, 1713, 1716, + 1719, 1722, 1725, 1728, 1731, 1734, 1737, 1740, 1743, 1746, 1749, 1752, + 1755, 1758, 1761, 1764, 1767, 1770, 1773, 1776, 1779, 1782, 1785, 1788, + 1791, 1794, 1797, 1800, 1803, 1806, 1809, 1812, 1815, 1818, 1821, 1824, + 1827, 1830, 1833, 1836, 1839, 1842, 1845, 1848, 1851, 1854, 1857, 1860, + 1863, 1866, 1869, 1872, 1875, 1878, 1881, 1884, 1887, 1890, 1893, 1896, + 1899, 1902, 1905, 1908, 1911, 1914, 1917, 1920, 1923, 1926, 1929, 1932, + 1935, 1938, 1941, 1944, 1947, 1950, 1953, 1956, 1959, 1962, 1965, 1968, + 1971, 1974, 1977, 1980, 1983, 1986, 1989, 1992, 1995, 1998, 2001, 2004, + 2007, 2010, 2013, 2016, 2019, 2022, 2025, 2028, 2031, 2034, 2037, 2040, + 2043, 2046, 2049, 2052, 2055, 2058, 2061, 2064, 2067, 2070, 2073, 2076, + 2079, 2082, 2085, 2088, 2091, 2094, 2097, 2100, 2103, 0, 0, 0, 0, 2106, + 2109, 2112, 2115, 2118, 2121, 2124, 2127, 2130, 2133, 2136, 2139, 2142, + 2145, 2148, 2151, 2154, 2157, 2160, 2163, 2166, 2169, 2172, 2175, 2178, + 2181, 2184, 2187, 2190, 2193, 2196, 2199, 2202, 2205, 2208, 2211, 2214, + 2217, 2220, 2223, 2226, 2229, 2232, 2235, 2238, 2241, 2244, 2247, 2250, + 2253, 2256, 2259, 2262, 2265, 2268, 2271, 2274, 2277, 2280, 2283, 2286, + 2289, 2292, 2295, 2298, 2301, 2304, 2307, 2310, 2313, 2316, 2319, 2322, + 2325, 2328, 2331, 2334, 2337, 2340, 2343, 2346, 2349, 2352, 2355, 2358, + 2361, 2364, 2367, 2370, 2373, 0, 0, 0, 0, 0, 0, 2376, 2379, 2382, 2385, + 2388, 2391, 2394, 2397, 2400, 2403, 2406, 2409, 2412, 2415, 2418, 2421, + 2424, 2427, 2430, 2433, 2436, 2439, 0, 0, 2442, 2445, 2448, 2451, 2454, + 2457, 0, 0, 2460, 2463, 2466, 2469, 2472, 2475, 2478, 2481, 2484, 2487, + 2490, 2493, 2496, 2499, 2502, 2505, 2508, 2511, 2514, 2517, 2520, 2523, + 2526, 2529, 2532, 2535, 2538, 2541, 2544, 2547, 2550, 2553, 2556, 2559, + 2562, 2565, 2568, 2571, 0, 0, 2574, 2577, 2580, 2583, 2586, 2589, 0, 0, + 2592, 2595, 2598, 2601, 2604, 2607, 2610, 2613, 0, 2616, 0, 2619, 0, + 2622, 0, 2625, 2628, 2631, 2634, 2637, 2640, 2643, 2646, 2649, 2652, + 2655, 2658, 2661, 2664, 2667, 2670, 2673, 2676, 2679, 2681, 2684, 2686, + 2689, 2691, 2694, 2696, 2699, 2701, 2704, 2706, 2709, 0, 0, 2711, 2714, + 2717, 2720, 2723, 2726, 2729, 2732, 2735, 2738, 2741, 2744, 2747, 2750, + 2753, 2756, 2759, 2762, 2765, 2768, 2771, 2774, 2777, 2780, 2783, 2786, + 2789, 2792, 2795, 2798, 2801, 2804, 2807, 2810, 2813, 2816, 2819, 2822, + 2825, 2828, 2831, 2834, 2837, 2840, 2843, 2846, 2849, 2852, 2855, 2858, + 2861, 2864, 2867, 0, 2870, 2873, 2876, 2879, 2882, 2885, 2887, 2890, + 2893, 2895, 2898, 2901, 2904, 2907, 2910, 0, 2913, 2916, 2919, 2922, + 2924, 2927, 2929, 2932, 2935, 2938, 2941, 2944, 2947, 2950, 0, 0, 2952, + 2955, 2958, 2961, 2964, 2967, 0, 2969, 2972, 2975, 2978, 2981, 2984, + 2987, 2989, 2992, 2995, 2998, 3001, 3004, 3007, 3010, 3012, 3015, 3018, + 3020, 0, 0, 3022, 3025, 3028, 0, 3031, 3034, 3037, 3040, 3042, 3045, + 3047, 3050, 3052, 0, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, + 3071, 3073, 3075, 0, 0, 0, 0, 0, 0, 3077, 0, 0, 0, 0, 0, 3079, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3082, 3084, 3087, 0, 0, 0, 0, 0, 0, 0, 0, + 3091, 0, 0, 0, 3093, 3096, 0, 3100, 3103, 0, 0, 0, 0, 3107, 0, 3110, 0, + 0, 0, 0, 0, 0, 0, 0, 3113, 3116, 3119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3122, 0, 0, 0, 0, 0, 0, 0, 3127, 3129, 3131, 0, 0, 3133, 3135, + 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, + 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, + 3185, 0, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, + 3207, 3209, 3211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3213, 0, 0, 0, 0, 0, + 0, 0, 3216, 3220, 3224, 3226, 0, 3229, 3233, 3237, 0, 3239, 3242, 3244, + 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, 0, 3262, 3264, 0, 0, + 3267, 3269, 3271, 3273, 3275, 0, 0, 3277, 3280, 3284, 0, 3287, 0, 3289, + 0, 3291, 0, 3293, 3295, 3297, 3299, 0, 3301, 3303, 3305, 0, 3307, 3309, + 3311, 3313, 3315, 3317, 3319, 0, 3321, 3325, 3327, 3329, 3331, 3333, 0, + 0, 0, 0, 3335, 3337, 3339, 3341, 3343, 0, 0, 0, 0, 0, 0, 3345, 3349, + 3353, 3358, 3362, 3366, 3370, 3374, 3378, 3382, 3386, 3390, 3394, 3398, + 3402, 3406, 3409, 3411, 3414, 3418, 3421, 3423, 3426, 3430, 3435, 3438, + 3440, 3443, 3447, 3449, 3451, 3453, 3455, 3457, 3460, 3464, 3467, 3469, + 3472, 3476, 3481, 3484, 3486, 3489, 3493, 3495, 3497, 3499, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3505, 3508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3511, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3514, 3517, 3520, 0, 0, 0, 0, + 3523, 0, 0, 0, 0, 3526, 0, 0, 3529, 0, 0, 0, 0, 0, 0, 0, 3532, 0, 3535, + 0, 0, 0, 0, 0, 3538, 3541, 0, 3545, 3548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3552, 0, 0, 3555, 0, 0, 3558, 0, 3561, 0, 0, 0, 0, 0, + 0, 3564, 0, 3567, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3570, 3573, 3576, 3579, + 3582, 0, 0, 3585, 3588, 0, 0, 3591, 3594, 0, 0, 0, 0, 0, 0, 3597, 3600, + 0, 0, 3603, 3606, 0, 0, 3609, 3612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3615, 3618, 3621, 3624, 3627, 3630, 3633, 3636, 0, 0, + 0, 0, 0, 0, 3639, 3642, 3645, 3648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3651, 3653, 0, 0, 0, 0, 0, 3655, 3657, 3659, 3661, 3663, 3665, 3667, + 3669, 3671, 3673, 3676, 3679, 3682, 3685, 3688, 3691, 3694, 3697, 3700, + 3703, 3706, 3710, 3714, 3718, 3722, 3726, 3730, 3734, 3738, 3742, 3747, + 3752, 3757, 3762, 3767, 3772, 3777, 3782, 3787, 3792, 3797, 3800, 3803, + 3806, 3809, 3812, 3815, 3818, 3821, 3824, 3828, 3832, 3836, 3840, 3844, + 3848, 3852, 3856, 3860, 3864, 3868, 3872, 3876, 3880, 3884, 3888, 3892, + 3896, 3900, 3904, 3908, 3912, 3916, 3920, 3924, 3928, 3932, 3936, 3940, + 3944, 3948, 3952, 3956, 3960, 3964, 3968, 3972, 3974, 3976, 3978, 3980, + 3982, 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, + 4006, 4008, 4010, 4012, 4014, 4016, 4018, 4020, 4022, 4024, 4026, 4028, + 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046, 4048, 4050, 4052, + 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4078, 0, 0, 0, 0, 0, + 0, 0, 4083, 4087, 4090, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4094, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4097, + 4099, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4103, 0, 0, 0, 4105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, + 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, + 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, + 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, + 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, + 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, + 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, + 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, + 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, + 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, + 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, + 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, + 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, + 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, + 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, + 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, + 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, + 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, + 4531, 4533, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4535, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4537, 0, 4539, 4541, 4543, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4545, 0, 4548, 0, 4551, 0, + 4554, 0, 4557, 0, 4560, 0, 4563, 0, 4566, 0, 4569, 0, 4572, 0, 4575, 0, + 4578, 0, 0, 4581, 0, 4584, 0, 4587, 0, 0, 0, 0, 0, 0, 4590, 4593, 0, + 4596, 4599, 0, 4602, 4605, 0, 4608, 4611, 0, 4614, 4617, 0, 0, 0, 0, 0, + 0, 4620, 0, 0, 0, 0, 0, 0, 4623, 4626, 0, 4629, 4632, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4635, 0, 4638, 0, 4641, 0, 4644, 0, 4647, 0, 4650, 0, + 4653, 0, 4656, 0, 4659, 0, 4662, 0, 4665, 0, 4668, 0, 0, 4671, 0, 4674, + 0, 4677, 0, 0, 0, 0, 0, 0, 4680, 4683, 0, 4686, 4689, 0, 4692, 4695, 0, + 4698, 4701, 0, 4704, 4707, 0, 0, 0, 0, 0, 0, 4710, 0, 0, 4713, 4716, + 4719, 4722, 0, 0, 0, 4725, 4728, 0, 4731, 4733, 4735, 4737, 4739, 4741, + 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, + 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, + 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, + 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, + 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, + 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, + 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, + 4911, 4913, 4915, 4917, 0, 0, 0, 4919, 4921, 4923, 4925, 4927, 4929, + 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4951, 4955, 4959, + 4963, 4967, 4971, 4975, 4979, 4983, 4987, 4991, 4995, 4999, 5003, 5008, + 5013, 5018, 5023, 5028, 5033, 5038, 5043, 5048, 5053, 5058, 5063, 5068, + 5073, 5078, 5086, 0, 5093, 5097, 5101, 5105, 5109, 5113, 5117, 5121, + 5125, 5129, 5133, 5137, 5141, 5145, 5149, 5153, 5157, 5161, 5165, 5169, + 5173, 5177, 5181, 5185, 5189, 5193, 5197, 5201, 5205, 5209, 5213, 5217, + 5221, 5225, 5229, 5233, 5237, 5239, 5241, 5243, 0, 0, 0, 0, 0, 0, 0, 0, + 5245, 5249, 5252, 5255, 5258, 5261, 5264, 5267, 5270, 5273, 5276, 5279, + 5282, 5285, 5288, 5291, 5294, 5296, 5298, 5300, 5302, 5304, 5306, 5308, + 5310, 5312, 5314, 5316, 5318, 5320, 5322, 5325, 5328, 5331, 5334, 5337, + 5340, 5343, 5346, 5349, 5352, 5355, 5358, 5361, 5364, 5370, 5375, 0, + 5378, 5380, 5382, 5384, 5386, 5388, 5390, 5392, 5394, 5396, 5398, 5400, + 5402, 5404, 5406, 5408, 5410, 5412, 5414, 5416, 5418, 5420, 5422, 5424, + 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, 5444, 5446, 5448, + 5450, 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, 5468, 5470, 5472, + 5474, 5476, 5479, 5482, 5485, 5488, 5491, 5494, 5497, 5500, 5503, 5506, + 5509, 5512, 5515, 5518, 5521, 5524, 5527, 5530, 5533, 5536, 5539, 5542, + 5545, 5548, 5552, 5556, 5560, 5563, 5567, 5570, 5574, 5576, 5578, 5580, + 5582, 5584, 5586, 5588, 5590, 5592, 5594, 5596, 5598, 5600, 5602, 5604, + 5606, 5608, 5610, 5612, 5614, 5616, 5618, 5620, 5622, 5624, 5626, 5628, + 5630, 5632, 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, 5650, 5652, + 5654, 5656, 5658, 5660, 5662, 5664, 5666, 0, 5668, 5673, 5678, 5683, + 5687, 5692, 5696, 5700, 5706, 5711, 5715, 5719, 5723, 5728, 5733, 5737, + 5741, 5744, 5748, 5753, 5758, 5761, 5767, 5774, 5780, 5784, 5790, 5796, + 5801, 5805, 5809, 5813, 5818, 5824, 5829, 5833, 5837, 5841, 5844, 5847, + 5850, 5853, 5857, 5861, 5867, 5871, 5876, 5882, 5886, 5889, 5892, 5898, + 5903, 5909, 5913, 5919, 5922, 5926, 5930, 5934, 5938, 5942, 5947, 5951, + 5954, 5958, 5962, 5966, 5971, 5975, 5979, 5983, 5989, 5994, 5997, 6003, + 6006, 6011, 6016, 6020, 6024, 6028, 6033, 6036, 6040, 6045, 6048, 6054, + 6058, 6061, 6064, 6067, 6070, 6073, 6076, 6079, 6082, 6085, 6088, 6092, + 6096, 6100, 6104, 6108, 6112, 6116, 6120, 6124, 6128, 6132, 6136, 6140, + 6144, 6148, 6152, 6155, 6158, 6162, 6165, 6168, 6171, 6175, 6179, 6182, + 6185, 6188, 6191, 6194, 6199, 6202, 6205, 6208, 6211, 6214, 6217, 6220, + 6223, 6227, 6232, 6235, 6238, 6241, 6244, 6247, 6250, 6253, 6257, 6261, + 6265, 6269, 6272, 6275, 6278, 6281, 6284, 6287, 6290, 6293, 6296, 6299, + 6303, 6307, 6310, 6314, 6318, 6322, 6325, 6329, 6333, 6338, 6341, 6345, + 6349, 6353, 6357, 6363, 6370, 6373, 6376, 6379, 6382, 6385, 6388, 6391, + 6394, 6397, 6400, 6403, 6406, 6409, 6412, 6415, 6418, 6421, 6424, 6429, + 6432, 6435, 6438, 6443, 6447, 6450, 6453, 6456, 6459, 6462, 6465, 6468, + 6471, 6474, 6477, 6481, 6484, 6487, 6491, 6495, 6498, 6503, 6507, 6510, + 6513, 6516, 6519, 6523, 6527, 6530, 6533, 6536, 6539, 6542, 6545, 6548, + 6551, 6554, 6558, 6562, 6566, 6570, 6574, 6578, 6582, 6586, 6590, 6594, + 6598, 6602, 6606, 6610, 6614, 6618, 6622, 6626, 6630, 6634, 6638, 6642, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6646, 6648, 0, 0, 6650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6652, 6654, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6656, 6658, 6660, + 6662, 6664, 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, 6682, 6684, + 6686, 6688, 6690, 6692, 6694, 6696, 6698, 6700, 6702, 6704, 6706, 6708, + 6710, 6712, 6714, 6716, 6718, 6720, 6722, 6724, 6726, 6728, 6730, 6732, + 6734, 6736, 6738, 6740, 6742, 6744, 6746, 6748, 6750, 6752, 6754, 6756, + 6758, 6760, 6762, 6764, 6766, 6768, 6770, 6772, 6774, 6776, 6778, 6780, + 6782, 6784, 6786, 6788, 6790, 6792, 6794, 6796, 6798, 6800, 6802, 6804, + 6806, 6808, 6810, 6812, 6814, 6816, 6818, 6820, 6822, 6824, 6826, 6828, + 6830, 6832, 6834, 6836, 6838, 6840, 6842, 6844, 6846, 6848, 6850, 6852, + 6854, 6856, 6858, 6860, 6862, 6864, 6866, 6868, 6870, 6872, 6874, 6876, + 6878, 6880, 6882, 6884, 6886, 6888, 6890, 6892, 6894, 6896, 6898, 6900, + 6902, 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, 6920, 6922, 6924, + 6926, 6928, 6930, 6932, 6934, 6936, 6938, 6940, 6942, 6944, 6946, 6948, + 6950, 6952, 6954, 6956, 6958, 6960, 6962, 6964, 6966, 6968, 6970, 6972, + 6974, 6976, 6978, 6980, 6982, 6984, 6986, 6988, 6990, 6992, 6994, 6996, + 6998, 7000, 7002, 7004, 7006, 7008, 7010, 7012, 7014, 7016, 7018, 7020, + 7022, 7024, 7026, 7028, 7030, 7032, 7034, 7036, 7038, 7040, 7042, 7044, + 7046, 7048, 7050, 7052, 7054, 7056, 7058, 7060, 7062, 7064, 7066, 7068, + 7070, 7072, 7074, 7076, 7078, 7080, 7082, 7084, 7086, 7088, 7090, 7092, + 7094, 7096, 7098, 7100, 7102, 7104, 7106, 7108, 7110, 7112, 7114, 7116, + 7118, 7120, 7122, 7124, 7126, 7128, 7130, 7132, 7134, 7136, 7138, 7140, + 7142, 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, 7162, 7164, + 7166, 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, 7186, 7188, + 7190, 7192, 7194, 7196, 7198, 7200, 7202, 0, 0, 7204, 0, 7206, 0, 0, + 7208, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 0, 7228, 0, + 7230, 0, 0, 7232, 7234, 0, 0, 0, 7236, 7238, 7240, 7242, 7244, 7246, + 7248, 7250, 7252, 7254, 7256, 7258, 7260, 7262, 7264, 7266, 7268, 7270, + 7272, 7274, 7276, 7278, 7280, 7282, 7284, 7286, 7288, 7290, 7292, 7294, + 7296, 7298, 7300, 7302, 7304, 7306, 7308, 7310, 7312, 7314, 7316, 7318, + 7320, 7322, 7324, 7326, 7328, 7330, 7332, 7334, 7336, 7338, 7340, 7342, + 7344, 7346, 7348, 7350, 7352, 7354, 7356, 7358, 7360, 7362, 7364, 7366, + 7368, 7371, 0, 0, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, + 7391, 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, + 7415, 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, + 7439, 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, + 7463, 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, + 7487, 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, + 7511, 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, + 7535, 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7553, 7555, 7557, + 7559, 7561, 7563, 7566, 7569, 7572, 7574, 7576, 7578, 7581, 7584, 7587, + 7589, 0, 0, 0, 0, 0, 0, 7591, 7594, 7597, 7600, 7604, 7608, 7611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7614, 7617, 7620, 7623, 7626, 0, 0, 0, 0, + 0, 7629, 0, 7632, 7635, 7637, 7639, 7641, 7643, 7645, 7647, 7649, 7651, + 7653, 7655, 7658, 7661, 7664, 7667, 7670, 7673, 7676, 7679, 7682, 7685, + 7688, 7691, 0, 7694, 7697, 7700, 7703, 7706, 0, 7709, 0, 7712, 7715, 0, + 7718, 7721, 0, 7724, 7727, 7730, 7733, 7736, 7739, 7742, 7745, 7748, + 7751, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, + 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, + 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, + 7824, 7826, 7828, 7830, 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, + 7848, 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, + 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892, 7894, + 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, + 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, 7936, 7938, 7940, 7942, + 7944, 7946, 7948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7950, 7952, 7954, 7956, 7958, 7960, 7962, 7964, 7966, 7968, 7970, 7972, + 7974, 7976, 7978, 7980, 7982, 7984, 7986, 7988, 7990, 7992, 7994, 7996, + 7999, 8002, 8005, 8008, 8011, 8014, 8017, 8020, 8023, 8026, 8029, 8032, + 8035, 8038, 8041, 8044, 8047, 8050, 8052, 8054, 8056, 8058, 8061, 8064, + 8067, 8070, 8073, 8076, 8079, 8082, 8085, 8088, 8091, 8094, 8097, 8100, + 8103, 8106, 8109, 8112, 8115, 8118, 8121, 8124, 8127, 8130, 8133, 8136, + 8139, 8142, 8145, 8148, 8151, 8154, 8157, 8160, 8163, 8166, 8169, 8172, + 8175, 8178, 8181, 8184, 8187, 8190, 8193, 8196, 8199, 8202, 8205, 8208, + 8211, 8214, 8217, 8220, 8223, 8226, 8229, 8232, 8235, 8238, 8241, 8244, + 8247, 8250, 8253, 8256, 8259, 8262, 8265, 8268, 8271, 8274, 8277, 8280, + 8283, 8286, 8289, 8292, 8295, 8298, 8301, 8304, 8307, 8310, 8313, 8316, + 8319, 8322, 8325, 8328, 8331, 8334, 8337, 8340, 8344, 8348, 8352, 8356, + 8360, 8364, 8367, 8370, 8373, 8376, 8379, 8382, 8385, 8388, 8391, 8394, + 8397, 8400, 8403, 8406, 8409, 8412, 8415, 8418, 8421, 8424, 8427, 8430, + 8433, 8436, 8439, 8442, 8445, 8448, 8451, 8454, 8457, 8460, 8463, 8466, + 8469, 8472, 8475, 8478, 8481, 8484, 8487, 8490, 8493, 8496, 8499, 8502, + 8505, 8508, 8511, 8514, 8517, 8520, 8523, 8526, 8529, 8532, 8535, 8538, + 8541, 8544, 8547, 8550, 8553, 8556, 8559, 8562, 8565, 8568, 8571, 8574, + 8577, 8580, 8583, 8586, 8589, 8592, 8595, 8598, 8601, 8604, 8607, 8610, + 8613, 8616, 8619, 8622, 8625, 8628, 8631, 8634, 8637, 8640, 8643, 8646, + 8649, 8652, 8655, 8658, 8661, 8664, 8667, 8670, 8673, 8676, 8679, 8682, + 8685, 8688, 8691, 8694, 8697, 8700, 8703, 8706, 8709, 8712, 8715, 8718, + 8721, 8724, 8727, 8730, 8733, 8736, 8739, 8742, 8745, 8748, 8751, 8754, + 8757, 8760, 8763, 8766, 8769, 8772, 8775, 8778, 8781, 8784, 8787, 8790, + 8794, 8798, 8802, 8805, 8808, 8811, 8814, 8817, 8820, 8823, 8826, 8829, + 8832, 8835, 8838, 8841, 8844, 8847, 8850, 8853, 8856, 8859, 8862, 8865, + 8868, 8871, 8874, 8877, 8880, 8883, 8886, 8889, 8892, 8895, 8898, 8901, + 8904, 8907, 8910, 8913, 8916, 8919, 8922, 8925, 8928, 8931, 8934, 8937, + 8940, 8943, 8946, 8949, 8952, 8955, 8958, 8961, 8964, 8967, 8970, 8973, + 8976, 8979, 8982, 8985, 8988, 8991, 8994, 8997, 9000, 9003, 9006, 9009, + 9012, 9015, 9018, 0, 0, 9021, 9025, 9029, 9033, 9037, 9041, 9045, 9049, + 9053, 9057, 9061, 9065, 9069, 9073, 9077, 9081, 9085, 9089, 9093, 9097, + 9101, 9105, 9109, 9113, 9117, 9121, 9125, 9129, 9133, 9137, 9141, 9145, + 9149, 9153, 9157, 9161, 9165, 9169, 9173, 9177, 9181, 9185, 9189, 9193, + 9197, 9201, 9205, 9209, 9213, 9217, 9221, 9225, 9229, 9233, 9237, 9241, + 9245, 9249, 9253, 9257, 9261, 9265, 9269, 9273, 0, 0, 9277, 9281, 9285, + 9289, 9293, 9297, 9301, 9305, 9309, 9313, 9317, 9321, 9325, 9329, 9333, + 9337, 9341, 9345, 9349, 9353, 9357, 9361, 9365, 9369, 9373, 9377, 9381, + 9385, 9389, 9393, 9397, 9401, 9405, 9409, 9413, 9417, 9421, 9425, 9429, + 9433, 9437, 9441, 9445, 9449, 9453, 9457, 9461, 9465, 9469, 9473, 9477, + 9481, 9485, 9489, 0, 0, 0, 0, 0, 0, 0, 0, 9493, 9497, 9501, 9506, 9511, + 9516, 9521, 9526, 9531, 9536, 9540, 9559, 9568, 0, 0, 0, 9573, 9575, + 9577, 9579, 9581, 9583, 9585, 9587, 9589, 9591, 0, 0, 0, 0, 0, 0, 9593, + 9595, 9597, 9599, 9601, 9603, 9605, 9607, 9609, 9611, 9613, 9615, 9617, + 9619, 9621, 9623, 9625, 9627, 9629, 9631, 9633, 0, 0, 9635, 9637, 9639, + 9641, 9643, 9645, 9647, 9649, 9651, 9653, 9655, 9657, 0, 9659, 9661, + 9663, 9665, 9667, 9669, 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, + 9687, 9689, 9691, 9693, 9695, 0, 9697, 9699, 9701, 9703, 0, 0, 0, 0, + 9705, 9708, 9711, 0, 9714, 0, 9717, 9720, 9723, 9726, 9729, 9732, 9735, + 9738, 9741, 9744, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, + 9765, 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, + 9789, 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, + 9813, 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, + 9837, 9839, 9841, 9843, 9845, 9847, 9849, 9851, 9853, 9855, 9857, 9859, + 9861, 9863, 9865, 9867, 9869, 9871, 9873, 9875, 9877, 9879, 9881, 9883, + 9885, 9887, 9889, 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, + 9909, 9911, 9913, 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, + 9933, 9935, 9937, 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, + 9957, 9959, 9961, 9963, 9965, 9967, 9969, 9971, 9973, 9975, 9977, 9979, + 9981, 9984, 9987, 9990, 9993, 9996, 9999, 10002, 0, 0, 0, 0, 10005, + 10007, 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, + 10027, 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, + 10047, 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, + 10067, 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, + 10087, 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, + 10107, 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, + 10127, 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, + 10147, 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, + 10167, 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, + 10187, 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, + 10207, 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, + 10227, 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, + 10247, 10249, 10251, 10253, 10255, 10257, 10259, 10261, 10263, 10265, + 10267, 10269, 10271, 10273, 10275, 10277, 10279, 10281, 10283, 10285, + 10287, 10289, 10291, 10293, 10295, 10297, 10299, 10301, 10303, 10305, + 10307, 10309, 10311, 10313, 10315, 10317, 10319, 10321, 10323, 10325, + 10327, 10329, 10331, 10333, 10335, 10337, 10339, 10341, 10343, 10345, + 10347, 10349, 10351, 10353, 10355, 10357, 10359, 10361, 10363, 10365, + 10367, 10369, 10371, 10373, 10375, 10377, 10379, 10381, 10383, 0, 0, 0, + 10385, 10387, 10389, 10391, 10393, 10395, 0, 0, 10397, 10399, 10401, + 10403, 10405, 10407, 0, 0, 10409, 10411, 10413, 10415, 10417, 10419, 0, + 0, 10421, 10423, 10425, 0, 0, 0, 10427, 10429, 10431, 10433, 10435, + 10437, 10439, 0, 10441, 10443, 10445, 10447, 10449, 10451, 10453, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10455, 0, 10460, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10465, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10470, 10475, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10480, 10485, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10490, 10495, 0, 10500, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10505, 10510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10515, 10520, 10525, 10530, 10535, 10540, 10545, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10550, 10555, 10560, + 10565, 10570, 10575, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10580, + 10582, 10584, 10586, 10588, 10590, 10592, 10594, 10596, 10598, 10600, + 10602, 10604, 10606, 10608, 10610, 10612, 10614, 10616, 10618, 10620, + 10622, 10624, 10626, 10628, 10630, 10632, 10634, 10636, 10638, 10640, + 10642, 10644, 10646, 10648, 10650, 10652, 10654, 10656, 10658, 10660, + 10662, 10664, 10666, 10668, 10670, 10672, 10674, 10676, 10678, 10680, + 10682, 10684, 10686, 10688, 10690, 10692, 10694, 10696, 10698, 10700, + 10702, 10704, 10706, 10708, 10710, 10712, 10714, 10716, 10718, 10720, + 10722, 10724, 10726, 10728, 10730, 10732, 10734, 10736, 10738, 10740, + 10742, 10744, 10746, 10748, 0, 10750, 10752, 10754, 10756, 10758, 10760, + 10762, 10764, 10766, 10768, 10770, 10772, 10774, 10776, 10778, 10780, + 10782, 10784, 10786, 10788, 10790, 10792, 10794, 10796, 10798, 10800, + 10802, 10804, 10806, 10808, 10810, 10812, 10814, 10816, 10818, 10820, + 10822, 10824, 10826, 10828, 10830, 10832, 10834, 10836, 10838, 10840, + 10842, 10844, 10846, 10848, 10850, 10852, 10854, 10856, 10858, 10860, + 10862, 10864, 10866, 10868, 10870, 10872, 10874, 10876, 10878, 10880, + 10882, 10884, 10886, 10888, 10890, 0, 10892, 10894, 0, 0, 10896, 0, 0, + 10898, 10900, 0, 0, 10902, 10904, 10906, 10908, 0, 10910, 10912, 10914, + 10916, 10918, 10920, 10922, 10924, 10926, 10928, 10930, 10932, 0, 10934, + 0, 10936, 10938, 10940, 10942, 10944, 10946, 10948, 0, 10950, 10952, + 10954, 10956, 10958, 10960, 10962, 10964, 10966, 10968, 10970, 10972, + 10974, 10976, 10978, 10980, 10982, 10984, 10986, 10988, 10990, 10992, + 10994, 10996, 10998, 11000, 11002, 11004, 11006, 11008, 11010, 11012, + 11014, 11016, 11018, 11020, 11022, 11024, 11026, 11028, 11030, 11032, + 11034, 11036, 11038, 11040, 11042, 11044, 11046, 11048, 11050, 11052, + 11054, 11056, 11058, 11060, 11062, 11064, 11066, 11068, 11070, 11072, + 11074, 11076, 11078, 0, 11080, 11082, 11084, 11086, 0, 0, 11088, 11090, + 11092, 11094, 11096, 11098, 11100, 11102, 0, 11104, 11106, 11108, 11110, + 11112, 11114, 11116, 0, 11118, 11120, 11122, 11124, 11126, 11128, 11130, + 11132, 11134, 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, + 11152, 11154, 11156, 11158, 11160, 11162, 11164, 11166, 11168, 11170, + 11172, 0, 11174, 11176, 11178, 11180, 0, 11182, 11184, 11186, 11188, + 11190, 0, 11192, 0, 0, 0, 11194, 11196, 11198, 11200, 11202, 11204, + 11206, 0, 11208, 11210, 11212, 11214, 11216, 11218, 11220, 11222, 11224, + 11226, 11228, 11230, 11232, 11234, 11236, 11238, 11240, 11242, 11244, + 11246, 11248, 11250, 11252, 11254, 11256, 11258, 11260, 11262, 11264, + 11266, 11268, 11270, 11272, 11274, 11276, 11278, 11280, 11282, 11284, + 11286, 11288, 11290, 11292, 11294, 11296, 11298, 11300, 11302, 11304, + 11306, 11308, 11310, 11312, 11314, 11316, 11318, 11320, 11322, 11324, + 11326, 11328, 11330, 11332, 11334, 11336, 11338, 11340, 11342, 11344, + 11346, 11348, 11350, 11352, 11354, 11356, 11358, 11360, 11362, 11364, + 11366, 11368, 11370, 11372, 11374, 11376, 11378, 11380, 11382, 11384, + 11386, 11388, 11390, 11392, 11394, 11396, 11398, 11400, 11402, 11404, + 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, + 11426, 11428, 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, + 11446, 11448, 11450, 11452, 11454, 11456, 11458, 11460, 11462, 11464, + 11466, 11468, 11470, 11472, 11474, 11476, 11478, 11480, 11482, 11484, + 11486, 11488, 11490, 11492, 11494, 11496, 11498, 11500, 11502, 11504, + 11506, 11508, 11510, 11512, 11514, 11516, 11518, 11520, 11522, 11524, + 11526, 11528, 11530, 11532, 11534, 11536, 11538, 11540, 11542, 11544, + 11546, 11548, 11550, 11552, 11554, 11556, 11558, 11560, 11562, 11564, + 11566, 11568, 11570, 11572, 11574, 11576, 11578, 11580, 11582, 11584, + 11586, 11588, 11590, 11592, 11594, 11596, 11598, 11600, 11602, 11604, + 11606, 11608, 11610, 11612, 11614, 11616, 11618, 11620, 11622, 11624, + 11626, 11628, 11630, 11632, 11634, 11636, 11638, 11640, 11642, 11644, + 11646, 11648, 11650, 11652, 11654, 11656, 11658, 11660, 11662, 11664, + 11666, 11668, 11670, 11672, 11674, 11676, 11678, 11680, 11682, 11684, + 11686, 11688, 11690, 11692, 11694, 11696, 11698, 11700, 11702, 11704, + 11706, 11708, 11710, 11712, 11714, 11716, 11718, 11720, 11722, 11724, + 11726, 11728, 11730, 11732, 11734, 11736, 11738, 11740, 11742, 11744, + 11746, 11748, 11750, 11752, 11754, 11756, 11758, 11760, 11762, 11764, + 11766, 11768, 11770, 11772, 11774, 11776, 11778, 11780, 11782, 11784, + 11786, 11788, 11790, 11792, 11794, 11796, 11798, 11800, 11802, 11804, + 11806, 11808, 11810, 11812, 11814, 11816, 11818, 11820, 11822, 11824, + 11826, 11828, 11830, 11832, 11834, 11836, 11838, 11840, 11842, 11844, + 11846, 11848, 11850, 11852, 11854, 11856, 11858, 11860, 11862, 11864, + 11866, 11868, 11870, 11872, 11874, 11876, 11878, 11880, 11882, 11884, + 11886, 0, 0, 11888, 11890, 11892, 11894, 11896, 11898, 11900, 11902, + 11904, 11906, 11908, 11910, 11912, 11914, 11916, 11918, 11920, 11922, + 11924, 11926, 11928, 11930, 11932, 11934, 11936, 11938, 11940, 11942, + 11944, 11946, 11948, 11950, 11952, 11954, 11956, 11958, 11960, 11962, + 11964, 11966, 11968, 11970, 11972, 11974, 11976, 11978, 11980, 11982, + 11984, 11986, 11988, 11990, 11992, 11994, 11996, 11998, 12000, 12002, + 12004, 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, + 12024, 12026, 12028, 12030, 12032, 12034, 12036, 12038, 12040, 12042, + 12044, 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, 12062, + 12064, 12066, 12068, 12070, 12072, 12074, 12076, 12078, 12080, 12082, + 12084, 12086, 12088, 12090, 12092, 12094, 12096, 12098, 12100, 12102, + 12104, 12106, 12108, 12110, 12112, 12114, 12116, 12118, 12120, 12122, + 12124, 12126, 12128, 12130, 12132, 12134, 12136, 12138, 12140, 12142, + 12144, 12146, 12148, 12150, 12152, 12154, 12156, 12158, 12160, 12162, + 12164, 12166, 12168, 12170, 12172, 12174, 12176, 12178, 12180, 12182, + 12184, 12186, 12188, 12190, 12192, 12194, 12196, 12198, 12200, 12202, + 12204, 12206, 12208, 12210, 12212, 12214, 12216, 12218, 12220, 12222, + 12224, 12226, 12228, 12230, 12232, 12234, 12236, 12238, 12240, 12242, + 12244, 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, + 12264, 12266, 12268, 12270, 12272, 12274, 12276, 12278, 12280, 12282, + 12284, 12286, 12288, 12290, 12292, 12294, 12296, 12298, 12300, 12302, + 12304, 12306, 12308, 12310, 12312, 12314, 12316, 12318, 12320, 12322, + 12324, 12326, 12328, 12330, 12332, 12334, 12336, 12338, 12340, 12342, + 12344, 12346, 12348, 12350, 12352, 12354, 12356, 12358, 12360, 12362, + 12364, 12366, 12368, 12370, 12372, 12374, 12376, 12378, 12380, 12382, + 12384, 12386, 12388, 12390, 12392, 12394, 12396, 12398, 12400, 12402, + 12404, 12406, 12408, 12410, 12412, 12414, 12416, 12418, 12420, 12422, + 12424, 12426, 12428, 12430, 12432, 12434, 12436, 12438, 12440, 12442, + 12444, 12446, 12448, 12450, 12452, 12454, 12456, 12458, 12460, 12462, + 12464, 12466, 12468, 12470, 0, 0, 12472, 12474, 12476, 12478, 12480, + 12482, 12484, 12486, 12488, 12490, 12492, 12494, 12496, 12498, 12500, + 12502, 12504, 12506, 12508, 12510, 12512, 12514, 12516, 12518, 12520, + 12522, 12524, 12526, 12528, 12530, 12532, 12534, 12536, 12538, 12540, + 12542, 12544, 12546, 12548, 12550, 12552, 12554, 12556, 12558, 12560, + 12562, 12564, 12566, 12568, 12570, 12572, 12574, 12576, 12578, 0, 12580, + 12582, 12584, 12586, 12588, 12590, 12592, 12594, 12596, 12598, 12600, + 12602, 12604, 12606, 12608, 12610, 12612, 12614, 12616, 12618, 12620, + 12622, 12624, 12626, 12628, 12630, 12632, 0, 12634, 12636, 0, 12638, 0, + 0, 12640, 0, 12642, 12644, 12646, 12648, 12650, 12652, 12654, 12656, + 12658, 12660, 0, 12662, 12664, 12666, 12668, 0, 12670, 0, 12672, 0, 0, 0, + 0, 0, 0, 12674, 0, 0, 0, 0, 12676, 0, 12678, 0, 12680, 0, 12682, 12684, + 12686, 0, 12688, 12690, 0, 12692, 0, 0, 12694, 0, 12696, 0, 12698, 0, + 12700, 0, 12702, 0, 12704, 12706, 0, 12708, 0, 0, 12710, 12712, 12714, + 12716, 0, 12718, 12720, 12722, 12724, 12726, 12728, 12730, 0, 12732, + 12734, 12736, 12738, 0, 12740, 12742, 12744, 12746, 0, 12748, 0, 12750, + 12752, 12754, 12756, 12758, 12760, 12762, 12764, 12766, 12768, 0, 12770, + 12772, 12774, 12776, 12778, 12780, 12782, 12784, 12786, 12788, 12790, + 12792, 12794, 12796, 12798, 12800, 12802, 0, 0, 0, 0, 0, 12804, 12806, + 12808, 0, 12810, 12812, 12814, 12816, 12818, 0, 12820, 12822, 12824, + 12826, 12828, 12830, 12832, 12834, 12836, 12838, 12840, 12842, 12844, + 12846, 12848, 12850, 12852, 0, 0, 0, 0, 12854, 12857, 12860, 12863, + 12866, 12869, 12872, 12875, 12878, 12881, 12884, 0, 0, 0, 0, 0, 12887, + 12891, 12895, 12899, 12903, 12907, 12911, 12915, 12919, 12923, 12927, + 12931, 12935, 12939, 12943, 12947, 12951, 12955, 12959, 12963, 12967, + 12971, 12975, 12979, 12983, 12987, 12991, 12995, 12997, 12999, 13002, 0, + 13005, 13007, 13009, 13011, 13013, 13015, 13017, 13019, 13021, 13023, + 13025, 13027, 13029, 13031, 13033, 13035, 13037, 13039, 13041, 13043, + 13045, 13047, 13049, 13051, 13053, 13055, 13057, 13060, 13063, 13066, + 13069, 13073, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13076, 13079, 0, 0, 0, 0, + 13082, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13085, 13088, 13091, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13093, 13095, 13097, 13099, 13101, + 13103, 13105, 13107, 13109, 13111, 13113, 13115, 13117, 13119, 13121, + 13123, 13125, 13127, 13129, 13131, 13133, 13135, 13137, 13139, 13141, + 13143, 13145, 13147, 13149, 13151, 13153, 13155, 13157, 13159, 13161, + 13163, 13165, 13167, 13169, 13171, 13173, 13175, 13177, 0, 0, 0, 0, 0, + 13179, 13183, 13187, 13191, 13195, 13199, 13203, 13207, 13211, 0, 0, 0, + 0, 0, 0, 0, 13215, 13217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13219, 13221, 13223, 13225, 13228, 13230, 13232, 13234, 13236, 13238, + 13240, 13242, 13244, 13246, 13249, 13251, 13253, 13255, 13257, 13260, + 13262, 13264, 13266, 13269, 13271, 13273, 13275, 13277, 13279, 13282, + 13284, 13286, 13288, 13290, 13292, 13294, 13296, 13298, 13300, 13302, + 13304, 13306, 13308, 13310, 13312, 13314, 13316, 13318, 13320, 13322, + 13324, 13326, 13328, 13331, 13333, 13335, 13337, 13340, 13342, 13344, + 13346, 13348, 13350, 13352, 13354, 13356, 13358, 13360, 13362, 13364, + 13366, 13368, 13370, 13372, 13374, 13376, 13378, 13380, 13382, 13384, + 13386, 13388, 13390, 13392, 13394, 13396, 13398, 13400, 13402, 13404, + 13407, 13409, 13411, 13413, 13415, 13417, 13419, 13422, 13425, 13427, + 13429, 13431, 13433, 13435, 13437, 13439, 13441, 13443, 13445, 13448, + 13450, 13452, 13454, 13456, 13459, 13461, 13463, 13465, 13467, 13469, + 13471, 13473, 13475, 13477, 13480, 13482, 13485, 13487, 13489, 13491, + 13493, 13495, 13497, 13499, 13501, 13503, 13505, 13507, 13510, 13512, + 13514, 13516, 13518, 13520, 13523, 13525, 13528, 13531, 13533, 13535, + 13537, 13539, 13542, 13545, 13547, 13549, 13551, 13553, 13555, 13557, + 13559, 13561, 13563, 13565, 13567, 13570, 13572, 13574, 13576, 13578, + 13580, 13582, 13584, 13586, 13588, 13590, 13592, 13594, 13596, 13598, + 13600, 13602, 13604, 13606, 13608, 13611, 13613, 13615, 13617, 13619, + 13621, 13624, 13626, 13628, 13630, 13632, 13634, 13636, 13638, 13640, + 13642, 13644, 13646, 13649, 13651, 13653, 13655, 13657, 13659, 13661, + 13663, 13665, 13667, 13669, 13671, 13673, 13675, 13677, 13679, 13681, + 13683, 13685, 13688, 13690, 13692, 13694, 13696, 13698, 13701, 13703, + 13705, 13707, 13709, 13711, 13713, 13715, 13717, 13720, 13722, 13724, + 13726, 13729, 13731, 13733, 13735, 13737, 13739, 13741, 13744, 13747, + 13750, 13752, 13755, 13757, 13759, 13761, 13763, 13765, 13767, 13769, + 13771, 13773, 13775, 13778, 13780, 13782, 13784, 13786, 13788, 13790, + 13793, 13795, 13797, 13800, 13803, 13805, 13807, 13809, 13811, 13813, + 13815, 13817, 13819, 13821, 13824, 13826, 13829, 13831, 13834, 13836, + 13838, 13840, 13843, 13845, 13847, 13850, 13853, 13855, 13857, 13859, + 13861, 13863, 13865, 13867, 13869, 13871, 13873, 13875, 13877, 13879, + 13882, 13884, 13887, 13889, 13892, 13894, 13897, 13900, 13903, 13905, + 13907, 13909, 13912, 13915, 13918, 13921, 13923, 13925, 13927, 13929, + 13931, 13933, 13935, 13937, 13940, 13942, 13944, 13946, 13948, 13951, + 13953, 13956, 13959, 13961, 13963, 13965, 13967, 13969, 13971, 13974, + 13977, 13980, 13982, 13984, 13987, 13989, 13991, 13993, 13996, 13998, + 14000, 14002, 14004, 14006, 14009, 14011, 14013, 14015, 14017, 14019, + 14021, 14024, 14027, 14029, 14032, 14034, 14037, 14039, 14041, 14043, + 14046, 14049, 14051, 14054, 14056, 14059, 14061, 14063, 14065, 14067, + 14069, 14071, 14074, 14077, 14080, 14083, 14085, 14087, 14089, 14091, + 14093, 14095, 14097, 14099, 14101, 14103, 14105, 14107, 14110, 14112, + 14114, 14116, 14118, 14120, 14122, 14124, 14126, 14128, 14130, 14132, + 14134, 14137, 14140, 14143, 14145, 14147, 14149, 14151, 14154, 14156, + 14159, 14161, 14163, 14166, 14169, 14171, 14173, 14175, 14177, 14179, + 14181, 14183, 14185, 14187, 14189, 14191, 14193, 14195, 14197, 14199, + 14201, 14203, 14205, 14207, 14210, 14212, 14214, 14216, 14218, 14220, + 14223, 14226, 14228, 14230, 14232, 14234, 14236, 14238, 14241, 14243, + 14245, 14247, 14249, 14252, 14255, 14257, 14259, 14261, 14264, 14266, + 14268, 14271, 14274, 14276, 14278, 14280, 14283, 14285, 14287, 14289, + 14291, 14293, 14295, 14297, 14300, 14302, 14304, 14306, 14309, 14311, + 14313, 14315, 14317, 14320, 14323, 14325, 14327, 14329, 14332, 14334, + 14337, 14339, 14341, 14343, 14346, 14348, 14350, 14352, 14354, 14356, + 14358, 14360, 14363, 14365, 14367, 14369, 14371, 14373, 14375, 14378, + 14380, 14383, 14386, 14389, 14391, 14393, 14395, 14397, 14399, 14401, + 14403, 14405, 0, 0, +}; + +/* NFC pairs */ +#define COMP_SHIFT1 2 +#define COMP_SHIFT2 1 +static const unsigned short comp_index0[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, + 5, 6, 7, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 11, 12, 13, 14, 0, 0, 0, 0, 0, + 15, 16, 17, 0, 0, 0, 0, 18, 19, 20, 21, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 0, 0, 0, 27, 28, 29, 30, 0, 0, 0, 0, 31, 32, 33, 34, + 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 36, 0, 37, 38, 39, 0, 0, 0, 40, 41, 42, + 43, 0, 0, 0, 0, 44, 45, 46, 0, 0, 0, 0, 0, 47, 48, 49, 50, 0, 0, 0, 51, + 52, 53, 54, 0, 0, 0, 0, 55, 56, 0, 0, 0, 0, 0, 0, 57, 58, 59, 60, 0, 0, + 0, 0, 61, 62, 63, 0, 0, 0, 0, 0, 64, 65, 66, 67, 0, 0, 0, 68, 69, 70, 71, + 0, 0, 0, 0, 72, 0, 73, 0, 0, 0, 0, 0, 74, 0, 75, 0, 0, 0, 0, 0, 76, 0, 0, + 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 80, 81, 82, 83, 0, 0, 0, 0, 84, + 85, 86, 0, 0, 0, 0, 0, 87, 88, 0, 89, 0, 0, 0, 90, 91, 0, 92, 0, 0, 0, 0, + 0, 93, 94, 95, 0, 0, 0, 0, 96, 97, 98, 99, 0, 0, 0, 0, 100, 0, 0, 0, 0, + 0, 0, 101, 102, 0, 103, 0, 0, 0, 0, 104, 105, 106, 107, 0, 0, 0, 0, 108, + 109, 110, 111, 0, 0, 0, 0, 112, 113, 0, 0, 0, 0, 0, 114, 115, 116, 117, + 0, 0, 0, 0, 118, 119, 120, 121, 0, 0, 0, 0, 122, 0, 123, 0, 0, 0, 0, 124, + 125, 126, 127, 128, 0, 0, 0, 129, 130, 131, 132, 0, 0, 0, 0, 133, 134, 0, + 0, 0, 0, 0, 0, 135, 136, 137, 138, 0, 0, 0, 139, 140, 141, 142, 0, 0, 0, + 0, 0, 143, 144, 145, 0, 0, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0, 150, 0, + 151, 0, 0, 0, 0, 152, 153, 154, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, + 156, 157, 158, 0, 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 163, 0, 0, 0, + 164, 0, 0, 0, 165, 166, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 168, + 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, + 172, 173, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 175, 176, 0, 0, 0, 0, + 0, 0, 177, 178, 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, + 0, 0, 0, 181, 182, 183, 0, 0, 0, 0, 0, 184, 185, 0, 0, 0, 0, 0, 0, 186, + 0, 0, 0, 0, 0, 0, 0, 187, 0, 0, 0, 0, 0, 0, 188, 189, 0, 0, 0, 0, 0, 0, + 190, 0, 0, 0, 0, 0, 0, 0, 191, 192, 0, 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, + 0, 194, 195, 0, 0, 0, 0, 0, 0, 196, 197, 0, 0, 0, 0, 0, 0, 198, 0, 0, 0, + 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, 0, 200, 201, 202, 0, 0, 0, 0, 0, 203, + 204, 0, 0, 0, 0, 0, 0, 205, 206, 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, + 208, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, + 0, 0, 211, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 213, 0, 0, 0, + 0, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, + 0, 0, 0, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 219, 0, + 0, 0, 0, 0, 0, 220, 221, 222, 0, 0, 0, 0, 0, 223, 224, 225, 0, 0, 0, 0, + 0, 226, 227, 228, 0, 0, 0, 0, 0, 229, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, + 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 234, 0, 0, 0, 0, 0, 0, 0, 235, 0, + 0, 0, 0, 0, 0, 0, 236, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 0, 238, + 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, + 241, 0, 0, 0, 0, 0, 0, 242, 0, 243, 244, 0, 0, 0, 0, 245, 246, 0, 0, 0, + 0, 0, 247, 0, 248, 0, 249, 0, 0, 0, 250, 251, 252, 0, 0, 0, 0, 0, 253, 0, + 254, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 256, 257, 258, 0, 0, 0, 0, 0, + 259, 0, 260, 0, 261, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 0, 263, 0, + 0, 0, 264, 265, 266, 0, 267, 0, 0, 0, 268, 0, 269, 0, 0, 0, 0, 0, 270, 0, + 271, 272, 0, 0, 0, 0, 273, 274, 0, 275, 0, 0, 0, 276, 0, 277, 0, 0, 0, 0, + 0, 0, 0, 278, 0, 0, 0, 0, 0, 279, 280, 281, 282, 0, 0, 0, 0, 283, 284, 0, + 285, 0, 0, 0, 286, 0, 0, 0, 287, 0, 0, 0, 288, 0, 0, 0, 289, 0, 0, 0, 0, + 0, 0, 290, 0, 0, 0, 0, 291, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 0, 0, + 0, 293, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 295, 0, 0, 0, 0, 0, + 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 297, 0, 0, 0, 0, 0, 0, 298, 299, 0, 0, 0, + 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 0, 301, 0, 0, 0, 0, 0, 0, 0, 302, 0, 0, + 0, 0, 0, 0, 0, 303, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 305, 0, + 0, 0, 0, 0, 0, 0, 306, 0, 0, 0, 0, 0, 0, 307, 0, 0, 0, 0, 0, 0, 0, 308, + 0, 0, 0, 0, 0, 0, 0, 309, 0, 0, 0, 0, 0, 0, 0, 310, 0, 0, 0, 0, 0, 0, + 311, 312, 0, 0, 0, 0, 0, 0, 313, 0, 0, 0, 0, 0, 0, 0, 314, 0, 0, 0, 0, 0, + 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, 316, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, + 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, + 0, 0, 0, 0, 0, 321, 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 323, 0, + 0, 0, 0, 0, 0, 0, 324, 0, 0, 0, 0, 0, 0, 325, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 326, 0, 0, 0, 0, 0, 0, 0, 327, 0, 0, 0, 0, 0, 0, 0, 328, 0, 0, 0, 0, + 0, 0, 329, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 0, 0, 0, 0, 0, 331, 0, 0, 0, + 0, 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 0, 333, 0, 0, 0, 0, 0, 0, 334, 0, 0, + 0, 0, 0, 0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, 0, 0, 0, 0, + 338, 0, 0, 0, 0, 0, 0, 339, 0, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, + 0, 341, 0, 0, 0, 0, 0, 0, 0, 342, 0, 0, 0, 0, 0, 0, 0, 343, 0, 0, 0, 0, + 0, 0, 344, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 347, 0, 0, 0, + 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 350, 0, + 0, 0, 0, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 353, + 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 0, 0, 0, 0, + 356, 0, 0, 0, 0, 0, 0, 357, 0, 0, 0, 0, 0, 0, 0, 358, 0, 0, 0, 0, 0, 0, + 0, 359, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 361, 0, 362, 0, 0, 0, + 0, 0, 0, 0, 363, 0, 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, 0, 0, 0, 0, 365, 0, + 0, 0, 0, 0, 0, 0, 366, 0, 0, 0, 0, 0, 0, 367, 0, 0, 0, 0, 0, 0, 0, 368, + 0, 0, 0, 0, 0, 0, 369, 370, 0, 0, 0, 0, 0, 0, 371, 0, 0, 0, 0, 0, 0, 0, + 372, 0, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 0, 0, 0, 0, + 0, 375, 0, 0, 376, 0, 0, 0, 0, 377, 0, 0, 378, 0, 0, 0, 0, 0, 0, 0, 379, + 0, 0, 0, 0, 0, 0, 0, 380, 0, 0, 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 0, 0, + 382, 0, 0, 0, 0, 0, 0, 0, 383, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 0, 385, 0, + 0, 386, 0, 0, 0, 0, 387, 0, 0, 388, 0, 0, 0, 0, 0, 0, 0, 389, 0, 0, 0, 0, + 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 391, 0, 0, 0, 0, 0, 0, 0, 392, 0, 0, 0, + 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 394, 0, 0, 0, 395, 0, 0, 0, 0, 0, + 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 0, 398, 0, 0, 0, 0, + 0, 0, 0, 399, 0, 0, 400, 0, 0, 0, 0, 401, 0, 0, 402, 0, 0, 0, 0, 0, 0, 0, + 403, 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 405, 0, 0, 0, 0, 0, 0, + 0, 406, 0, 0, 0, 0, 0, 0, 0, 407, 0, 0, 0, 0, 0, 0, 0, 408, 0, 0, 0, 409, + 0, 0, 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 0, 0, 0, 0, 413, 0, 0, 0, + 0, 0, 0, 0, 414, 0, 0, 0, 0, 0, 0, 415, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, 0, 418, 0, 0, 0, 419, 0, 0, 420, 0, + 0, 0, 0, 421, 0, 0, 422, 0, 0, 0, 423, 0, 0, 0, 424, 0, 0, 0, 425, 0, 0, + 0, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 429, 0, + 0, 0, 0, 0, 0, 0, 430, 0, 0, 0, 0, 0, 0, 0, 431, 0, 0, 432, 0, 0, 0, 0, + 433, 0, 0, 434, 0, 0, 0, 435, 0, 0, 0, 436, 0, 0, 0, 437, 0, 0, 0, 438, + 0, 0, 0, 439, 0, 0, 440, 0, 0, 0, 0, 0, 0, 0, 441, 0, 0, 0, 0, 0, 0, 0, + 442, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 444, 0, 0, 0, 0, 0, 0, + 0, 445, 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, 0, 447, 0, 0, 0, 448, 0, 0, 0, + 449, 0, 0, 450, 0, 0, 0, 0, 0, 0, 0, 451, 0, 0, 0, 0, 0, 0, 0, 452, 0, 0, + 0, 0, 0, 0, 0, 453, 0, 0, 0, 0, 0, 0, 454, 0, 0, 0, 0, 0, 0, 0, 455, 0, + 0, 0, 0, 0, 0, 0, 456, 0, 0, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, 458, + 0, 0, 0, 0, 0, 0, 0, 459, 0, 0, 0, 0, 0, 0, 0, 460, 0, 0, 0, 461, 0, 0, + 0, 462, 0, 0, 0, 0, 0, 0, 463, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 465, 0, + 0, 0, 466, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, + 0, 0, 0, 469, 0, 0, 0, 0, 0, 0, 0, 470, 0, 0, 0, 0, 0, 0, 471, 0, 0, 0, + 0, 0, 0, 0, 472, 0, 0, 0, 0, 0, 0, 0, 473, 0, 0, 0, 0, 0, 0, 0, 474, 0, + 0, 0, 0, 0, 0, 475, 0, 0, 0, 0, 0, 0, 0, 476, 0, 0, 0, 0, 0, 0, 0, 477, + 0, 0, 0, 0, 0, 0, 0, 478, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 0, + 480, 0, 0, 0, 0, 0, 0, 0, 481, 0, 0, 0, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, + 0, 483, 0, 0, 0, 0, 0, 0, 0, 484, 0, 0, 0, 0, 0, 0, 0, 485, 0, 0, 0, 0, + 0, 0, 0, 486, 0, 0, 0, 0, 0, 0, 487, 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, 0, + 0, 0, 0, 0, 489, 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, + 0, 0, 0, 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, 494, + 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, + 497, 0, 0, 0, 0, 0, 0, 0, 498, 0, 0, 0, 0, 0, 0, 499, 0, 0, 0, 0, 0, 0, + 0, 500, 0, 0, 0, 0, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, 502, 0, 0, 0, 0, + 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 0, 505, 0, 0, 0, + 0, 0, 0, 0, 506, 0, 0, 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 508, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, + 0, 0, 511, 0, 0, 0, 0, 0, 0, 512, 0, 0, 0, 0, 0, 0, 0, 513, 0, 0, 0, 0, + 0, 0, 0, 514, 0, 0, 0, 0, 0, 0, 0, 515, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, + 0, 0, 0, 0, 517, 0, 0, 0, 0, 0, 0, 0, 518, 0, 0, 0, 0, 0, 0, 0, 519, 0, + 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, 0, 0, 0, 0, 522, + 0, 0, 0, 0, 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, + 525, 0, 0, 0, 0, 0, 0, 0, 526, 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, 0, 0, 0, + 0, 528, 0, 0, 0, 0, 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, + 0, 0, 0, 531, 0, 0, 0, 0, 0, 0, 532, 0, 0, 0, 0, 0, 0, 0, 533, 0, 0, 0, + 0, 0, 0, 0, 534, 0, 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, 0, 0, 0, 536, 0, 0, + 0, 0, 0, 0, 0, 537, 0, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 0, 539, + 0, 0, 0, 0, 0, 0, 540, 0, 0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, 0, + 542, 0, 0, 0, 0, 0, 0, 0, 543, 0, 0, 0, 0, 0, 0, 544, 0, 0, 0, 0, 0, 0, + 0, 545, 0, 0, 0, 0, 0, 0, 0, 546, 0, 0, 0, 0, 0, 0, 0, 547, 0, 0, 0, 0, + 0, 0, 548, 0, 0, 0, 0, 0, 0, 0, 549, 0, 0, 0, 0, 0, 0, 0, 550, 0, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 0, 552, 0, 0, 0, 0, 0, 0, 0, 553, 0, 0, + 0, 0, 0, 0, 0, 554, 0, 0, 0, 0, 0, 0, 0, 555, 0, 0, 0, 0, 0, 0, 0, 556, + 0, 0, 0, 0, 0, 0, 557, 0, 0, 0, 0, 0, 0, 0, 558, 0, 0, 0, 0, 0, 0, 0, + 559, 0, 0, 0, 0, 0, 0, 0, 560, 0, 0, 0, 0, 0, 0, 0, 561, 0, 0, 0, 0, 0, + 0, 0, 562, 0, 0, 0, 0, 0, 0, 0, 563, 0, 0, 0, 0, 0, 0, 564, +}; + +static const unsigned short comp_index1[] = { + 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 4, 5, 6, 7, 8, 9, 10, + 0, 11, 12, 0, 13, 0, 0, 0, 0, 0, 0, 14, 15, 0, 0, 0, 0, 16, 0, 0, 0, 0, + 0, 17, 18, 0, 19, 0, 20, 0, 0, 0, 0, 21, 0, 0, 0, 22, 0, 23, 0, 0, 24, 0, + 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 33, 34, 0, 35, 0, 36, 37, 38, 0, 0, + 0, 0, 0, 39, 0, 0, 0, 40, 41, 42, 43, 0, 44, 0, 0, 0, 0, 45, 0, 0, 0, 0, + 0, 46, 0, 47, 0, 48, 0, 0, 49, 0, 50, 0, 51, 0, 0, 52, 53, 54, 55, 56, + 57, 58, 0, 59, 0, 0, 60, 61, 0, 0, 0, 62, 0, 0, 0, 0, 0, 63, 64, 0, 0, + 65, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, 0, 70, 0, 71, + 72, 0, 73, 0, 74, 0, 0, 75, 0, 0, 0, 0, 76, 0, 0, 77, 78, 0, 79, 0, 80, + 0, 0, 81, 0, 82, 83, 0, 84, 0, 0, 0, 0, 0, 85, 86, 87, 88, 89, 90, 91, 0, + 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 95, 0, 0, 0, 96, 0, 0, 97, 0, 98, 99, 0, + 100, 0, 101, 0, 0, 102, 0, 103, 104, 0, 105, 0, 106, 0, 0, 107, 0, 108, + 0, 0, 0, 109, 0, 110, 0, 0, 111, 0, 112, 113, 0, 114, 0, 0, 0, 0, 0, 115, + 116, 117, 118, 119, 120, 121, 0, 122, 123, 0, 124, 125, 0, 0, 0, 126, 0, + 0, 127, 0, 0, 128, 129, 0, 130, 131, 0, 0, 0, 0, 0, 132, 0, 0, 0, 133, + 134, 135, 136, 137, 0, 0, 0, 138, 0, 0, 139, 140, 0, 141, 0, 142, 0, 0, + 143, 0, 0, 0, 0, 144, 0, 145, 146, 147, 148, 149, 150, 151, 0, 152, 153, + 0, 154, 0, 0, 155, 0, 0, 0, 0, 156, 157, 0, 0, 0, 0, 0, 158, 159, 0, 160, + 0, 161, 162, 0, 0, 0, 163, 0, 164, 0, 0, 165, 0, 166, 167, 0, 168, 0, + 169, 170, 171, 172, 173, 174, 175, 0, 176, 0, 177, 178, 179, 0, 0, 0, 0, + 0, 180, 0, 0, 0, 181, 182, 183, 184, 0, 185, 186, 0, 0, 0, 0, 0, 187, 0, + 188, 0, 189, 0, 0, 190, 0, 191, 0, 192, 193, 0, 194, 195, 196, 197, 198, + 199, 200, 0, 201, 0, 0, 202, 203, 0, 0, 0, 204, 0, 0, 0, 205, 0, 0, 0, 0, + 0, 206, 0, 0, 0, 0, 207, 0, 0, 208, 0, 209, 0, 0, 210, 0, 211, 0, 0, 0, + 0, 212, 0, 0, 213, 0, 214, 215, 0, 216, 0, 217, 0, 0, 218, 219, 0, 0, 0, + 0, 0, 0, 220, 221, 0, 222, 0, 223, 0, 0, 224, 0, 225, 226, 0, 227, 0, 0, + 0, 0, 0, 228, 229, 230, 231, 232, 233, 234, 0, 235, 0, 0, 236, 0, 0, 0, + 237, 0, 0, 238, 0, 0, 0, 239, 0, 0, 240, 0, 241, 242, 0, 243, 0, 244, 0, + 0, 245, 0, 0, 0, 0, 0, 246, 247, 0, 248, 0, 249, 0, 0, 250, 0, 251, 0, 0, + 0, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 0, 258, 259, 260, 261, + 262, 263, 264, 0, 265, 266, 0, 267, 268, 0, 0, 0, 269, 0, 0, 270, 0, 0, + 0, 0, 0, 0, 271, 272, 0, 273, 274, 0, 0, 0, 275, 0, 276, 0, 0, 0, 277, + 278, 279, 280, 281, 0, 0, 0, 282, 0, 0, 283, 284, 0, 285, 0, 286, 0, 0, + 287, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 289, 0, 290, 0, 0, 0, 0, 291, 292, + 0, 0, 293, 0, 0, 0, 0, 294, 295, 0, 0, 0, 0, 0, 0, 296, 0, 297, 0, 0, 0, + 0, 298, 0, 0, 299, 300, 0, 0, 301, 0, 0, 302, 0, 0, 0, 0, 0, 0, 303, 304, + 0, 0, 305, 0, 0, 306, 0, 307, 308, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 0, + 0, 0, 311, 0, 312, 0, 0, 313, 0, 0, 0, 0, 0, 314, 315, 0, 0, 316, 0, 0, + 0, 0, 317, 318, 0, 0, 0, 0, 0, 0, 319, 0, 320, 0, 0, 0, 0, 321, 0, 0, + 322, 323, 0, 0, 324, 0, 0, 325, 0, 0, 0, 0, 0, 0, 326, 327, 0, 0, 328, 0, + 0, 329, 0, 330, 331, 0, 0, 0, 0, 0, 332, 333, 0, 0, 0, 0, 0, 0, 334, 0, + 335, 0, 0, 336, 0, 0, 0, 0, 0, 337, 338, 0, 0, 339, 0, 0, 340, 341, 0, 0, + 342, 0, 0, 343, 0, 0, 0, 0, 0, 0, 344, 0, 0, 345, 0, 0, 346, 0, 0, 0, 0, + 0, 347, 0, 0, 348, 0, 0, 349, 0, 0, 350, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, + 352, 0, 353, 0, 0, 354, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 356, 357, 0, 0, + 358, 0, 0, 0, 359, 0, 0, 360, 361, 0, 0, 362, 0, 0, 0, 363, 0, 0, 364, + 365, 0, 0, 366, 0, 0, 0, 367, 0, 0, 368, 369, 0, 0, 370, 0, 0, 0, 371, 0, + 0, 0, 372, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 375, 0, 0, 376, 0, + 0, 377, 0, 0, 0, 0, 0, 0, 378, 0, 0, 379, 0, 0, 380, 0, 0, 0, 0, 0, 381, + 0, 382, 0, 383, 384, 0, 0, 0, 0, 0, 0, 385, 386, 0, 0, 0, 0, 0, 0, 387, + 0, 0, 0, 388, 0, 0, 389, 0, 0, 390, 0, 0, 0, 0, 391, 0, 392, 393, 0, 0, + 0, 394, 0, 0, 0, 395, 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 398, 0, + 399, 400, 0, 0, 0, 401, 0, 0, 0, 402, 0, 0, 403, 0, 0, 404, 0, 0, 0, 0, + 0, 0, 405, 0, 0, 406, 0, 0, 0, 0, 407, 0, 408, 0, 0, 0, 0, 409, 0, 0, + 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 413, 0, 0, 414, 0, 0, 0, 0, 0, + 0, 415, 416, 0, 417, 418, 0, 0, 0, 419, 0, 0, 420, 0, 0, 0, 0, 421, 0, 0, + 422, 0, 0, 423, 0, 0, 0, 424, 0, 425, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, + 0, 428, 429, 0, 0, 0, 0, 0, 0, 430, 0, 0, 431, 0, 0, 0, 0, 432, 0, 433, + 0, 0, 0, 0, 434, 0, 435, 0, 0, 0, 0, 0, 0, 436, 437, 0, 0, 438, 0, 0, + 439, 0, 440, 441, 0, 0, 0, 442, 0, 0, 443, 0, 444, 445, 0, 446, 447, 0, + 0, 448, 0, 0, 0, 449, 0, 450, 451, 0, 0, 0, 452, 0, 0, 0, 0, 0, 453, 0, + 454, 455, 0, 456, 457, 0, 0, 0, 0, 0, 0, 458, 0, 0, 459, 0, 460, 461, 0, + 0, 0, 462, 0, 0, 463, 0, 464, 465, 0, 466, 467, 0, 0, 468, 0, 0, 0, 469, + 0, 470, 471, 0, 0, 0, 472, 0, 0, 0, 0, 0, 473, 0, 474, 475, 0, 476, 477, + 0, 0, 0, 0, 0, 0, 478, 0, 0, 479, 0, 0, 480, 0, 0, 0, 0, 0, 481, 0, 0, + 482, 0, 0, 0, 483, 0, 0, 484, 0, 0, 485, 0, 0, 0, 0, 0, 0, 486, 0, 0, + 487, 488, 0, 489, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, 492, 0, 0, 493, + 0, 0, 0, 494, 0, 0, 495, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 497, 0, 0, 0, + 498, 499, 0, 0, 0, 500, 0, 0, 0, 0, 0, 501, 502, 0, 503, 0, 0, 0, 504, 0, + 0, 0, 505, 0, 0, 506, 507, 0, 0, 0, 0, 0, 508, 0, 0, 0, 509, 510, 0, 0, + 0, 0, 0, 511, 0, 0, 0, 512, 513, 0, 514, 0, 0, 0, 0, 515, 0, 0, 516, 0, + 0, 517, 0, 0, 0, 0, 0, 0, 518, 0, 0, 519, 0, 0, 520, 0, 0, 521, 0, 0, 0, + 0, 0, 0, 522, 0, 0, 523, 0, 0, 524, 0, 0, 525, 0, 0, 0, 0, 0, 0, 526, 0, + 0, 0, 527, 0, 0, 528, 0, 0, 529, 0, 0, 530, 0, 0, 0, 531, 0, 0, 0, 0, 0, + 0, 532, 533, 534, 0, 0, 0, 0, 0, 535, 536, 0, 0, 0, 0, 0, 537, 0, 0, 538, + 0, 0, 539, 0, 0, 0, 0, 0, 0, 540, 0, 541, 0, 0, 0, 0, 0, 542, 543, 0, 0, + 0, 0, 0, 544, 0, 0, 545, 0, 0, 546, 0, 0, 0, 0, 0, 0, 547, 0, 0, 548, 0, + 0, 549, 0, 0, 550, 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 552, 553, 0, 0, 0, 0, + 0, 554, 0, 0, 555, 0, 0, 556, 0, 0, 0, 0, 0, 0, 557, 0, 0, 558, 0, 0, + 559, 0, 0, 560, 0, 0, 0, 0, 561, 0, 0, 562, 0, 0, 0, 0, 0, 0, 563, 0, 0, + 564, 0, 0, 565, 0, 0, 0, 0, 0, 566, 567, 0, 0, 0, 0, 0, 568, 0, 0, 569, + 0, 0, 570, 0, 0, 0, 0, 0, 0, 571, 0, 0, 572, 0, 0, 573, 0, 0, 574, 0, 0, + 0, 0, 575, 0, 0, 0, 0, 0, 576, 577, 0, 0, 0, 0, 0, 578, 0, 0, 579, 0, 0, + 580, 0, 0, 0, 0, 0, 0, 581, 0, 0, 582, 0, 0, 583, 0, 0, 584, 0, 0, 0, 0, + 585, 0, 0, 0, 0, 0, 586, 587, 0, 0, 0, 0, 0, 588, 0, 0, 0, 0, 589, 0, + 590, 0, 0, 0, 0, 591, 0, 592, 0, 0, 0, 0, 593, 0, 0, 594, 0, 0, 0, 0, 0, + 0, 595, 0, 0, 596, 0, 0, 597, 0, 0, 0, 0, 0, 598, 599, 0, 0, 0, 0, 0, + 600, 0, 0, 0, 0, 601, 0, 602, 0, 0, 0, 0, 603, 0, 604, 0, 0, 0, 0, 605, + 0, 0, 0, 0, 0, 606, 0, 0, 607, 0, 0, 608, 0, 0, 609, 0, 0, 0, 0, 0, 0, + 610, 0, 0, 611, 0, 0, 612, 0, 0, 0, 0, 613, 0, 614, 0, 0, 0, 0, 615, 0, + 0, 0, 0, 0, 616, 0, 0, 617, 0, 0, 618, 0, 0, 619, 0, 0, 0, 0, 0, 0, 620, + 0, 0, 621, 0, 0, 622, 0, 0, 623, 0, 0, 0, 0, 0, 0, 624, 0, 0, 625, 0, 0, + 626, 0, 0, 0, 0, 627, 0, 628, 0, 0, 0, 0, 0, 0, 629, 0, 0, 630, 0, 0, 0, + 0, 631, 0, 632, 0, 0, 0, 0, 0, 633, 0, 0, 634, 0, 0, 635, 0, 0, 636, 0, + 0, 0, 0, 0, 0, 637, 0, 0, 638, 0, 0, 639, 0, 0, 640, 0, 0, 0, 0, 0, 0, + 641, 0, 0, 642, 0, 0, 643, 0, 0, 644, 0, 0, 0, 0, 0, 0, 645, 0, 0, 646, + 0, 0, 647, 0, 0, 648, 0, 0, 0, 0, 0, 0, 649, 0, 0, 650, 0, 0, 651, 0, 0, + 652, 0, 0, 0, 0, 0, 0, 653, 0, 0, 654, 0, 0, 655, 0, 0, 656, 0, 0, 0, 0, + 0, 0, 657, 0, 0, 658, 0, 0, 659, 0, 0, 660, 0, 0, 0, 0, 0, 0, 661, 0, 0, + 662, 0, 0, 663, 0, 0, 664, 0, 0, 0, 0, 0, 0, 665, 0, 0, 666, 0, 0, 667, + 0, 0, 668, 0, 0, 0, 0, 0, 0, 669, 0, 0, 670, 0, 0, 671, 0, 0, 672, 0, 0, + 0, 0, 0, 0, 673, 0, 0, 0, 674, 0, 0, 675, 0, 0, 676, 0, 0, 677, 0, 0, 0, + 0, 0, 0, 678, 0, 0, 679, 0, 0, 680, 0, 0, 681, 0, 0, 0, 0, 0, 0, 682, 0, + 0, 683, 0, 0, 684, 0, 0, 685, 0, 0, 0, 0, 0, 0, 686, 0, 0, 687, 0, 0, + 688, 0, 0, 689, 0, 0, 0, 0, 0, 0, 690, 0, 0, 691, 0, 0, 692, 0, 0, 693, + 0, 0, 0, 0, 0, 0, 694, 0, 0, 695, 0, 0, 696, 0, 0, 697, 0, 0, 0, 0, 0, 0, + 698, 0, 0, 699, 0, 0, 700, 0, 0, 701, 0, 0, 0, 0, 0, 0, 702, 0, 0, 703, + 0, 0, 704, 0, 0, 705, 0, 0, 0, 0, 0, 0, 706, 0, 0, 707, 0, 0, 708, 0, 0, + 709, 0, 0, 0, 0, 0, 0, 710, 0, 0, 711, 0, 0, 712, 0, 0, 713, 0, 0, 0, 0, + 0, 0, 714, 0, 0, 715, 0, 0, 716, 0, 0, 717, 0, 0, 0, 0, 0, 0, 718, 0, 0, + 719, 0, 0, 720, 0, 0, 721, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 723, 0, 0, + 724, 0, 0, 725, 0, 0, 726, 0, 0, 0, 727, 0, 0, 0, 728, 729, 0, 0, 730, 0, + 0, 0, 0, 0, 0, 731, +}; + +static const unsigned int comp_data[] = { + 0, 0, 0, 8814, 0, 8800, 0, 8815, 192, 193, 194, 195, 256, 258, 550, 196, + 7842, 197, 0, 461, 512, 514, 0, 7840, 0, 7680, 260, 0, 7682, 0, 0, 7684, + 7686, 0, 0, 262, 264, 0, 266, 0, 0, 268, 0, 199, 7690, 0, 0, 270, 0, + 7692, 0, 7696, 0, 7698, 7694, 0, 200, 201, 202, 7868, 274, 276, 278, 203, + 7866, 0, 0, 282, 516, 518, 0, 7864, 0, 552, 280, 7704, 0, 7706, 7710, 0, + 0, 500, 284, 0, 7712, 286, 288, 0, 0, 486, 0, 290, 292, 0, 7714, 7718, 0, + 542, 0, 7716, 0, 7720, 7722, 0, 204, 205, 206, 296, 298, 300, 304, 207, + 7880, 0, 0, 463, 520, 522, 0, 7882, 302, 0, 0, 7724, 308, 0, 0, 7728, 0, + 488, 0, 7730, 0, 310, 7732, 0, 0, 313, 0, 317, 0, 7734, 0, 315, 0, 7740, + 7738, 0, 0, 7742, 7744, 0, 0, 7746, 504, 323, 0, 209, 7748, 0, 0, 327, 0, + 7750, 0, 325, 0, 7754, 7752, 0, 210, 211, 212, 213, 332, 334, 558, 214, + 7886, 0, 336, 465, 524, 526, 416, 7884, 490, 0, 0, 7764, 7766, 0, 0, 340, + 7768, 0, 0, 344, 528, 530, 0, 7770, 0, 342, 7774, 0, 0, 346, 348, 0, + 7776, 0, 0, 352, 0, 7778, 536, 350, 7786, 0, 0, 356, 0, 7788, 538, 354, + 0, 7792, 7790, 0, 217, 218, 219, 360, 362, 364, 0, 220, 7910, 366, 368, + 467, 532, 534, 431, 7908, 7794, 0, 370, 7798, 0, 7796, 0, 7804, 0, 7806, + 7808, 7810, 372, 0, 7814, 7812, 0, 7816, 7818, 7820, 7922, 221, 374, + 7928, 562, 0, 7822, 376, 7926, 0, 0, 7924, 0, 377, 7824, 0, 379, 0, 0, + 381, 0, 7826, 7828, 0, 224, 225, 226, 227, 257, 259, 551, 228, 7843, 229, + 0, 462, 513, 515, 0, 7841, 0, 7681, 261, 0, 7683, 0, 0, 7685, 7687, 0, 0, + 263, 265, 0, 267, 0, 0, 269, 0, 231, 7691, 0, 0, 271, 0, 7693, 0, 7697, + 0, 7699, 7695, 0, 232, 233, 234, 7869, 275, 277, 279, 235, 7867, 0, 0, + 283, 517, 519, 0, 7865, 0, 553, 281, 7705, 0, 7707, 7711, 0, 0, 501, 285, + 0, 7713, 287, 289, 0, 0, 487, 0, 291, 293, 0, 7715, 7719, 0, 543, 0, + 7717, 0, 7721, 7723, 0, 7830, 0, 236, 237, 238, 297, 299, 301, 0, 239, + 7881, 0, 0, 464, 521, 523, 0, 7883, 303, 0, 0, 7725, 309, 0, 0, 496, 0, + 7729, 0, 489, 0, 7731, 0, 311, 7733, 0, 0, 314, 0, 318, 0, 7735, 0, 316, + 0, 7741, 7739, 0, 0, 7743, 7745, 0, 0, 7747, 505, 324, 0, 241, 7749, 0, + 0, 328, 0, 7751, 0, 326, 0, 7755, 7753, 0, 242, 243, 244, 245, 333, 335, + 559, 246, 7887, 0, 337, 466, 525, 527, 417, 7885, 491, 0, 0, 7765, 7767, + 0, 0, 341, 7769, 0, 0, 345, 529, 531, 0, 7771, 0, 343, 7775, 0, 0, 347, + 349, 0, 7777, 0, 0, 353, 0, 7779, 537, 351, 7787, 7831, 0, 357, 0, 7789, + 539, 355, 0, 7793, 7791, 0, 249, 250, 251, 361, 363, 365, 0, 252, 7911, + 367, 369, 468, 533, 535, 432, 7909, 7795, 0, 371, 7799, 0, 7797, 0, 7805, + 0, 7807, 7809, 7811, 373, 0, 7815, 7813, 0, 7832, 0, 7817, 7819, 7821, + 7923, 253, 375, 7929, 563, 0, 7823, 255, 7927, 7833, 0, 7925, 0, 378, + 7825, 0, 380, 0, 0, 382, 0, 7827, 7829, 0, 8173, 901, 8129, 0, 7846, + 7844, 0, 7850, 7848, 0, 478, 0, 0, 506, 0, 508, 482, 0, 0, 7688, 7872, + 7870, 0, 7876, 7874, 0, 0, 7726, 7890, 7888, 0, 7894, 7892, 0, 0, 7756, + 556, 0, 0, 7758, 554, 0, 0, 510, 475, 471, 469, 0, 0, 473, 7847, 7845, 0, + 7851, 7849, 0, 479, 0, 0, 507, 0, 509, 483, 0, 0, 7689, 7873, 7871, 0, + 7877, 7875, 0, 0, 7727, 7891, 7889, 0, 7895, 7893, 0, 0, 7757, 557, 0, 0, + 7759, 555, 0, 0, 511, 476, 472, 470, 0, 0, 474, 7856, 7854, 0, 7860, + 7858, 0, 7857, 7855, 0, 7861, 7859, 0, 7700, 7702, 7701, 7703, 7760, + 7762, 7761, 7763, 7780, 0, 7781, 0, 7782, 0, 7783, 0, 0, 7800, 0, 7801, + 0, 7802, 0, 7803, 7835, 0, 7900, 7898, 0, 7904, 7902, 0, 0, 7906, 7901, + 7899, 0, 7905, 7903, 0, 0, 7907, 7914, 7912, 0, 7918, 7916, 0, 0, 7920, + 7915, 7913, 0, 7919, 7917, 0, 0, 7921, 0, 494, 492, 0, 493, 0, 480, 0, + 481, 0, 0, 7708, 0, 7709, 560, 0, 561, 0, 0, 495, 8122, 902, 8121, 8120, + 7944, 7945, 0, 8124, 8136, 904, 7960, 7961, 8138, 905, 7976, 7977, 0, + 8140, 8154, 906, 8153, 8152, 0, 938, 7992, 7993, 8184, 908, 8008, 8009, + 0, 8172, 8170, 910, 8169, 8168, 0, 939, 0, 8025, 8186, 911, 8040, 8041, + 0, 8188, 0, 8116, 0, 8132, 8048, 940, 8113, 8112, 7936, 7937, 8118, 8115, + 8050, 941, 7952, 7953, 8052, 942, 7968, 7969, 8134, 8131, 8054, 943, + 8145, 8144, 0, 970, 7984, 7985, 8150, 0, 8056, 972, 8000, 8001, 8164, + 8165, 8058, 973, 8161, 8160, 0, 971, 8016, 8017, 8166, 0, 8060, 974, + 8032, 8033, 8182, 8179, 8146, 912, 8151, 0, 8162, 944, 8167, 0, 0, 8180, + 0, 979, 0, 980, 0, 1031, 0, 1232, 0, 1234, 0, 1027, 1024, 0, 0, 1238, 0, + 1025, 0, 1217, 0, 1244, 0, 1246, 1037, 0, 1250, 1049, 0, 1252, 0, 1036, + 0, 1254, 1262, 1038, 0, 1264, 1266, 0, 0, 1268, 0, 1272, 0, 1260, 0, + 1233, 0, 1235, 0, 1107, 1104, 0, 0, 1239, 0, 1105, 0, 1218, 0, 1245, 0, + 1247, 1117, 0, 1251, 1081, 0, 1253, 0, 1116, 0, 1255, 1263, 1118, 0, + 1265, 1267, 0, 0, 1269, 0, 1273, 0, 1261, 0, 1111, 1142, 0, 1143, 0, 0, + 1242, 0, 1243, 0, 1258, 0, 1259, 1570, 1571, 1573, 0, 0, 1572, 0, 1574, + 0, 1730, 0, 1747, 0, 1728, 0, 2345, 0, 2353, 0, 2356, 2507, 2508, 2891, + 2888, 2892, 0, 2964, 0, 0, 3018, 3020, 0, 0, 3019, 0, 3144, 0, 3264, + 3274, 3271, 3272, 0, 0, 3275, 0, 3402, 3404, 0, 0, 3403, 0, 3546, 3548, + 3550, 0, 3549, 4134, 0, 0, 6918, 0, 6920, 0, 6922, 0, 6924, 0, 6926, 0, + 6930, 0, 6971, 0, 6973, 0, 6976, 0, 6977, 0, 6979, 7736, 0, 7737, 0, + 7772, 0, 7773, 0, 7784, 0, 7785, 0, 7852, 0, 0, 7862, 7853, 0, 0, 7863, + 7878, 0, 7879, 0, 7896, 0, 7897, 0, 7938, 7940, 7942, 8064, 7939, 7941, + 7943, 8065, 0, 8066, 0, 8067, 0, 8068, 0, 8069, 0, 8070, 0, 8071, 7946, + 7948, 7950, 8072, 7947, 7949, 7951, 8073, 0, 8074, 0, 8075, 0, 8076, 0, + 8077, 0, 8078, 0, 8079, 7954, 7956, 7955, 7957, 7962, 7964, 7963, 7965, + 7970, 7972, 7974, 8080, 7971, 7973, 7975, 8081, 0, 8082, 0, 8083, 0, + 8084, 0, 8085, 0, 8086, 0, 8087, 7978, 7980, 7982, 8088, 7979, 7981, + 7983, 8089, 0, 8090, 0, 8091, 0, 8092, 0, 8093, 0, 8094, 0, 8095, 7986, + 7988, 7990, 0, 7987, 7989, 7991, 0, 7994, 7996, 7998, 0, 7995, 7997, + 7999, 0, 8002, 8004, 8003, 8005, 8010, 8012, 8011, 8013, 8018, 8020, + 8022, 0, 8019, 8021, 8023, 0, 8027, 8029, 8031, 0, 8034, 8036, 8038, + 8096, 8035, 8037, 8039, 8097, 0, 8098, 0, 8099, 0, 8100, 0, 8101, 0, + 8102, 0, 8103, 8042, 8044, 8046, 8104, 8043, 8045, 8047, 8105, 0, 8106, + 0, 8107, 0, 8108, 0, 8109, 0, 8110, 0, 8111, 0, 8114, 0, 8130, 0, 8178, + 0, 8119, 8141, 8142, 8143, 0, 0, 8135, 0, 8183, 8157, 8158, 8159, 0, 0, + 8602, 0, 8603, 0, 8622, 0, 8653, 0, 8655, 0, 8654, 0, 8708, 0, 8713, 0, + 8716, 0, 8740, 0, 8742, 0, 8769, 0, 8772, 0, 8775, 0, 8777, 0, 8813, 0, + 8802, 0, 8816, 0, 8817, 0, 8820, 0, 8821, 0, 8824, 0, 8825, 0, 8832, 0, + 8833, 0, 8928, 0, 8929, 0, 8836, 0, 8837, 0, 8840, 0, 8841, 0, 8930, 0, + 8931, 0, 8876, 0, 8877, 0, 8878, 0, 8879, 0, 8938, 0, 8939, 0, 8940, 0, + 8941, 12436, 0, 12364, 0, 12366, 0, 12368, 0, 12370, 0, 12372, 0, 12374, + 0, 12376, 0, 12378, 0, 12380, 0, 12382, 0, 12384, 0, 12386, 0, 12389, 0, + 12391, 0, 12393, 0, 12400, 12401, 12403, 12404, 12406, 12407, 12409, + 12410, 12412, 12413, 12446, 0, 12532, 0, 12460, 0, 12462, 0, 12464, 0, + 12466, 0, 12468, 0, 12470, 0, 12472, 0, 12474, 0, 12476, 0, 12478, 0, + 12480, 0, 12482, 0, 12485, 0, 12487, 0, 12489, 0, 12496, 12497, 12499, + 12500, 12502, 12503, 12505, 12506, 12508, 12509, 12535, 0, 12536, 0, + 12537, 0, 12538, 0, 12542, 0, 69786, 0, 69788, 0, 69803, 0, 0, 69934, 0, + 69935, 70475, 70476, 70844, 70843, 70846, 0, 0, 71098, 0, 71099, +}; + diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh index fd1fd44..a2c59da 100644 --- a/src/hb-unicode-private.hh +++ b/src/hb-unicode-private.hh @@ -1,7 +1,7 @@ /* * Copyright © 2009 Red Hat, Inc. * Copyright © 2011 Codethink Limited - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -32,11 +32,10 @@ #define HB_UNICODE_PRIVATE_HH #include "hb-private.hh" - -#include "hb-unicode.h" #include "hb-object-private.hh" +extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; /* * hb_unicode_funcs_t @@ -50,18 +49,19 @@ 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 (unsigned int, combining_class) \ + 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 { +struct hb_unicode_funcs_t { hb_object_header_t header; ASSERT_POD (); @@ -69,7 +69,136 @@ struct _hb_unicode_funcs_t { bool immutable; - /* Don't access these directly. Call hb_unicode_*() instead. */ +#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; + + 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; + } + } + } + struct { #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; @@ -91,69 +220,98 @@ struct _hb_unicode_funcs_t { }; -#ifdef HAVE_GLIB -extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs; -#define _hb_unicode_funcs_default _hb_glib_unicode_funcs -#elif defined(HAVE_ICU) -extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs; -#define _hb_unicode_funcs_default _hb_icu_unicode_funcs -#else -#define HB_UNICODE_FUNCS_NIL 1 extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil; -#define _hb_unicode_funcs_default _hb_unicode_funcs_nil -#endif - -HB_INTERNAL unsigned int -_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs, - hb_codepoint_t unicode); -static inline hb_bool_t -_hb_unicode_is_variation_selector (hb_codepoint_t unicode) -{ - return unlikely ((unicode >= 0x180B && unicode <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */ - (unicode >= 0xFE00 && unicode <= 0xFE0F) || /* VARIATION SELECTOR-1..16 */ - (unicode >= 0xE0100 && unicode <= 0xE01EF)); /* VARIATION SELECTOR-17..256 */ -} +/* Modified combining marks */ -/* Zero-Width invisible characters: +/* Hebrew * - * 00AD SOFT HYPHEN - * 034F COMBINING GRAPHEME JOINER + * We permute the "fixed-position" classes 10-26 into the order + * described in the SBL Hebrew manual: * - * 200B ZERO WIDTH SPACE - * 200C ZERO WIDTH NON-JOINER - * 200D ZERO WIDTH JOINER - * 200E LEFT-TO-RIGHT MARK - * 200F RIGHT-TO-LEFT MARK + * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf * - * 2028 LINE SEPARATOR + * (as recommended by: + * http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html) * - * 202A LEFT-TO-RIGHT EMBEDDING - * 202B RIGHT-TO-LEFT EMBEDDING - * 202C POP DIRECTIONAL FORMATTING - * 202D LEFT-TO-RIGHT OVERRIDE - * 202E RIGHT-TO-LEFT OVERRIDE + * 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 * - * 2060 WORD JOINER - * 2061 FUNCTION APPLICATION - * 2062 INVISIBLE TIMES - * 2063 INVISIBLE SEPARATOR + * 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 * - * FEFF ZERO WIDTH NO-BREAK SPACE + * 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. */ -static inline hb_bool_t -_hb_unicode_is_zero_width (hb_codepoint_t ch) -{ - return ((ch & ~0x007F) == 0x2000 && ( - (ch >= 0x200B && ch <= 0x200F) || - (ch >= 0x202A && ch <= 0x202E) || - (ch >= 0x2060 && ch <= 0x2063) || - (ch == 0x2028) - )) || unlikely (ch == 0x0009 - || ch == 0x00AD - || ch == 0x034F - || ch == 0xFEFF); -} +#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 */ +#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */ +#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */ +#define HB_MODIFIED_COMBINING_CLASS_CCC132 132 /* sign u */ + + +/* Misc */ + +#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \ + (FLAG (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + #endif /* HB_UNICODE_PRIVATE_HH */ diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc index 6a21ef3..fc19006 100644 --- a/src/hb-unicode.cc +++ b/src/hb-unicode.cc @@ -1,7 +1,7 @@ /* * Copyright © 2009 Red Hat, Inc. - * Copyright © 2011 Codethink Limited - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2010,2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -38,12 +38,12 @@ * hb_unicode_funcs_t */ -static unsigned int +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 0; + return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; } static unsigned int @@ -99,13 +99,72 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, } +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) { - return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default); +#define HB_UNICODE_FUNCS_IMPLEMENT(set) \ + return hb_##set##_get_unicode_funcs (); + +#ifdef HAVE_GLIB + HB_UNICODE_FUNCS_IMPLEMENT(glib) +#elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) + HB_UNICODE_FUNCS_IMPLEMENT(icu) +#elif defined(HAVE_UCDN) + HB_UNICODE_FUNCS_IMPLEMENT(ucdn) +#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) +#ifdef _MSC_VER +#pragma message("Could not find any Unicode functions implementation, you have to provide your own") +#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS") +#else +#warning "Could not find any Unicode functions implementation, you have to provide your own" +#warning "To suppress this warning, define HB_NO_UNICODE_FUNCS" +#endif +#endif + +/** + * hb_unicode_funcs_create: (Xconstructor) + * @parent: (nullable): + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ hb_unicode_funcs_t * hb_unicode_funcs_create (hb_unicode_funcs_t *parent) { @@ -131,7 +190,6 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent) } -//extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil; const hb_unicode_funcs_t _hb_unicode_funcs_nil = { HB_OBJECT_HEADER_STATIC, @@ -144,18 +202,45 @@ const hb_unicode_funcs_t _hb_unicode_funcs_nil = { } }; +/** + * hb_unicode_funcs_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 1.0 + **/ 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: 1.0 + **/ 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: 1.0 + **/ void hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) { @@ -171,6 +256,20 @@ hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) free (ufuncs); } +/** + * hb_unicode_funcs_set_user_data: (skip) + * @ufuncs: Unicode functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, hb_user_data_key_t *key, @@ -181,6 +280,17 @@ hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, 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: 1.0 + **/ void * hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, hb_user_data_key_t *key) @@ -189,21 +299,49 @@ hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, } +/** + * hb_unicode_funcs_make_immutable: + * @ufuncs: Unicode functions. + * + * + * + * Since: 1.0 + **/ void hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs) { - if (hb_object_is_inert (ufuncs)) + if (unlikely (hb_object_is_inert (ufuncs))) return; ufuncs->immutable = true; } +/** + * hb_unicode_funcs_is_immutable: + * @ufuncs: Unicode functions. + * + * + * + * Return value: + * + * Since: 1.0 + **/ 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: 1.0 + **/ hb_unicode_funcs_t * hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs) { @@ -236,7 +374,7 @@ hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t *ufuncs, \ } \ } - HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS #undef HB_UNICODE_FUNC_IMPLEMENT @@ -246,83 +384,185 @@ return_type \ hb_unicode_##name (hb_unicode_funcs_t *ufuncs, \ hb_codepoint_t unicode) \ { \ - return ufuncs->func.name (ufuncs, unicode, ufuncs->user_data.name); \ + return ufuncs->name (unicode); \ } - HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE #undef HB_UNICODE_FUNC_IMPLEMENT +/** + * hb_unicode_compose: + * @ufuncs: Unicode functions. + * @a: + * @b: + * @ab: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_unicode_compose (hb_unicode_funcs_t *ufuncs, hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) { - *ab = 0; - return ufuncs->func.compose (ufuncs, a, b, ab, ufuncs->user_data.compose); + return ufuncs->compose (a, b, ab); } +/** + * hb_unicode_decompose: + * @ufuncs: Unicode functions. + * @ab: + * @a: (out): + * @b: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) { - *a = ab; *b = 0; - return ufuncs->func.decompose (ufuncs, ab, a, b, ufuncs->user_data.decompose); + return ufuncs->decompose (ab, a, b); } - - +/** + * hb_unicode_decompose_compatibility: + * @ufuncs: Unicode functions. + * @u: + * @decomposed: (out): + * + * + * + * Return value: + * + * Since: 1.0 + **/ unsigned int -_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs, - hb_codepoint_t unicode) +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed) { - int c = hb_unicode_combining_class (ufuncs, unicode); - - if (unlikely (hb_in_range<int> (c, 27, 33))) - { - /* Modify the combining-class to suit Arabic better. See: - * http://unicode.org/faq/normalization.html#8 - * http://unicode.org/faq/normalization.html#9 - */ - c = c == 33 ? 27 : c + 1; - } - else if (unlikely (hb_in_range<int> (c, 10, 25))) - { - /* The equivalent fix for Hebrew is more complex. - * - * We permute the "fixed-position" classes 10-25 into the order - * described in the SBL Hebrew manual: - * - * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf - * - * (as recommended by: - * http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html) - * - * More details here: - * https://bugzilla.mozilla.org/show_bug.cgi?id=662055 - */ - static const int permuted_hebrew_classes[25 - 10 + 1] = { - /* 10 sheva */ 22, - /* 11 hataf segol */ 15, - /* 12 hataf patah */ 16, - /* 13 hataf qamats */ 17, - /* 14 hiriq */ 23, - /* 15 tsere */ 18, - /* 16 segol */ 19, - /* 17 patah */ 20, - /* 18 qamats */ 21, - /* 19 holam */ 14, - /* 20 qubuts */ 24, - /* 21 dagesh */ 12, - /* 22 meteg */ 25, - /* 23 rafe */ 13, - /* 24 shin dot */ 10, - /* 25 sin dot */ 11, - }; - c = permuted_hebrew_classes[c - 10]; - } - - return c; + 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/src/hb-unicode.h b/src/hb-unicode.h index b26168f..1c4e097 100644 --- a/src/hb-unicode.h +++ b/src/hb-unicode.h @@ -1,7 +1,7 @@ /* * Copyright © 2009 Red Hat, Inc. * Copyright © 2011 Codethink Limited - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -40,11 +40,135 @@ 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; +typedef struct hb_unicode_funcs_t hb_unicode_funcs_t; /* @@ -71,7 +195,8 @@ 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_bool_t replace); + void * hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, @@ -94,7 +219,7 @@ hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs); /* typedefs */ -typedef unsigned int (*hb_unicode_combining_class_func_t) (hb_unicode_funcs_t *ufuncs, +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, @@ -121,47 +246,165 @@ typedef hb_bool_t (*hb_unicode_decompose_func_t) (hb_unicode_funcs_t *ufuncs, 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: 1.0 + **/ void hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_combining_class_func_t combining_class_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_eastasian_width_func_t eastasian_width_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_general_category_func_t general_category_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_mirroring_func_t mirroring_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_script_func_t script_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_compose_func_t compose_func, + 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: 1.0 + **/ void hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs, - hb_unicode_decompose_func_t decompose_func, + 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: 1.0 + **/ +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 */ -unsigned int +hb_unicode_combining_class_t hb_unicode_combining_class (hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode); @@ -192,6 +435,11 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, hb_codepoint_t *a, hb_codepoint_t *b); +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/src/hb-uniscribe.cc b/src/hb-uniscribe.cc index 9f84a3c..e7bcad2 100644 --- a/src/hb-uniscribe.cc +++ b/src/hb-uniscribe.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012,2013 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,211 +24,703 @@ * Google Author(s): Behdad Esfahbod */ -#define _WIN32_WINNT 0x0500 - -#include "hb-private.hh" +#define HB_SHAPER uniscribe +#include "hb-shaper-impl-private.hh" #include <windows.h> #include <usp10.h> - -typedef ULONG WIN_ULONG; +#include <rpc.h> #include "hb-uniscribe.h" +#include "hb-open-file-private.hh" #include "hb-ot-name-table.hh" #include "hb-ot-tag.h" -#include "hb-font-private.hh" -#include "hb-buffer-private.hh" - - #ifndef HB_DEBUG_UNISCRIBE #define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) #endif -/* -DWORD GetFontData( - __in HDC hdc, - __in DWORD dwTable, - __in DWORD dwOffset, - __out LPVOID lpvBuffer, - __in DWORD cbData +static 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 ); -*/ -static bool -populate_log_font (LOGFONTW *lf, - HDC hdc, - hb_font_t *font) +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 +) { - memset (lf, 0, sizeof (*lf)); - int dpi = GetDeviceCaps (hdc, LOGPIXELSY); - lf->lfHeight = -font->y_scale; +{ + return ScriptItemize (pwcInChars, + cInChars, + cMaxItems, + psControl, + psState, + pItems, + pcItems); +} +} - hb_blob_t *blob = Sanitizer<name>::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e'))); - const name *name_table = Sanitizer<name>::lock_instance (blob); - unsigned int len = name_table->get_name (3, 1, 0x409, 4, - lf->lfFaceName, - sizeof (lf->lfFaceName[0]) * LF_FACESIZE) - / sizeof (lf->lfFaceName[0]); - hb_blob_destroy (blob); +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); +} - if (unlikely (!len)) { - DEBUG_MSG (UNISCRIBE, NULL, "Didn't find English name table entry"); - return false; - } - if (unlikely (len >= LF_FACESIZE)) { - DEBUG_MSG (UNISCRIBE, NULL, "Font name too long"); - return false; +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; - for (unsigned int i = 0; i < len; i++) - lf->lfFaceName[i] = hb_be_uint16 (lf->lfFaceName[i]); - lf->lfFaceName[len] = 0; +static inline void +free_uniscribe_funcs (void) +{ + free (uniscribe_funcs); +} - return true; +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; } -static hb_user_data_key_t hb_uniscribe_data_key; +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 struct hb_uniscribe_face_data_t { + 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_face_data_nil = {0}; + 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_uniscribe_face_data_destroy (hb_uniscribe_face_data_t *data) +_hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen) { - if (data->fh) - RemoveFontMemResourceEx (data->fh); - free (data); + /* 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; } -static hb_uniscribe_face_data_t * -_hb_uniscribe_face_get_data (hb_face_t *face) +/* Destroys blob. */ +static hb_blob_t * +_hb_rename_font (hb_blob_t *blob, wchar_t *new_name) { - hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key); - if (likely (data)) return data; + /* 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); - data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t)); + _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 &_hb_uniscribe_face_data_nil; + 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); - unsigned int blob_length; - const char *blob_data = hb_blob_get_data (blob, &blob_length); - if (unlikely (!blob_length)) + 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 *) blob_data, blob_length, 0, &num_fonts_installed); - hb_blob_destroy (blob); + 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"); - - - if (unlikely (!hb_face_set_user_data (face, &hb_uniscribe_data_key, data, - (hb_destroy_func_t) _hb_uniscribe_face_data_destroy, - false))) { - _hb_uniscribe_face_data_destroy (data); - data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key); - if (data) - return data; - else - return &_hb_uniscribe_face_data_nil; + 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); +} + -static struct hb_uniscribe_font_data_t { +/* + * shaper font data + */ + +struct hb_uniscribe_shaper_font_data_t { HDC hdc; LOGFONTW log_font; HFONT hfont; SCRIPT_CACHE script_cache; -} _hb_uniscribe_font_data_nil = {NULL, NULL, NULL}; +}; -static void -_hb_uniscribe_font_data_destroy (hb_uniscribe_font_data_t *data) +static bool +populate_log_font (LOGFONTW *lf, + hb_font_t *font) { - if (data->hdc) - ReleaseDC (NULL, data->hdc); - if (data->hfont) - DeleteObject (data->hfont); - if (data->script_cache) - ScriptFreeCache (&data->script_cache); - free (data); + memset (lf, 0, sizeof (*lf)); + lf->lfHeight = -font->y_scale; + 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; } -static hb_uniscribe_font_data_t * -_hb_uniscribe_font_get_data (hb_font_t *font) +hb_uniscribe_shaper_font_data_t * +_hb_uniscribe_shaper_font_data_create (hb_font_t *font) { - hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key); - if (likely (data)) return data; + if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return NULL; - data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t)); + 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 &_hb_uniscribe_font_data_nil; + return NULL; data->hdc = GetDC (NULL); - if (unlikely (!populate_log_font (&data->log_font, data->hdc, font))) + if (unlikely (!populate_log_font (&data->log_font, font))) { DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); - else { - data->hfont = CreateFontIndirectW (&data->log_font); - if (unlikely (!data->hfont)) - DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); - if (!SelectObject (data->hdc, data->hfont)) - DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return NULL; } - if (unlikely (!hb_font_set_user_data (font, &hb_uniscribe_data_key, data, - (hb_destroy_func_t) _hb_uniscribe_font_data_destroy, - false))) - { - _hb_uniscribe_font_data_destroy (data); - data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key); - if (data) - return data; - else - return &_hb_uniscribe_font_data_nil; + 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) { - hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); - if (unlikely (!font_data)) - return NULL; + 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) { - hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); - if (unlikely (!font_data)) - return 0; + 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) +{ + 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_font_t *font, +_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) { - buffer->guess_properties (); + 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 { \ @@ -236,58 +728,66 @@ _hb_uniscribe_shape (hb_font_t *font, return false; \ } HB_STMT_END; - hb_uniscribe_face_data_t *face_data = _hb_uniscribe_face_get_data (font->face); - if (unlikely (!face_data->fh)) - FAIL ("Couldn't get face data"); - - hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); - if (unlikely (!font_data->hfont)) - FAIL ("Couldn't get font font"); - - if (unlikely (!buffer->len)) - return true; - HRESULT hr; retry: unsigned int scratch_size; - char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size); - - /* Allocate char buffers; they all fit */ + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); #define ALLOCATE_ARRAY(Type, name, len) \ Type *name = (Type *) scratch; \ - scratch += len * sizeof (name[0]); \ - scratch_size -= len * sizeof (name[0]); + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } #define utf16_index() var1.u32 - WCHAR *pchars = (WCHAR *) scratch; + ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2); + unsigned int chars_len = 0; - for (unsigned int i = 0; i < buffer->len; i++) { + for (unsigned int i = 0; i < buffer->len; i++) + { hb_codepoint_t c = buffer->info[i].codepoint; buffer->info[i].utf16_index() = chars_len; - if (likely (c < 0x10000)) + if (likely (c <= 0xFFFFu)) pchars[chars_len++] = c; - else if (unlikely (c >= 0x110000)) - pchars[chars_len++] = 0xFFFD; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; else { - pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10); - pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1)); + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); } } - ALLOCATE_ARRAY (WCHAR, wchars, chars_len); ALLOCATE_ARRAY (WORD, log_clusters, chars_len); ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); - /* On Windows, we don't care about alignment...*/ - unsigned int glyphs_size = scratch_size / (sizeof (WORD) + - sizeof (SCRIPT_GLYPHPROP) + - sizeof (int) + - sizeof (GOFFSET) + - sizeof (uint32_t)); + 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); @@ -295,13 +795,22 @@ retry: 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 10 +#define MAX_ITEMS 256 SCRIPT_ITEM items[MAX_ITEMS + 1]; SCRIPT_CONTROL bidi_control = {0}; SCRIPT_STATE bidi_state = {0}; - WIN_ULONG script_tags[MAX_ITEMS]; + ULONG script_tags[MAX_ITEMS]; int item_count; /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ @@ -309,96 +818,147 @@ retry: *(uint32_t*)&bidi_control |= 1<<24; bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; -// bidi_state.fOverrideDirection = 1; - - hr = ScriptItemizeOpenType (wchars, - chars_len, - MAX_ITEMS, - &bidi_control, - &bidi_state, - items, - script_tags, - &item_count); + 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 - int *range_char_counts = NULL; - TEXTRANGE_PROPERTIES **range_properties = NULL; - int range_count = 0; - if (num_features) { - /* TODO setup ranges */ - } - - OPENTYPE_TAG language_tag = hb_ot_tag_from_language (buffer->props.language); + 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; - OPENTYPE_TAG script_tag = script_tags[i]; /* XXX buffer->props.script */ - - hr = ScriptShapeOpenType (font_data->hdc, - &font_data->script_cache, - &items[i].a, - script_tag, - language_tag, - range_char_counts, - range_properties, - range_count, - wchars + chars_offset, - item_chars_len, - glyphs_size - glyphs_offset, - /* out */ - log_clusters + chars_offset, - char_props + chars_offset, - glyphs + glyphs_offset, - glyph_props + glyphs_offset, - (int *) &glyphs_len); - - for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) - log_clusters[j] += glyphs_offset; - - if (unlikely (items[i].a.fNoGlyphIndex)) - FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); - if (unlikely (hr == E_OUTOFMEMORY)) + 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++) { - buffer->ensure (buffer->allocated * 2); - if (buffer->in_error) - FAIL ("Buffer resize failed"); - goto retry; + 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; } - if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) + } + + /* 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)) + { + 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"); - if (unlikely (FAILED (hr))) - FAIL ("ScriptShapeOpenType() failed: 0x%08xL", hr); - - hr = ScriptPlaceOpenType (font_data->hdc, - &font_data->script_cache, - &items[i].a, - script_tag, - language_tag, - range_char_counts, - range_properties, - range_count, - wchars + chars_offset, - log_clusters + chars_offset, - char_props + chars_offset, - item_chars_len, - glyphs + glyphs_offset, - glyph_props + glyphs_offset, - glyphs_len, - /* out */ - advances + glyphs_offset, - offsets + glyphs_offset, - NULL); - if (unlikely (FAILED (hr))) - FAIL ("ScriptPlaceOpenType() failed: 0x%08xL", hr); - - glyphs_offset += glyphs_len; + 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; @@ -412,20 +972,13 @@ retry: uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; *p = MIN (*p, buffer->info[i].cluster); } - if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { - for (unsigned int i = 1; i < glyphs_len; i++) - if (!glyph_props[i].sva.fClusterStart) - vis_clusters[i] = vis_clusters[i - 1]; - } else { - for (int i = glyphs_len - 2; i >= 0; i--) - if (!glyph_props[i].sva.fClusterStart) - vis_clusters[i] = vis_clusters[i + 1]; - } + for (unsigned int i = 1; i < glyphs_len; i++) + if (vis_clusters[i] == -1) + vis_clusters[i] = vis_clusters[i - 1]; #undef utf16_index - buffer->ensure (glyphs_len); - if (buffer->in_error) + if (unlikely (!buffer->ensure (glyphs_len))) FAIL ("Buffer in error"); #undef FAIL @@ -454,10 +1007,13 @@ retry: /* TODO vertical */ pos->x_advance = info->mask; - pos->x_offset = info->var1.u32; + pos->x_offset = backward ? -info->var1.u32 : info->var1.u32; pos->y_offset = info->var2.u32; } + if (backward) + hb_buffer_reverse (buffer); + /* Wow, done! */ return true; } diff --git a/src/hb-uniscribe.h b/src/hb-uniscribe.h index 216610e..001ab38 100644 --- a/src/hb-uniscribe.h +++ b/src/hb-uniscribe.h @@ -29,7 +29,6 @@ #include "hb.h" -#define _WIN32_WINNT 0x0500 #include <windows.h> HB_BEGIN_DECLS diff --git a/src/hb-utf-private.hh b/src/hb-utf-private.hh new file mode 100644 index 0000000..14d3c2e --- /dev/null +++ b/src/hb-utf-private.hh @@ -0,0 +1,278 @@ +/* + * 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 (hb_in_range (c, 0xD800u, 0xDBFFu))) + { + /* High-surrogate in c */ + hb_codepoint_t l; + if (text < end && ((l = *text), 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) + { + const uint16_t *end = text--; + hb_codepoint_t c = *text; + + if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (start < text && hb_in_range (c, 0xDC00u, 0xDFFFu))) + text--; + + if (likely (next (text, end, unicode, replacement) == end)) + return text; + + *unicode = replacement; + return end - 1; + } + + + 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 = *text++; + if (validate && unlikely (c > 0x10FFFFu || hb_in_range (c, 0xD800u, 0xDFFFu))) + goto error; + *unicode = c; + return text; + + error: + *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) + { + next (text - 1, text, unicode, replacement); + return text - 1; + } + + 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/src/hb-version.h.in b/src/hb-version.h.in index 43634f9..2517160 100644 --- a/src/hb-version.h.in +++ b/src/hb-version.h.in @@ -42,8 +42,8 @@ HB_BEGIN_DECLS #define HB_VERSION_STRING "@HB_VERSION@" -#define HB_VERSION_CHECK(major,minor,micro) \ - ((major)*10000+(minor)*100+(micro) >= \ +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) @@ -56,9 +56,9 @@ const char * hb_version_string (void); hb_bool_t -hb_version_check (unsigned int major, - unsigned int minor, - unsigned int micro); +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); HB_END_DECLS diff --git a/src/hb-warning.cc b/src/hb-warning.cc index 4f1f65f..e0f88e2 100644 --- a/src/hb-warning.cc +++ b/src/hb-warning.cc @@ -53,14 +53,3 @@ #endif -#include "hb-unicode-private.hh" - -#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) -#ifdef _MSC_VER -#pragma message("Could not find any Unicode functions implementation, you have to provide your own") -#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS") -#else -#warning "Could not find any Unicode functions implementation, you have to provide your own" -#warning "To suppress this warning, define HB_NO_UNICODE_FUNCS" -#endif -#endif @@ -31,9 +31,12 @@ #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" diff --git a/src/main.cc b/src/main.cc index 07d3d69..f9708cc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -36,6 +36,8 @@ #include <stdio.h> +using namespace OT; + int main (int argc, char **argv) @@ -129,8 +131,11 @@ main (int argc, char **argv) else printf (" Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, (const char *)script.get_lang_sys_tag (n_langsys)); - if (langsys.get_required_feature_index () == Index::NOT_FOUND_INDEX) + 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); @@ -145,11 +150,10 @@ main (int argc, char **argv) printf (" %d feature(s) found in table\n", num_features); for (int n_feature = 0; n_feature < num_features; n_feature++) { const Feature &feature = g.get_feature (n_feature); - printf (" Feature %2d of %2d: %.4s; %d lookup(s)\n", n_feature, num_features, - (const char *)g.get_feature_tag(n_feature), - feature.get_lookup_count()); - int num_lookups = feature.get_lookup_count (); + printf (" 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, @@ -168,7 +172,7 @@ main (int argc, char **argv) } break; - case GDEF::Tag: + case GDEF::tableTag: { const GDEF &gdef = *CastP<GDEF> (font_data + table.offset); diff --git a/src/sample.py b/src/sample.py new file mode 100755 index 0000000..f8d2216 --- /dev/null +++ b/src/sample.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import print_function +import sys +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 () +hb.buffer_add_utf8 (buf, text.encode('utf-8'), 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/src/test-buffer-serialize.cc b/src/test-buffer-serialize.cc new file mode 100644 index 0000000..18c46e9 --- /dev/null +++ b/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/src/test-size-params.cc b/src/test-size-params.cc new file mode 100644 index 0000000..35d9e3c --- /dev/null +++ b/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/src/test-would-substitute.cc b/src/test-would-substitute.cc new file mode 100644 index 0000000..8ea87cd --- /dev/null +++ b/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/src/test.cc b/src/test.cc new file mode 100644 index 0000000..a8fe046 --- /dev/null +++ b/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->x_offset, + pos->x_advance, + pos->y_advance); + + } + + hb_buffer_destroy (buffer); + hb_font_destroy (font); + hb_face_destroy (face); + + return 0; +} + + |