From b08254dad51328fed58b3c0999a1eb7a25f53efc Mon Sep 17 00:00:00 2001 From: Ankush Dua Date: Wed, 27 Jun 2012 15:27:25 +0900 Subject: [PATCH] Upgrade to latest harfbuzz Change-Id: I9d121292a9dee634ebd0a542d1b290c898db09b7 --- Makefile.am | 2 +- TODO | 80 +- autogen.sh | 10 +- configure.ac | 104 +- debian/changelog | 8 + debian/control | 2 +- packaging/harfbuzz.spec | 2 +- src/Makefile.am | 135 ++- src/check-c-linkage-decls.sh | 6 +- src/check-header-guards.sh | 9 +- src/check-internal-symbols.sh | 24 +- src/check-libstdc++.sh | 24 +- src/gen-arabic-table.py | 237 +++- src/gen-indic-table.py | 35 +- src/hb-atomic-private.hh | 112 ++ src/hb-blob.cc | 70 +- src/hb-blob.h | 7 +- src/hb-buffer-private.hh | 26 +- src/hb-buffer.cc | 202 +++- src/hb-buffer.h | 28 +- src/hb-cache-private.hh | 72 ++ src/hb-common.cc | 148 ++- src/hb-common.h | 249 ++-- src/hb-fallback-shape-private.hh | 47 + src/hb-fallback-shape.cc | 61 + src/hb-font-private.hh | 16 +- src/hb-font.cc | 287 +++-- src/hb-font.h | 69 +- src/hb-ft.cc | 223 +++- src/hb-ft.h | 14 +- src/hb-glib.cc | 42 +- src/hb-glib.h | 1 + src/hb-gobject-enums.cc.tmpl | 74 ++ src/hb-gobject-structs.cc | 63 + src/hb-gobject.h | 69 ++ src/hb-graphite2-private.hh | 42 + src/hb-graphite2.cc | 350 ++++++ src/hb-graphite2.h | 40 + src/hb-icu.cc | 63 +- src/hb-icu.h | 1 + src/hb-mutex-private.hh | 106 +- src/hb-object-private.hh | 131 +- src/hb-open-file-private.hh | 25 +- src/hb-open-type-private.hh | 179 +-- src/{hb-ot-head-private.hh => hb-ot-head-table.hh} | 14 +- src/hb-ot-hhea-table.hh | 92 ++ src/hb-ot-hmtx-table.hh | 86 ++ src/hb-ot-layout-common-private.hh | 212 +++- ...-gdef-private.hh => hb-ot-layout-gdef-table.hh} | 60 +- ...-gpos-private.hh => hb-ot-layout-gpos-table.hh} | 512 ++++---- src/hb-ot-layout-gsub-private.hh | 947 --------------- src/hb-ot-layout-gsub-table.hh | 1253 ++++++++++++++++++++ src/hb-ot-layout-gsubgpos-private.hh | 827 +++++++++---- src/hb-ot-layout-private.hh | 15 +- src/hb-ot-layout.cc | 82 +- src/hb-ot-layout.h | 14 +- src/hb-ot-map-private.hh | 10 +- src/hb-ot-map.cc | 17 +- src/{hb-ot-maxp-private.hh => hb-ot-maxp-table.hh} | 17 +- src/hb-ot-name-table.hh | 128 ++ src/hb-ot-shape-complex-arabic-table.hh | 254 +++- src/hb-ot-shape-complex-arabic.cc | 95 +- src/hb-ot-shape-complex-indic-machine.hh | 293 +++++ src/hb-ot-shape-complex-indic-machine.rl | 60 +- src/hb-ot-shape-complex-indic-private.hh | 270 +++++ src/hb-ot-shape-complex-indic-table.hh | 184 +-- src/hb-ot-shape-complex-indic.cc | 928 ++++++++------- src/hb-ot-shape-complex-misc.cc | 152 ++- src/hb-ot-shape-complex-private.hh | 207 +++- src/hb-ot-shape-normalize-private.hh | 46 + src/hb-ot-shape-normalize.cc | 165 +-- src/hb-ot-shape-private.hh | 76 +- src/hb-ot-shape.cc | 165 ++- src/hb-ot-tag.cc | 18 +- src/hb-ot-tag.h | 6 +- src/hb-ot.h | 11 +- src/hb-private.hh | 291 +++-- src/hb-set-private.hh | 172 +++ src/hb-set.cc | 191 +++ src/hb-set.h | 132 +++ src/hb-shape.cc | 210 +++- src/hb-shape.h | 23 +- src/hb-tt-font.cc | 243 ++++ src/hb-unicode-private.hh | 64 +- src/hb-unicode.cc | 106 +- src/hb-unicode.h | 8 +- src/hb-uniscribe-private.hh | 42 + src/hb-uniscribe.cc | 465 ++++++++ src/{hb-ot-shape.h => hb-uniscribe.h} | 25 +- src/hb-version.h | 66 ++ src/hb-version.h.in | 4 + src/hb-view.cc | 570 --------- src/hb-warning.cc | 66 ++ src/hb.h | 3 + src/indic.cc | 46 + src/main.cc | 17 +- src/test.cc | 100 -- test/.valgrind-suppressions | 0 test/Makefile.am | 128 +- test/api/Makefile.am | 125 ++ test/{ => api}/hb-test.h | 3 +- test/{ => api}/test-blob.c | 0 test/{ => api}/test-buffer.c | 13 +- test/{ => api}/test-c.c | 16 +- test/{ => api}/test-common.c | 97 +- test/{ => api}/test-cplusplus.cc | 0 test/{ => api}/test-font.c | 0 test/{ => api}/test-object.c | 46 +- test/{ => api}/test-ot-tag.c | 27 +- test/{ => api}/test-shape-complex.c | 0 test/{ => api}/test-shape.c | 17 +- test/{ => api}/test-unicode.c | 6 +- test/{ => api}/test-version.c | 0 test/shaping/Makefile.am | 35 + test/shaping/Makefile.in | 406 +++++++ test/shaping/hb-diff | 10 + test/shaping/hb-diff-colorize | 7 + test/shaping/hb-diff-filter-failures | 5 + test/shaping/hb-diff-ngrams | 5 + test/shaping/hb-diff-stat | 5 + test/shaping/hb-manifest-read | 5 + test/shaping/hb-manifest-update | 5 + test/shaping/hb-unicode-decode | 5 + test/shaping/hb-unicode-encode | 5 + test/shaping/hb-unicode-prettyname | 6 + test/shaping/hb_test_tools.py | 511 ++++++++ util/Makefile.am | 74 ++ util/ansi-print.cc | 413 +++++++ util/ansi-print.hh | 39 + util/hb-ot-shape-closure.cc | 112 ++ util/hb-shape.cc | 97 ++ util/hb-view.cc | 37 + util/helper-cairo-ansi.cc | 102 ++ util/helper-cairo-ansi.hh | 39 + util/helper-cairo.cc | 453 +++++++ util/helper-cairo.hh | 81 ++ util/main-font-text.hh | 80 ++ util/options.cc | 829 +++++++++++++ util/options.hh | 368 ++++++ util/shape-consumer.hh | 82 ++ util/view-cairo.cc | 126 ++ util/view-cairo.hh | 99 ++ 142 files changed, 13708 insertions(+), 4366 deletions(-) create mode 100644 src/hb-atomic-private.hh create mode 100644 src/hb-cache-private.hh create mode 100644 src/hb-fallback-shape-private.hh create mode 100644 src/hb-fallback-shape.cc create mode 100644 src/hb-gobject-enums.cc.tmpl create mode 100644 src/hb-gobject-structs.cc create mode 100644 src/hb-gobject.h create mode 100644 src/hb-graphite2-private.hh create mode 100644 src/hb-graphite2.cc create mode 100644 src/hb-graphite2.h rename src/{hb-ot-head-private.hh => hb-ot-head-table.hh} (95%) create mode 100644 src/hb-ot-hhea-table.hh create mode 100644 src/hb-ot-hmtx-table.hh rename src/{hb-ot-layout-gdef-private.hh => hb-ot-layout-gdef-table.hh} (89%) rename src/{hb-ot-layout-gpos-private.hh => hb-ot-layout-gpos-table.hh} (73%) delete mode 100644 src/hb-ot-layout-gsub-private.hh create mode 100644 src/hb-ot-layout-gsub-table.hh rename src/{hb-ot-maxp-private.hh => hb-ot-maxp-table.hh} (84%) create mode 100644 src/hb-ot-name-table.hh create mode 100644 src/hb-ot-shape-complex-indic-machine.hh create mode 100644 src/hb-ot-shape-complex-indic-private.hh create mode 100644 src/hb-ot-shape-normalize-private.hh create mode 100644 src/hb-set-private.hh create mode 100644 src/hb-set.cc create mode 100644 src/hb-set.h create mode 100644 src/hb-tt-font.cc create mode 100644 src/hb-uniscribe-private.hh create mode 100644 src/hb-uniscribe.cc rename src/{hb-ot-shape.h => hb-uniscribe.h} (75%) create mode 100644 src/hb-version.h delete mode 100644 src/hb-view.cc create mode 100644 src/hb-warning.cc create mode 100644 src/indic.cc delete mode 100644 src/test.cc delete mode 100644 test/.valgrind-suppressions create mode 100644 test/api/Makefile.am rename test/{ => api}/hb-test.h (98%) rename test/{ => api}/test-blob.c (100%) rename test/{ => api}/test-buffer.c (98%) rename test/{ => api}/test-c.c (87%) rename test/{ => api}/test-common.c (70%) rename test/{ => api}/test-cplusplus.cc (100%) rename test/{ => api}/test-font.c (100%) rename test/{ => api}/test-object.c (93%) rename test/{ => api}/test-ot-tag.c (89%) rename test/{ => api}/test-shape-complex.c (100%) rename test/{ => api}/test-shape.c (92%) rename test/{ => api}/test-unicode.c (99%) rename test/{ => api}/test-version.c (100%) create mode 100644 test/shaping/Makefile.am create mode 100644 test/shaping/Makefile.in create mode 100755 test/shaping/hb-diff create mode 100755 test/shaping/hb-diff-colorize create mode 100755 test/shaping/hb-diff-filter-failures create mode 100755 test/shaping/hb-diff-ngrams create mode 100755 test/shaping/hb-diff-stat create mode 100755 test/shaping/hb-manifest-read create mode 100755 test/shaping/hb-manifest-update create mode 100755 test/shaping/hb-unicode-decode create mode 100755 test/shaping/hb-unicode-encode create mode 100755 test/shaping/hb-unicode-prettyname create mode 100644 test/shaping/hb_test_tools.py create mode 100644 util/Makefile.am create mode 100644 util/ansi-print.cc create mode 100644 util/ansi-print.hh create mode 100644 util/hb-ot-shape-closure.cc create mode 100644 util/hb-shape.cc create mode 100644 util/hb-view.cc create mode 100644 util/helper-cairo-ansi.cc create mode 100644 util/helper-cairo-ansi.hh create mode 100644 util/helper-cairo.cc create mode 100644 util/helper-cairo.hh create mode 100644 util/main-font-text.hh create mode 100644 util/options.cc create mode 100644 util/options.hh create mode 100644 util/shape-consumer.hh create mode 100644 util/view-cairo.cc create mode 100644 util/view-cairo.hh diff --git a/Makefile.am b/Makefile.am index bb68ede..a405dbf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ NULL = -SUBDIRS = src test +SUBDIRS = src util test pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = harfbuzz.pc diff --git a/TODO b/TODO index 04852c6..4a1ad19 100644 --- a/TODO +++ b/TODO @@ -1,33 +1,72 @@ General fixes: ============= -- Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS) +- Warn at compile time (and runtime with HB_DEBUG?) if no Unicode / font + funcs found / set. -- Do proper rounding when scaling from font space? +- In hb_shape(), assert if direction is INVALID. + +- Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS). + +- Do proper rounding when scaling from font space? May be a non-issue. - Misc features: * init/medi/fina/isol for non-cursive scripts - * vkna,hkna etc for kana, *jmo for hangul, etc + * vkna,hkna etc for kana, etc + +- Move non-native direction and normalization handling to the generic non-OT + layer, such that uniscribe and other backends can use. + +- Uniscribe backend needs to enforce one direction only, otherwise cluster + values can confuse the user. + +- GSUB ligation should call merge_clusters(). Also other places. + +- Convert NBSP into space glyph. + +- Synthetic GDEF. + +- Add Pango backend? + +- Add ICUlayout backend? + +- Add ICUlayout API? + +- Add Old HarfBuzz backend? + +- Add Old HarfBuzz API? API issues to fix before 1.0: ============================ +- Add default font_funcs / Unicode funcs API and to utils. + +- Add init_func to font_funcs. Adjust ft. + - Add pkg-config files for glue codes (harfbuzz-glib, etc) - Figure out how many .so objects, how to link, etc +- 'const' for getter APIs? (use mutable internally) + +- blob_from_file? + + +API additions +============= + +- Buffer (de)serialize API ala hb-shape? + +- Move feature parsing from util into the library + - Add hb-cairo glue - Add sanitize API (and a cached version, that saves result on blob user-data) -- hb_shape() currently does a bit more than hb_ot_shape(). Shouldn't. - - Add glib GBoxedType stuff and introspection - -API to add (maybe after 1.0): -============================ +- Add Uniscribe face / font get API - BCP 47 language handling / API (language_matches?) @@ -35,26 +74,23 @@ API to add (maybe after 1.0): - Add hb_font_create_linear()? -- Add hb_shape_plan()/hb_shape_execute() +- Add hb_shape_plan()/hb_shape_planned() - Add query API for aalt-like features? - SFNT api? get_num_faces? get_table_tags? (there's something in stash) -- Full matrix instead of scale? - - Add segmentation API -- Add hb-fribidi? +- Add hb-fribidi glue? -hb-view enhancements: -==================== +hb-view / hb-shape enhancements: +=============================== -- Add --format - Add --width, --height, --auto-size, --align, etc? -- Port to GOption, --help -- Add XML and JSON formats +- Add XML and JSON formats to hb-shape +- --features="init=medi=isol=fina=0" Tests to write: @@ -66,10 +102,18 @@ Tests to write: - Finish test-unicode.c, grep for TODO +- GObject, FreeType, etc + +- hb_set_t + +- hb_cache_t and relatives + Optimizations: ============= - Avoid allocating blob objects internally for for_data() faces? -- Add caching layer to hb-ft +- Add caching layer to hb-ft? + +- Cache feature-less shape plans internally on the face. diff --git a/autogen.sh b/autogen.sh index cdebe07..833a621 100755 --- a/autogen.sh +++ b/autogen.sh @@ -13,14 +13,20 @@ which ragel || { exit 1 } +echo -n "checking for pkg-config... " +which pkg-config || { + echo "*** No pkg-config found, please install it ***" + exit 1 +} + echo -n "checking for autoreconf... " which autoreconf || { echo "*** No autoreconf found, please install it ***" exit 1 } -echo "running autoreconf" -autoreconf --force --install || exit $? +echo "running autoreconf --force --install --verbose" +autoreconf --force --install --verbose || exit $? cd $olddir echo "running configure $@" diff --git a/configure.ac b/configure.ac index d2a2331..2fb058f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.64]) AC_INIT([harfbuzz], - [0.7.0], + [0.9.0], [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz], [harfbuzz], [http://harfbuzz.org/]) @@ -11,15 +11,15 @@ AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.11.1 gnu dist-bzip2 no-dist-gzip -Wall no-define]) AM_SILENT_RULES([yes]) +# Initialize libtool +LT_PREREQ([2.2]) +LT_INIT([disable-static]) + # Check for programs AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX -# Initialize libtool -LT_PREREQ([2.2]) -LT_INIT([disable-static]) - # Version m4_define(hb_version_triplet,m4_split(AC_PACKAGE_VERSION,[[.]])) m4_define(hb_version_major,m4_argn(1,hb_version_triplet)) @@ -49,9 +49,11 @@ m4_define([hb_libtool_current], HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age AC_SUBST(HB_LIBTOOL_VERSION_INFO) +dnl GTK_DOC_CHECK([1.15],[--flavour no-tmpl]) + # Functions and headers -AC_CHECK_FUNCS(mprotect sysconf getpagesize mmap) -AC_CHECK_HEADERS(unistd.h sys/mman.h) +AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize sched_yield mmap _setmode isatty) +AC_CHECK_HEADERS(unistd.h sys/mman.h sched.h io.h) # Compiler flags AC_CANONICAL_HOST @@ -73,13 +75,34 @@ fi dnl ========================================================================== +have_ot=true +if $have_ot; then + AC_DEFINE(HAVE_OT, 1, [Have native OpenType Layout backend]) +fi +AM_CONDITIONAL(HAVE_OT, $have_ot) + +dnl =========================================================================== + PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, have_glib=true, have_glib=false) -PKG_CHECK_MODULES(GTHREAD, gthread-2.0, , have_glib=false) if $have_glib; then AC_DEFINE(HAVE_GLIB, 1, [Have glib2 library]) fi AM_CONDITIONAL(HAVE_GLIB, $have_glib) +PKG_CHECK_MODULES(GTHREAD, gthread-2.0, have_gthread=true, have_gthread=false) +if $have_gthread; then + AC_DEFINE(HAVE_GTHREAD, 1, [Have gthread2 library]) +fi +AM_CONDITIONAL(HAVE_GTHREAD, $have_gthread) + +PKG_CHECK_MODULES(GOBJECT, gobject-2.0 glib-2.0 >= 2.16, have_gobject=true, have_gobject=false) +if $have_gobject; then + AC_DEFINE(HAVE_GOBJECT, 1, [Have gobject2 library]) + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + AC_SUBST(GLIB_MKENUMS) +fi +AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject) + dnl ========================================================================== PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, have_cairo=false) @@ -88,12 +111,6 @@ if $have_cairo; then fi AM_CONDITIONAL(HAVE_CAIRO, $have_cairo) -PKG_CHECK_MODULES(CAIRO_PNG, cairo-png, have_cairo_png=true, have_cairo_png=false) -if $have_cairo_png; then - AC_DEFINE(HAVE_CAIRO_PNG, 1, [Have cairo-png support in cairo graphics library]) -fi -AM_CONDITIONAL(HAVE_CAIRO_PNG, $have_cairo_png) - PKG_CHECK_MODULES(CAIRO_FT, cairo-ft, have_cairo_ft=true, have_cairo_ft=false) if $have_cairo_ft; then AC_DEFINE(HAVE_CAIRO_FT, 1, [Have cairo-ft support in cairo graphics library]) @@ -103,12 +120,21 @@ AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft) dnl ========================================================================== PKG_CHECK_MODULES(ICU, icu, have_icu=true, [ - AC_CHECK_PROG([have_icu], [icu-config], [true], [false]) + have_icu=true + AC_CHECK_HEADERS(unicode/uchar.h,, have_icu=false) + AC_MSG_CHECKING([for libicuuc]) + LIBS_old=$LIBS + LIBS="$LIBS -licuuc" + AC_TRY_LINK([#include ], + [u_getIntPropertyValue (0, (UProperty)0);], + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no);have_icu=false) + LIBS=$LIBS_old if $have_icu; then - icu_cflags=`icu-config --cppflags` - icu_libs=`icu-config --ldflags-libsonly` - AC_SUBST(ICU_CFLAGS, [$icu_cflags]) - AC_SUBST(ICU_LIBS, [$icu_libs]) + ICU_CFLAGS=-D_REENTRANT + ICU_LIBS="-licuuc" + AC_SUBST(ICU_CFLAGS) + AC_SUBST(ICU_LIBS) fi ]) if $have_icu; then @@ -118,7 +144,15 @@ AM_CONDITIONAL(HAVE_ICU, $have_icu) dnl ========================================================================== -PKG_CHECK_MODULES(FREETYPE, freetype2, have_freetype=true, have_freetype=false) +PKG_CHECK_MODULES(GRAPHITE2, graphite2, have_graphite=true, have_graphite=false) +if $have_graphite; then + AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite library]) +fi +AM_CONDITIONAL(HAVE_GRAPHITE2, $have_graphite) + +dnl ========================================================================== + +PKG_CHECK_MODULES(FREETYPE, freetype2 >= 2.3.8, have_freetype=true, have_freetype=false) if $have_freetype; then AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library]) _save_libs="$LIBS" @@ -133,12 +167,42 @@ AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype) dnl =========================================================================== +AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true, have_uniscribe=false) +if $have_uniscribe; then + UNISCRIBE_CFLAGS= + UNISCRIBE_LIBS="-lusp10 -lgdi32" + AC_SUBST(UNISCRIBE_CFLAGS) + AC_SUBST(UNISCRIBE_LIBS) + AC_DEFINE(HAVE_UNISCRIBE, 1, [Have Uniscribe backend]) +fi +AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe) + +dnl =========================================================================== + +AC_CACHE_CHECK([for Intel atomic primitives], hb_cv_have_intel_atomic_primitives, [ + hb_cv_have_intel_atomic_primitives=false + AC_TRY_LINK([], [ + void memory_barrier (void) { __sync_synchronize (); } + int atomic_add (int i) { return __sync_fetch_and_add (&i, 1); } + int atomic_cmpxchg (int *i, int *j, int *k) { return __sync_bool_compare_and_swap (&i, j, k); } + ], hb_cv_have_intel_atomic_primitives=true + ) +]) +if $hb_cv_have_intel_atomic_primitives; then + AC_DEFINE(HAVE_INTEL_ATOMIC_PRIMITIVES, 1, [Have Intel __sync_* atomic primitives]) +fi + +dnl =========================================================================== + AC_CONFIG_FILES([ Makefile harfbuzz.pc src/Makefile src/hb-version.h +util/Makefile test/Makefile +test/api/Makefile +test/shaping/Makefile ]) AC_OUTPUT diff --git a/debian/changelog b/debian/changelog index 34e1d09..f60ddf7 100755 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +harfbuzz (0.9.0-1slp2+1) unstable; urgency=low + + * Upgrade to latest harfbuzz + * Git: 165.213.180.234:slp/pkgs/h/harfbuzz + * Tag: harfbuzz_0.9.1-1slp2+1 + + -- Ankush Dua Wed, 27 June 2012 15:00:08 +0900 + harfbuzz (0.7.0-1slp2+1) unstable; urgency=low * Upgrade to latest harfbuzz diff --git a/debian/control b/debian/control index 6c61eff..065539e 100755 --- a/debian/control +++ b/debian/control @@ -1,7 +1,7 @@ Source: harfbuzz Section: libs Priority: optional -Maintainer: janani , Kesavardhana Gandla +Maintainer: janani , Ankush Dua Build-Depends: debhelper (>= 5),libfreetype6-dev, pkg-config, ragel Standards-Version: 3.7.2 diff --git a/packaging/harfbuzz.spec b/packaging/harfbuzz.spec index 076b4a4..cda2d79 100644 --- a/packaging/harfbuzz.spec +++ b/packaging/harfbuzz.spec @@ -1,6 +1,6 @@ Name: harfbuzz Summary: Hindi Reshaping Library -Version: 0.7.0 +Version: 0.9.0 Release: 1 Group: TO_BE/FILLED_IN License: TO BE FILLED IN diff --git a/src/Makefile.am b/src/Makefile.am index b812419..344cc57 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ # Process this file with automake to produce Makefile.in NULL = +BUILT_SOURCES = EXTRA_DIST = CLEANFILES = DISTCLEANFILES = @@ -14,22 +15,34 @@ lib_LTLIBRARIES = libharfbuzz.la HBCFLAGS = HBLIBS = HBSOURCES = \ + hb-atomic-private.hh \ hb-blob.cc \ hb-buffer-private.hh \ hb-buffer.cc \ + hb-cache-private.hh \ hb-common.cc \ + hb-fallback-shape-private.hh \ + hb-fallback-shape.cc \ hb-font-private.hh \ hb-font.cc \ hb-mutex-private.hh \ hb-object-private.hh \ hb-open-file-private.hh \ hb-open-type-private.hh \ - hb-ot-head-private.hh \ - hb-ot-maxp-private.hh \ + hb-ot-head-table.hh \ + hb-ot-hhea-table.hh \ + hb-ot-hmtx-table.hh \ + hb-ot-maxp-table.hh \ + hb-ot-name-table.hh \ + hb-ot-tag.cc \ hb-private.hh \ + hb-set-private.hh \ + hb-set.cc \ hb-shape.cc \ + hb-tt-font.cc \ hb-unicode-private.hh \ hb-unicode.cc \ + hb-warning.cc \ $(NULL) HBHEADERS = \ hb.h \ @@ -37,18 +50,20 @@ HBHEADERS = \ hb-buffer.h \ hb-common.h \ hb-font.h \ + hb-set.h \ hb-shape.h \ hb-unicode.h \ hb-version.h \ $(NULL) +if HAVE_OT HBSOURCES += \ hb-ot-layout.cc \ hb-ot-layout-common-private.hh \ - hb-ot-layout-gdef-private.hh \ - hb-ot-layout-gpos-private.hh \ + hb-ot-layout-gdef-table.hh \ + hb-ot-layout-gpos-table.hh \ hb-ot-layout-gsubgpos-private.hh \ - hb-ot-layout-gsub-private.hh \ + hb-ot-layout-gsub-table.hh \ hb-ot-layout-private.hh \ hb-ot-map.cc \ hb-ot-map-private.hh \ @@ -57,59 +72,75 @@ HBSOURCES += \ hb-ot-shape-complex-arabic-table.hh \ hb-ot-shape-complex-indic.cc \ hb-ot-shape-complex-indic-machine.hh \ + hb-ot-shape-complex-indic-private.hh \ hb-ot-shape-complex-indic-table.hh \ hb-ot-shape-complex-misc.cc \ hb-ot-shape-complex-private.hh \ + hb-ot-shape-normalize-private.hh \ hb-ot-shape-normalize.cc \ hb-ot-shape-private.hh \ - hb-ot-tag.cc \ $(NULL) HBHEADERS += \ hb-ot.h \ hb-ot-layout.h \ - hb-ot-shape.h \ hb-ot-tag.h \ $(NULL) - -MAINTAINERCLEANFILES += \ - $(srcdir)/hb-version.h \ - $(NULL) +endif if HAVE_GLIB HBCFLAGS += $(GLIB_CFLAGS) HBLIBS += $(GLIB_LIBS) -HBSOURCES += \ - hb-glib.cc \ - $(NULL) -HBHEADERS += \ - hb-glib.h \ - $(NULL) +HBSOURCES += hb-glib.cc +HBHEADERS += hb-glib.h +endif + +if HAVE_GOBJECT +HBCFLAGS += $(GOBJECT_CFLAGS) +HBLIBS += $(GOBJECT_LIBS) +HBSOURCES += hb-gobject-structs.cc +nodist_HBSOURCES = hb-gobject-enums.cc +HBHEADERS += hb-gobject.h +BUILT_SOURCES += hb-gobject-enums.cc +EXTRA_DIST += hb-gobject-enums.cc.tmpl +DISTCLEANFILES += hb-gobject-enums.cc + +hb-gobject-enums.cc: hb-gobject-enums.cc.tmpl $(HBHEADERS) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > "$@.tmp" && \ + mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false ) endif if HAVE_ICU HBCFLAGS += $(ICU_CFLAGS) HBLIBS += $(ICU_LIBS) -HBSOURCES += \ - hb-icu.cc \ - $(NULL) -HBHEADERS += \ - hb-icu.h \ - $(NULL) +HBSOURCES += hb-icu.cc +HBHEADERS += hb-icu.h endif if HAVE_FREETYPE HBCFLAGS += $(FREETYPE_CFLAGS) HBLIBS += $(FREETYPE_LIBS) -HBSOURCES += \ - hb-ft.cc \ - $(NULL) -HBHEADERS += \ - hb-ft.h \ - $(NULL) +HBSOURCES += hb-ft.cc +HBHEADERS += hb-ft.h endif -CXXLINK = $(LINK) +if HAVE_GRAPHITE2 +HBCFLAGS += $(GRAPHITE2_CFLAGS) +HBLIBS += $(GRAPHITE2_LIBS) +HBSOURCES += hb-graphite2.cc hb-graphite2-private.hh +HBHEADERS += hb-graphite2.h +endif + +if HAVE_UNISCRIBE +HBCFLAGS += $(UNISCRIBE_CFLAGS) +HBLIBS += $(UNISCRIBE_LIBS) +HBSOURCES += hb-uniscribe.cc hb-uniscribe-private.hh +HBHEADERS += hb-uniscribe.h +endif + +# Use a C linker, not C++; Don't link to libstdc++ +libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS) libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) +nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES) libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined libharfbuzz_la_LIBADD = $(HBLIBS) @@ -124,35 +155,37 @@ GENERATORS = \ EXTRA_DIST += $(GENERATORS) -BUILT_SOURCES = hb-ot-shape-complex-indic-machine.hh +unicode-tables: arabic-table indic-table + +indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.hh.tmp && \ + mv hb-ot-shape-complex-indic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-indic-table.hh || \ + ($(RM) hb-ot-shape-complex-indic-table.hh.tmp; false) + +arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt + $(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \ + mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \ + ($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false) + + +.PHONY: unicode-tables arabic-table indic-table + +BUILT_SOURCES += hb-ot-shape-complex-indic-machine.hh EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl $(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \ - mv "$@.tmp" "$@" + mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false ) -noinst_PROGRAMS = main test +noinst_PROGRAMS = main indic bin_PROGRAMS = -if HAVE_GLIB -if HAVE_FREETYPE -if HAVE_CAIRO_FT -if HAVE_CAIRO_PNG -hb_view_SOURCES = hb-view.cc -hb_view_CPPFLAGS = $(HBCFLAGS) $(CAIRO_FT_CFLAGS) $(CAIRO_PNG_CFLAGS) -hb_view_LDADD = libharfbuzz.la -lm $(HBLIBS) $(CAIRO_FT_LIBS) $(CAIRO_PNG_LIBS) -bin_PROGRAMS += hb-view -endif -endif -endif -endif - main_SOURCES = main.cc main_CPPFLAGS = $(HBCFLAGS) main_LDADD = libharfbuzz.la $(HBLIBS) -test_SOURCES = test.cc -test_CPPFLAGS = $(HBCFLAGS) -test_LDADD = libharfbuzz.la $(HBLIBS) +indic_SOURCES = indic.cc +indic_CPPFLAGS = $(HBCFLAGS) +indic_LDADD = libharfbuzz.la $(HBLIBS) dist_check_SCRIPTS = \ check-c-linkage-decls.sh \ @@ -174,4 +207,8 @@ TESTS_ENVIRONMENT = \ HBHEADERS="$(HBHEADERS)" \ $(NULL) +scan: + g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library libharfbuzz.la + + -include $(top_srcdir)/git.mk diff --git a/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh index 84e77cf..e7c95ab 100755 --- a/src/check-c-linkage-decls.sh +++ b/src/check-c-linkage-decls.sh @@ -7,12 +7,10 @@ test -z "$srcdir" && srcdir=. stat=0 test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'` -test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` -cd "$srcdir" - -for x in $HBHEADERS $HBSOURCES; do +for x in $HBHEADERS; do + test -f $srcdir/$x && x=$srcdir/$x if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then echo "Ouch, file $x does not HB_BEGIN_DECLS / HB_END_DECLS" stat=1 diff --git a/src/check-header-guards.sh b/src/check-header-guards.sh index ddbcd83..af9fa7f 100755 --- a/src/check-header-guards.sh +++ b/src/check-header-guards.sh @@ -10,13 +10,12 @@ test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'` test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` -cd "$srcdir" - for x in $HBHEADERS $HBSOURCES; do + test -f "$srcdir/$x" && x="$srcdir/$x" echo "$x" | grep '[^h]$' -q && continue; - x=`echo "$x" | sed 's@.*/@@'` - tag=`echo "$x" | tr 'a-z.-' 'A-Z_'` - lines=`grep "\<$tag\>" "$x" | wc -l` + xx=`echo "$x" | sed 's@.*/@@'` + tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'` + lines=`grep "\<$tag\>" "$x" | wc -l | sed 's/[ ]*//g'` if test "x$lines" != x3; then echo "Ouch, header file $x does not have correct preprocessor guards" stat=1 diff --git a/src/check-internal-symbols.sh b/src/check-internal-symbols.sh index 2885fa4..a24a693 100755 --- a/src/check-internal-symbols.sh +++ b/src/check-internal-symbols.sh @@ -11,18 +11,24 @@ if which nm 2>/dev/null >/dev/null; then : else echo "check-internal-symbols.sh: 'nm' not found; skipping test" - exit 0 + exit 77 fi -so=.libs/libharfbuzz.so -if test -f "$so"; then - echo "Checking that we are exposing internal symbols" - if nm $so | grep ' T ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then - echo "Ouch, internal symbols exposed" - stat=1 +tested=false +for suffix in so; do + so=.libs/libharfbuzz.$suffix + if test -f "$so"; then + echo "Checking that we are exposing internal symbols" + if nm $so | grep ' T ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then + echo "Ouch, internal symbols exposed" + stat=1 + fi + tested=true fi -else - echo "check-internal-symbols.sh: libharfbuzz.so not found; skipping test" +done +if ! $tested; then + echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test" + exit 77 fi exit $stat diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh index 40e73b0..0521532 100755 --- a/src/check-libstdc++.sh +++ b/src/check-libstdc++.sh @@ -11,18 +11,24 @@ if which ldd 2>/dev/null >/dev/null; then : else echo "check-libstdc++.sh: 'ldd' not found; skipping test" - exit 0 + exit 77 fi -so=.libs/libharfbuzz.so -if test -f "$so"; then - echo "Checking that we are not linking to libstdc++" - if ldd $so | grep 'libstdc[+][+]'; then - echo "Ouch, linked to libstdc++" - stat=1 +tested=false +for suffix in so dylib; do + so=.libs/libharfbuzz.$suffix + if test -f "$so"; then + echo "Checking that we are not linking to libstdc++" + if ldd $so | grep 'libstdc[+][+]'; then + echo "Ouch, linked to libstdc++" + stat=1 + fi + tested=true fi -else - echo "check-libstdc++.sh: libharfbuzz.so not found; skipping test" +done +if ! $tested; then + echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test" + exit 77 fi exit $stat diff --git a/src/gen-arabic-table.py b/src/gen-arabic-table.py index 10fb22d..2d3c881 100755 --- a/src/gen-arabic-table.py +++ b/src/gen-arabic-table.py @@ -1,88 +1,197 @@ #!/usr/bin/python import sys +import os.path -if len (sys.argv) < 2: - print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt" +if len (sys.argv) != 3: + print >>sys.stderr, "usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt" sys.exit (1) -f = file (sys.argv[1]) +files = [file (x) for x in sys.argv[1:]] -header = f.readline (), f.readline () -while f.readline ().find ('##################') < 0: +headers = [[files[0].readline (), files[0].readline ()]] +headers.append (["UnicodeData.txt does not have a header."]) +while files[0].readline ().find ('##################') < 0: pass +def print_joining_table(f): + + print + print "static const uint8_t joining_table[] =" + print "{" + + min_u = 0x110000 + max_u = 0 + num = 0 + last = -1 + block = '' + for line in f: + + if line[0] == '#': + if line.find (" characters"): + block = line[2:].strip () + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + u = int (fields[0], 16) + if u == 0x200C or u == 0x200D: + continue + if u < last: + raise Exception ("Input data character not sorted", u) + min_u = min (min_u, u) + max_u = max (max_u, u) + num += 1 + + if block: + print "\n /* %s */\n" % block + block = '' + + if last != -1: + last += 1 + while last < u: + print " JOINING_TYPE_X, /* %04X */" % last + last += 1 + else: + last = u + + if fields[3] in ["ALAPH", "DALATH RISH"]: + value = "JOINING_GROUP_" + fields[3].replace(' ', '_') + else: + value = "JOINING_TYPE_" + fields[2] + print " %s, /* %s */" % (value, '; '.join(fields)) + + print + print "};" + print + print "#define JOINING_TABLE_FIRST 0x%04X" % min_u + print "#define JOINING_TABLE_LAST 0x%04X" % max_u + print + + occupancy = num * 100 / (max_u - min_u + 1) + # Maintain at least 40% occupancy in the table */ + if occupancy < 40: + raise Exception ("Table too sparse, please investigate: ", occupancy) + +def print_shaping_table(f): + + shapes = {} + ligatures = {} + names = {} + for line in f: + + fields = [x.strip () for x in line.split (';')] + if fields[5][0:1] != '<': + continue + + items = fields[5].split (' ') + shape, items = items[0][1:-1], tuple (int (x, 16) for x in items[1:]) + + if not shape in ['initial', 'medial', 'isolated', 'final']: + continue + + c = int (fields[0], 16) + if len (items) != 1: + # We only care about lam-alef ligatures + if len (items) != 2 or items[0] != 0x0644 or items[1] not in [0x0622, 0x0623, 0x0625, 0x0627]: + continue + + # Save ligature + names[c] = fields[1] + if items not in ligatures: + ligatures[items] = {} + ligatures[items][shape] = c + pass + else: + # Save shape + if items[0] not in names: + names[items[0]] = fields[1] + else: + names[items[0]] = os.path.commonprefix ([names[items[0]], fields[1]]).strip () + if items[0] not in shapes: + shapes[items[0]] = {} + shapes[items[0]][shape] = c + + print + print "static const uint16_t shaping_table[][4] =" + print "{" + + keys = shapes.keys () + min_u, max_u = min (keys), max (keys) + for u in range (min_u, max_u + 1): + s = [shapes[u][shape] if u in shapes and shape in shapes[u] else u + for shape in ['initial', 'medial', 'final', 'isolated']] + value = ', '.join ("0x%04X" % c for c in s) + print " {%s}, /* U+%04X %s */" % (value, u, names[u] if u in names else "") + + print "};" + print + print "#define SHAPING_TABLE_FIRST 0x%04X" % min_u + print "#define SHAPING_TABLE_LAST 0x%04X" % max_u + print + + ligas = {} + for pair in ligatures.keys (): + for shape in ligatures[pair]: + c = ligatures[pair][shape] + if shape == 'isolated': + liga = (shapes[pair[0]]['initial'], shapes[pair[1]]['final']) + elif shape == 'final': + liga = (shapes[pair[0]]['medial'], shapes[pair[1]]['final']) + else: + raise Exception ("Unexpected shape", shape) + if liga[0] not in ligas: + ligas[liga[0]] = [] + ligas[liga[0]].append ((liga[1], c)) + max_i = max (len (ligas[l]) for l in ligas) + print + print "static const struct {" + print " uint16_t first;" + print " struct {" + print " uint16_t second;" + print " uint16_t ligature;" + print " } ligatures[%d];" % max_i + print "} ligature_table[] =" + print "{" + keys = ligas.keys () + keys.sort () + for first in keys: + + print " { 0x%04X, {" % (first) + for liga in ligas[first]: + print " { 0x%04X, 0x%04X }, /* %s */" % (liga[0], liga[1], names[liga[1]]) + print " }}," + + print "};" + print + + + print "/* == Start of generated table == */" print "/*" print " * The following table is generated by running:" print " *" -print " * ./gen-arabic-table.py ArabicShaping.txt" +print " * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt" print " *" print " * on files with these headers:" print " *" -for line in header: - print " * %s" % (line.strip()) +for h in headers: + for l in h: + print " * %s" % (l.strip()) print " */" - -print "static const uint8_t joining_table[] =" -print "{" - - -min_u = 0x110000 -max_u = 0 -num = 0 -last = -1 -block = '' -for line in f: - - if line[0] == '#': - if line.find (" characters"): - block = line[2:].strip () - continue - - fields = [x.strip () for x in line.split (';')] - if len (fields) == 1: - continue - - u = int (fields[0], 16) - if u == 0x200C or u == 0x200D: - continue - if u < last: - raise Exception ("Input data character not sorted", u) - min_u = min (min_u, u) - max_u = max (max_u, u) - num += 1 - - if block: - print "\n /* %s */\n" % block - block = '' - - if last != -1: - last += 1 - while last < u: - print " JOINING_TYPE_X, /* %04X */" % last - last += 1 - else: - last = u - - if fields[3] in ["ALAPH", "DALATH RISH"]: - value = "JOINING_GROUP_" + fields[3].replace(' ', '_') - else: - value = "JOINING_TYPE_" + fields[2] - print " %s, /* %s */" % (value, '; '.join(fields)) - print -print "};" +print "#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH" +print "#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH" print -print "#define JOINING_TABLE_FIRST 0x%04X" % min_u -print "#define JOINING_TABLE_LAST 0x%04X" % max_u -print +print_joining_table (files[0]) +print_shaping_table (files[1]) +print +print "#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */" +print print "/* == End of generated table == */" -occupancy = num * 100 / (max_u - min_u + 1) -# Maintain at least 40% occupancy in the table */ -if occupancy < 40: - raise Exception ("Table too sparse, please investigate: ", occupancy) diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py index f4fdb83..94aa2ab 100755 --- a/src/gen-indic-table.py +++ b/src/gen-indic-table.py @@ -2,11 +2,11 @@ import sys -if len (sys.argv) < 4: +if len (sys.argv) != 4: print >>sys.stderr, "usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt" sys.exit (1) -files = [file (sys.argv[i+1]) for i in range (3)] +files = [file (x) for x in sys.argv[1:]] headers = [[f.readline () for i in range (2)] for f in files] @@ -19,7 +19,7 @@ for i, f in enumerate (files): j = line.find ('#') if j >= 0: line = line[:j] - + fields = [x.strip () for x in line.split (';')] if len (fields) == 1: continue @@ -74,9 +74,12 @@ for h in headers: for l in h: print " * %s" % (l.strip()) print " */" +print +print "#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH" +print "#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH" +print # Shorten values -print short = [{ "Bindu": 'Bi', "Visarga": 'Vs', @@ -120,6 +123,8 @@ print "#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)" print print +total = 0 +used = 0 def print_block (block, start, end, data): print print @@ -134,14 +139,9 @@ def print_block (block, start, end, data): d = data.get (u, defaults) sys.stdout.write ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]]))) - if num == 0: - # Filler block, don't check occupancy - return - total = end - start + 1 - occupancy = num * 100. / total - # Maintain at least 30% occupancy in the table */ - if occupancy < 30: - raise Exception ("Table too sparse, please investigate: ", occupancy, block) + global total, used + total += end - start + 1 + used += num uu = data.keys () uu.sort () @@ -179,8 +179,8 @@ print print print "#define indic_offset_total %d" % offset print -print "};" - +occupancy = used * 100. / total +print "}; /* Table occupancy: %d%% */" % occupancy print print "static INDIC_TABLE_ELEMENT_TYPE" print "get_indic_categories (hb_codepoint_t u)" @@ -192,7 +192,6 @@ for u,d in singles.items (): print " if (unlikely (u == 0x%04X)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]]) print " return _(x,x);" print "}" - print print "#undef _" for i in range (2): @@ -202,7 +201,11 @@ for i in range (2): for v in vv: print "#undef %s_%s" % \ (what_short[i], short[i][v]) - print +print "#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */" print print "/* == End of generated table == */" + +# Maintain at least 30% occupancy in the table */ +if occupancy < 30: + raise Exception ("Table too sparse, please investigate: ", occupancy) diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh new file mode 100644 index 0000000..c543a8d --- /dev/null +++ b/src/hb-atomic-private.hh @@ -0,0 +1,112 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_PRIVATE_HH +#define HB_ATOMIC_PRIVATE_HH + +#include "hb-private.hh" + + +/* atomic_int */ + +/* We need external help for these */ + +#if 0 + + +#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600 + +#include +#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchangePointer) + +typedef long hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) _InterlockedExchangeAdd (&(AI), (V)) + +#define hb_atomic_ptr_get(P) (MemoryBarrier (), (void *) *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) (_InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) + + +#elif !defined(HB_NO_MT) && defined(__APPLE__) + +#include + +typedef int32_t hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) + +#define hb_atomic_ptr_get(P) (OSMemoryBarrier (), (void *) *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) && !defined(__MINGW32__) + +typedef int hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) __sync_fetch_and_add (&(AI), (V)) + +#define hb_atomic_ptr_get(P) (void *) (__sync_synchronize (), *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) + +#elif !defined(HB_NO_MT) && defined(HAVE_GLIB) + +#include +typedef int hb_atomic_int_t; +#if GLIB_CHECK_VERSION(2,29,5) +#define hb_atomic_int_add(AI, V) g_atomic_int_add (&(AI), (V)) +#else +#define hb_atomic_int_add(AI, V) g_atomic_int_exchange_and_add (&(AI), (V)) +#endif + +#define hb_atomic_ptr_get(P) g_atomic_pointer_get (P) +#define hb_atomic_ptr_cmpexch(P,O,N) g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (N)) + + +#elif !defined(HB_NO_MT) + +#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */ +typedef volatile int hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_get(P) ((void *) *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false) + + +#else /* HB_NO_MT */ + +typedef int hb_atomic_int_t; +#define hb_atomic_int_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_get(P) ((void *) *(P)) +#define hb_atomic_ptr_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + +#endif + +/* TODO Add tracing. */ + +#endif /* HB_ATOMIC_PRIVATE_HH */ diff --git a/src/hb-blob.cc b/src/hb-blob.cc index 2d0c016..3cc2d9d 100644 --- a/src/hb-blob.cc +++ b/src/hb-blob.cc @@ -39,7 +39,6 @@ #include #include -HB_BEGIN_DECLS #ifndef HB_DEBUG_BLOB @@ -49,6 +48,7 @@ HB_BEGIN_DECLS struct _hb_blob_t { hb_object_header_t header; + ASSERT_POD (); bool immutable; @@ -60,19 +60,6 @@ struct _hb_blob_t { hb_destroy_func_t destroy; }; -static hb_blob_t _hb_blob_nil = { - HB_OBJECT_HEADER_STATIC, - - TRUE, /* immutable */ - - 0, /* length */ - NULL, /* data */ - HB_MEMORY_MODE_READONLY, /* mode */ - - NULL, /* user_data */ - NULL /* destroy */ -}; - static bool _try_writable (hb_blob_t *blob); @@ -98,7 +85,7 @@ hb_blob_create (const char *data, if (!length || !(blob = hb_object_create ())) { if (destroy) destroy (user_data); - return &_hb_blob_nil; + return hb_blob_get_empty (); } blob->data = data; @@ -112,7 +99,7 @@ hb_blob_create (const char *data, blob->mode = HB_MEMORY_MODE_READONLY; if (!_try_writable (blob)) { hb_blob_destroy (blob); - return &_hb_blob_nil; + return hb_blob_get_empty (); } } @@ -127,7 +114,7 @@ hb_blob_create_sub_blob (hb_blob_t *parent, hb_blob_t *blob; if (!length || offset >= parent->length) - return &_hb_blob_nil; + return hb_blob_get_empty (); hb_blob_make_immutable (parent); @@ -143,7 +130,20 @@ hb_blob_create_sub_blob (hb_blob_t *parent, hb_blob_t * hb_blob_get_empty (void) { - return &_hb_blob_nil; + static const hb_blob_t _hb_blob_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* data */ + 0, /* length */ + HB_MEMORY_MODE_READONLY, /* mode */ + + NULL, /* user_data */ + NULL /* destroy */ + }; + + return const_cast (&_hb_blob_nil); } hb_blob_t * @@ -166,9 +166,10 @@ hb_bool_t hb_blob_set_user_data (hb_blob_t *blob, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (blob, key, data, destroy); + return hb_object_set_user_data (blob, key, data, destroy, replace); } void * @@ -185,7 +186,7 @@ hb_blob_make_immutable (hb_blob_t *blob) if (hb_object_is_inert (blob)) return; - blob->immutable = TRUE; + blob->immutable = true; } hb_bool_t @@ -244,7 +245,7 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) if ((uintptr_t) -1L == pagesize) { DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); - return FALSE; + return false; } DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); @@ -256,7 +257,7 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) addr, addr+length, (unsigned long) length); if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno)); - return FALSE; + return false; } blob->mode = HB_MEMORY_MODE_WRITABLE; @@ -264,9 +265,9 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) DEBUG_MSG_FUNC (BLOB, blob, "successfully made [%p..%p] (%lu bytes) writable\n", addr, addr+length, (unsigned long) length); - return TRUE; + return true; #else - return FALSE; + return false; #endif } @@ -276,38 +277,38 @@ _try_writable_inplace (hb_blob_t *blob) DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n"); if (_try_make_writable_inplace_unix (blob)) - return TRUE; + return true; DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); /* Failed to make writable inplace, mark that */ blob->mode = HB_MEMORY_MODE_READONLY; - return FALSE; + return false; } static bool _try_writable (hb_blob_t *blob) { if (blob->immutable) - return FALSE; + return false; if (blob->mode == HB_MEMORY_MODE_WRITABLE) - return TRUE; + return true; if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) - return TRUE; + return true; if (blob->mode == HB_MEMORY_MODE_WRITABLE) - return TRUE; + return true; - DEBUG_MSG_FUNC (BLOB, blob, "currect data is -> %p\n", blob->data); + DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data); char *new_data; new_data = (char *) malloc (blob->length); if (unlikely (!new_data)) - return FALSE; + return false; DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); @@ -318,8 +319,7 @@ _try_writable (hb_blob_t *blob) blob->user_data = new_data; blob->destroy = free; - return TRUE; + return true; } -HB_END_DECLS diff --git a/src/hb-blob.h b/src/hb-blob.h index f2eaae9..360310b 100644 --- a/src/hb-blob.h +++ b/src/hb-blob.h @@ -24,6 +24,10 @@ * Red Hat Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_BLOB_H #define HB_BLOB_H @@ -66,7 +70,8 @@ hb_bool_t hb_blob_set_user_data (hb_blob_t *blob, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace); void * diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh index 8f2095a..b539f26 100644 --- a/src/hb-buffer-private.hh +++ b/src/hb-buffer-private.hh @@ -35,7 +35,6 @@ #include "hb-object-private.hh" #include "hb-unicode-private.hh" -HB_BEGIN_DECLS ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); @@ -45,11 +44,13 @@ typedef struct _hb_segment_properties_t { hb_direction_t direction; hb_script_t script; hb_language_t language; + ASSERT_POD (); } hb_segment_properties_t; struct _hb_buffer_t { hb_object_header_t header; + ASSERT_POD (); /* Information about how the text in the buffer should be treated */ @@ -71,6 +72,15 @@ struct _hb_buffer_t { hb_glyph_info_t *out_info; hb_glyph_position_t *pos; + inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } + inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } + + inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } + inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } + + inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; } + inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; } + unsigned int serial; uint8_t allocated_var_bytes[8]; const char *allocated_var_owner[8]; @@ -95,6 +105,7 @@ struct _hb_buffer_t { HB_INTERNAL void reverse_range (unsigned int start, unsigned int end); HB_INTERNAL void reverse (void); HB_INTERNAL void reverse_clusters (void); + HB_INTERNAL void guess_properties (void); HB_INTERNAL void swap_buffers (void); HB_INTERNAL void clear_output (void); @@ -102,6 +113,9 @@ struct _hb_buffer_t { HB_INTERNAL void replace_glyphs_be16 (unsigned int num_in, unsigned int num_out, const uint16_t *glyph_data_be); + HB_INTERNAL void replace_glyphs (unsigned int num_in, + unsigned int num_out, + const hb_codepoint_t *glyph_data); HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index); /* Makes a copy of the glyph at idx to output and replace glyph_index */ HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index); @@ -128,13 +142,20 @@ struct _hb_buffer_t { unsigned int cluster_start, unsigned int cluster_end); + HB_INTERNAL void merge_clusters (unsigned int start, + unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, + unsigned int end); + /* Internal methods */ HB_INTERNAL bool enlarge (unsigned int size); inline bool ensure (unsigned int size) - { return likely (size <= allocated) ? TRUE : enlarge (size); } + { return likely (size <= allocated) ? true : enlarge (size); } HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + + HB_INTERNAL void *get_scratch_buffer (unsigned int *size); }; @@ -147,6 +168,5 @@ struct _hb_buffer_t { HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var) -HB_END_DECLS #endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index ea05307..e2c34f1 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -31,28 +31,14 @@ #include -HB_BEGIN_DECLS #ifndef HB_DEBUG_BUFFER #define HB_DEBUG_BUFFER (HB_DEBUG+0) #endif - -static hb_buffer_t _hb_buffer_nil = { - HB_OBJECT_HEADER_STATIC, - - &_hb_unicode_funcs_default, - { - HB_DIRECTION_INVALID, - HB_SCRIPT_INVALID, - NULL, - }, - - TRUE, /* in_error */ - TRUE, /* have_output */ - TRUE /* have_positions */ -}; +#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast (&_hb_unicode_funcs_default)) +#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID } /* Here is how the buffer works internally: * @@ -80,7 +66,7 @@ bool hb_buffer_t::enlarge (unsigned int size) { if (unlikely (in_error)) - return FALSE; + return false; unsigned int new_allocated = allocated; hb_glyph_position_t *new_pos = NULL; @@ -102,7 +88,7 @@ hb_buffer_t::enlarge (unsigned int size) done: if (unlikely (!new_pos || !new_info)) - in_error = TRUE; + in_error = true; if (likely (new_pos)) pos = new_pos; @@ -121,7 +107,7 @@ bool hb_buffer_t::make_room_for (unsigned int num_in, unsigned int num_out) { - if (unlikely (!ensure (out_len + num_out))) return FALSE; + if (unlikely (!ensure (out_len + num_out))) return false; if (out_info == info && out_len + num_out > idx + num_in) @@ -132,7 +118,17 @@ hb_buffer_t::make_room_for (unsigned int num_in, memcpy (out_info, info, out_len * sizeof (out_info[0])); } - return TRUE; + return true; +} + +void * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + out_len = 0; + *size = allocated * sizeof (pos[0]); + return pos; } @@ -145,13 +141,14 @@ hb_buffer_t::reset (void) return; hb_unicode_funcs_destroy (unicode); - unicode = _hb_buffer_nil.unicode; + unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT; - props = _hb_buffer_nil.props; + hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT; + props = default_props; - in_error = FALSE; - have_output = FALSE; - have_positions = FALSE; + in_error = false; + have_output = false; + have_positions = false; idx = 0; len = 0; @@ -189,8 +186,8 @@ hb_buffer_t::clear_output (void) if (unlikely (hb_object_is_inert (this))) return; - have_output = TRUE; - have_positions = FALSE; + have_output = true; + have_positions = false; out_len = 0; out_info = info; @@ -202,8 +199,8 @@ hb_buffer_t::clear_positions (void) if (unlikely (hb_object_is_inert (this))) return; - have_output = FALSE; - have_positions = TRUE; + have_output = false; + have_positions = true; memset (pos, 0, sizeof (pos[0]) * len); } @@ -214,6 +211,7 @@ hb_buffer_t::swap_buffers (void) if (unlikely (in_error)) return; assert (have_output); + have_output = false; if (out_info != info) { @@ -259,6 +257,32 @@ hb_buffer_t::replace_glyphs_be16 (unsigned int num_in, } void +hb_buffer_t::replace_glyphs (unsigned int num_in, + unsigned int num_out, + const uint32_t *glyph_data) +{ + if (!make_room_for (num_in, num_out)) return; + + hb_glyph_info_t orig_info = info[idx]; + for (unsigned int i = 1; i < num_in; i++) + { + hb_glyph_info_t *inf = &info[idx + i]; + orig_info.cluster = MIN (orig_info.cluster, inf->cluster); + } + + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; +} + +void hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) { if (!make_room_for (0, 1)) return; @@ -282,6 +306,8 @@ hb_buffer_t::copy_glyph (void) void hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) { + if (!make_room_for (1, 1)) return; + out_info[out_len] = info[idx]; out_info[out_len].codepoint = glyph_index; @@ -393,6 +419,58 @@ hb_buffer_t::reverse_clusters (void) reverse_range (start, i); } +void +hb_buffer_t::merge_clusters (unsigned int start, + unsigned int end) +{ + unsigned int cluster = this->info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, this->info[i].cluster); + for (unsigned int i = start; i < end; i++) + this->info[i].cluster = cluster; +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + unsigned int cluster = this->out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, this->out_info[i].cluster); + for (unsigned int i = start; i < end; i++) + this->out_info[i].cluster = cluster; +} + +void +hb_buffer_t::guess_properties (void) +{ + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = hb_unicode_script (unicode, info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* If direction is set to INVALID, guess from script */ + if (props.direction == HB_DIRECTION_INVALID) { + props.direction = hb_script_get_horizontal_direction (props.script); + } + + /* If language is not set, use default language from locale */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + static inline void dump_var_allocation (const hb_buffer_t *buffer) { @@ -448,27 +526,33 @@ void hb_buffer_t::deallocate_var_all (void) /* Public API */ hb_buffer_t * -hb_buffer_create (unsigned int pre_alloc_size) +hb_buffer_create () { hb_buffer_t *buffer; if (!(buffer = hb_object_create ())) - return &_hb_buffer_nil; + return hb_buffer_get_empty (); buffer->reset (); - if (pre_alloc_size && !buffer->ensure (pre_alloc_size)) { - hb_buffer_destroy (buffer); - return &_hb_buffer_nil; - } - return buffer; } hb_buffer_t * hb_buffer_get_empty (void) { - return &_hb_buffer_nil; + static const hb_buffer_t _hb_buffer_nil = { + HB_OBJECT_HEADER_STATIC, + + _HB_BUFFER_UNICODE_FUNCS_DEFAULT, + _HB_BUFFER_PROPS_DEFAULT, + + true, /* in_error */ + true, /* have_output */ + true /* have_positions */ + }; + + return const_cast (&_hb_buffer_nil); } hb_buffer_t * @@ -494,9 +578,10 @@ hb_bool_t hb_buffer_set_user_data (hb_buffer_t *buffer, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (buffer, key, data, destroy); + return hb_object_set_user_data (buffer, key, data, destroy, replace); } void * @@ -515,7 +600,7 @@ hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, return; if (!unicode) - unicode = _hb_buffer_nil.unicode; + unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT; hb_unicode_funcs_reference (unicode); hb_unicode_funcs_destroy (buffer->unicode); @@ -609,8 +694,11 @@ hb_bool_t hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length) { + if (unlikely (hb_object_is_inert (buffer))) + return length == 0; + if (!buffer->ensure (length)) - return FALSE; + return false; /* Wipe the new space */ if (length > buffer->len) { @@ -620,7 +708,7 @@ hb_buffer_set_length (hb_buffer_t *buffer, } buffer->len = length; - return TRUE; + return true; } unsigned int @@ -666,8 +754,25 @@ hb_buffer_reverse_clusters (hb_buffer_t *buffer) buffer->reverse_clusters (); } +void +hb_buffer_guess_properties (hb_buffer_t *buffer) +{ + buffer->guess_properties (); +} + #define ADD_UTF(T) \ HB_STMT_START { \ + if (text_length == -1) { \ + text_length = 0; \ + const T *p = (const T *) text; \ + while (*p) { \ + text_length++; \ + p++; \ + } \ + } \ + if (item_length == -1) \ + item_length = text_length - item_offset; \ + buffer->ensure (buffer->len + item_length * sizeof (T) / 4); \ const T *next = (const T *) text + item_offset; \ const T *end = next + item_length; \ while (next < end) { \ @@ -722,9 +827,9 @@ hb_utf8_next (const uint8_t *text, void hb_buffer_add_utf8 (hb_buffer_t *buffer, const char *text, - unsigned int text_length HB_UNUSED, + int text_length, unsigned int item_offset, - unsigned int item_length) + int item_length) { #define UTF_NEXT(S, E, U) hb_utf8_next (S, E, &(U)) ADD_UTF (uint8_t); @@ -756,9 +861,9 @@ hb_utf16_next (const uint16_t *text, void hb_buffer_add_utf16 (hb_buffer_t *buffer, const uint16_t *text, - unsigned int text_length HB_UNUSED, + int text_length, unsigned int item_offset, - unsigned int item_length) + int item_length) { #define UTF_NEXT(S, E, U) hb_utf16_next (S, E, &(U)) ADD_UTF (uint16_t); @@ -768,9 +873,9 @@ hb_buffer_add_utf16 (hb_buffer_t *buffer, void hb_buffer_add_utf32 (hb_buffer_t *buffer, const uint32_t *text, - unsigned int text_length HB_UNUSED, + int text_length, unsigned int item_offset, - unsigned int item_length) + int item_length) { #define UTF_NEXT(S, E, U) ((U) = *(S), (S)+1) ADD_UTF (uint32_t); @@ -778,4 +883,3 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, } -HB_END_DECLS diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 020a120..fe53197 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -27,6 +27,10 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_BUFFER_H #define HB_BUFFER_H @@ -60,7 +64,7 @@ typedef struct _hb_glyph_position_t { hb_buffer_t * -hb_buffer_create (unsigned int pre_alloc_size); +hb_buffer_create (void); hb_buffer_t * hb_buffer_get_empty (void); @@ -75,7 +79,8 @@ hb_bool_t hb_buffer_set_user_data (hb_buffer_t *buffer, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace); void * hb_buffer_get_user_data (hb_buffer_t *buffer, @@ -116,13 +121,13 @@ hb_buffer_get_language (hb_buffer_t *buffer); void hb_buffer_reset (hb_buffer_t *buffer); -/* Returns FALSE if allocation failed */ +/* Returns false if allocation failed */ hb_bool_t hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size); -/* Returns FALSE if allocation has failed before */ +/* Returns false if allocation has failed before */ hb_bool_t hb_buffer_allocation_successful (hb_buffer_t *buffer); @@ -132,6 +137,9 @@ hb_buffer_reverse (hb_buffer_t *buffer); void hb_buffer_reverse_clusters (hb_buffer_t *buffer); +void +hb_buffer_guess_properties (hb_buffer_t *buffer); + /* Filling the buffer in */ @@ -144,23 +152,23 @@ hb_buffer_add (hb_buffer_t *buffer, void hb_buffer_add_utf8 (hb_buffer_t *buffer, const char *text, - unsigned int text_length, + int text_length, unsigned int item_offset, - unsigned int item_length); + int item_length); void hb_buffer_add_utf16 (hb_buffer_t *buffer, const uint16_t *text, - unsigned int text_length, + int text_length, unsigned int item_offset, - unsigned int item_length); + int item_length); void hb_buffer_add_utf32 (hb_buffer_t *buffer, const uint32_t *text, - unsigned int text_length, + int text_length, unsigned int item_offset, - unsigned int item_length); + int item_length); /* Clears any new items added at the end */ diff --git a/src/hb-cache-private.hh b/src/hb-cache-private.hh new file mode 100644 index 0000000..a0928a0 --- /dev/null +++ b/src/hb-cache-private.hh @@ -0,0 +1,72 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_CACHE_PRIVATE_HH +#define HB_CACHE_PRIVATE_HH + +#include "hb-private.hh" + + +/* Implements a lock-free cache for int->int functions. */ + +template +struct hb_cache_t +{ + ASSERT_STATIC (key_bits >= cache_bits); + ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int)); + + inline void clear (void) + { + memset (values, 255, sizeof (values)); + } + + inline bool get (unsigned int key, unsigned int *value) + { + unsigned int k = key & ((1<> value_bits) != (key >> cache_bits)) + return false; + } + + inline bool set (unsigned int key, unsigned int value) + { + if (unlikely ((key >> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1<>cache_bits)< hb_cmap_cache_t; +typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; + + +#endif /* HB_CACHE_PRIVATE_HH */ diff --git a/src/hb-common.cc b/src/hb-common.cc index a886474..331d255 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -1,6 +1,6 @@ /* * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -35,21 +35,22 @@ #include -HB_BEGIN_DECLS /* hb_tag_t */ hb_tag_t -hb_tag_from_string (const char *s) +hb_tag_from_string (const char *s, int len) { char tag[4]; unsigned int i; - if (!s || !*s) + if (!s || !len || !*s) return HB_TAG_NONE; - for (i = 0; i < 4 && s[i]; i++) + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && s[i]; i++) tag[i] = s[i]; for (; i < 4; i++) tag[i] = ' '; @@ -68,9 +69,9 @@ const char direction_strings[][4] = { }; hb_direction_t -hb_direction_from_string (const char *str) +hb_direction_from_string (const char *str, int len) { - if (unlikely (!str || !*str)) + if (unlikely (!str || !len || !*str)) return HB_DIRECTION_INVALID; /* Lets match loosely: just match the first letter, such that @@ -79,7 +80,7 @@ hb_direction_from_string (const char *str) char c = TOLOWER (str[0]); for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) if (c == direction_strings[i][0]) - return (hb_direction_t) i; + return (hb_direction_t) (HB_DIRECTION_LTR + i); return HB_DIRECTION_INVALID; } @@ -87,8 +88,9 @@ hb_direction_from_string (const char *str) const char * hb_direction_to_string (hb_direction_t direction) { - if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings))) - return direction_strings[direction]; + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; return "invalid"; } @@ -112,18 +114,16 @@ static const char canon_map[256] = { }; static hb_bool_t -lang_equal (const void *v1, - const void *v2) +lang_equal (hb_language_t v1, + const void *v2) { const unsigned char *p1 = (const unsigned char *) v1; const unsigned char *p2 = (const unsigned char *) v2; - while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2]) - { - p1++, p2++; - } + while (*p1 && *p1 == canon_map[*p2]) + p1++, p2++; - return (canon_map[*p1] == canon_map[*p2]); + return *p1 == canon_map[*p2]; } #if 0 @@ -145,6 +145,7 @@ lang_hash (const void *key) struct hb_language_item_t { + struct hb_language_item_t *next; hb_language_t lang; inline bool operator == (const char *s) const { @@ -162,16 +163,67 @@ struct hb_language_item_t { void finish (void) { free (lang); } }; -static hb_static_mutex_t langs_lock; -static hb_lockable_set_t langs; + +/* Thread-safe lock-free language list */ + +static hb_language_item_t *langs; + +static +void free_langs (void) +{ + while (langs) { + hb_language_item_t *next = langs->next; + langs->finish (); + free (langs); + langs = next; + } +} + +static hb_language_item_t * +lang_find_or_insert (const char *key) +{ +retry: + hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); + + for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) + if (*lang == key) + return lang; + + /* Not found; allocate one. */ + hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + if (unlikely (!lang)) + return NULL; + lang->next = first_lang; + *lang = key; + + if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { + free (lang); + goto retry; + } + +#ifdef HAVE_ATEXIT + if (!first_lang) + atexit (free_langs); /* First person registers atexit() callback. */ +#endif + + return lang; +} + hb_language_t -hb_language_from_string (const char *str) +hb_language_from_string (const char *str, int len) { - if (!str || !*str) + if (!str || !len || !*str) return HB_LANGUAGE_INVALID; - hb_language_item_t *item = langs.find_or_insert (str, langs_lock); + char strbuf[32]; + if (len >= 0) { + len = MIN (len, (int) sizeof (strbuf) - 1); + str = (char *) memcpy (strbuf, str, len); + strbuf[len] = '\0'; + } + + hb_language_item_t *item = lang_find_or_insert (str); return likely (item) ? item->lang : HB_LANGUAGE_INVALID; } @@ -196,7 +248,7 @@ hb_language_get_default (void) /* I hear that setlocale() doesn't honor env vars on Windows, * but for now we ignore that. */ - default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL)); + default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); } return default_language; @@ -240,9 +292,9 @@ hb_script_from_iso15924_tag (hb_tag_t tag) } hb_script_t -hb_script_from_string (const char *s) +hb_script_from_string (const char *s, int len) { - return hb_script_from_iso15924_tag (hb_tag_from_string (s)); + return hb_script_from_iso15924_tag (hb_tag_from_string (s, len)); } hb_tag_t @@ -254,20 +306,30 @@ hb_script_to_iso15924_tag (hb_script_t script) hb_direction_t hb_script_get_horizontal_direction (hb_script_t script) { + /* http://goo.gl/x9ilM */ switch ((hb_tag_t) script) { + /* Unicode-1.1 additions */ case HB_SCRIPT_ARABIC: case HB_SCRIPT_HEBREW: + + /* Unicode-3.0 additions */ case HB_SCRIPT_SYRIAC: case HB_SCRIPT_THAANA: /* Unicode-4.0 additions */ case HB_SCRIPT_CYPRIOT: + /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + /* Unicode-5.0 additions */ case HB_SCRIPT_PHOENICIAN: case HB_SCRIPT_NKO: + /* Unicode-5.1 additions */ + case HB_SCRIPT_LYDIAN: + /* Unicode-5.2 additions */ case HB_SCRIPT_AVESTAN: case HB_SCRIPT_IMPERIAL_ARAMAIC: @@ -280,6 +342,10 @@ hb_script_get_horizontal_direction (hb_script_t script) /* Unicode-6.0 additions */ case HB_SCRIPT_MANDAIC: + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + return HB_DIRECTION_RTL; } @@ -289,44 +355,41 @@ hb_script_get_horizontal_direction (hb_script_t script) /* hb_user_data_array_t */ - -/* NOTE: Currently we use a global lock for user_data access - * threadsafety. If one day we add a mutex to any object, we - * should switch to using that insted for these too. - */ - -static hb_static_mutex_t user_data_lock; - bool hb_user_data_array_t::set (hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace, + hb_mutex_t &lock) { if (!key) return false; - if (!data && !destroy) { - items.remove (key, user_data_lock); - return true; + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } } hb_user_data_item_t item = {key, data, destroy}; - bool ret = !!items.replace_or_insert (item, user_data_lock); + bool ret = !!items.replace_or_insert (item, lock, replace); return ret; } void * -hb_user_data_array_t::get (hb_user_data_key_t *key) +hb_user_data_array_t::get (hb_user_data_key_t *key, + hb_mutex_t &lock) { hb_user_data_item_t item = {NULL }; - return items.find (key, &item, user_data_lock) ? item.data : NULL; + return items.find (key, &item, lock) ? item.data : NULL; } void -hb_user_data_array_t::finish (void) +hb_user_data_array_t::finish (hb_mutex_t &lock) { - items.finish (user_data_lock); + items.finish (lock); } @@ -357,4 +420,3 @@ hb_version_check (unsigned int major, } -HB_END_DECLS diff --git a/src/hb-common.h b/src/hb-common.h index a0ab17e..562b04c 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -26,6 +26,10 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_COMMON_H #define HB_COMMON_H @@ -89,38 +93,42 @@ typedef uint32_t hb_tag_t; #define HB_TAG_NONE HB_TAG(0,0,0,0) -hb_tag_t hb_tag_from_string (const char *s); +/* len=-1 means str is NUL-terminated */ +hb_tag_t hb_tag_from_string (const char *str, int len); /* hb_direction_t */ -typedef enum _hb_direction_t { - HB_DIRECTION_INVALID = -1, - HB_DIRECTION_LTR = 0, +typedef enum { + HB_DIRECTION_INVALID = 0, + HB_DIRECTION_LTR = 4, HB_DIRECTION_RTL, HB_DIRECTION_TTB, HB_DIRECTION_BTT } hb_direction_t; +/* len=-1 means str is NUL-terminated */ hb_direction_t -hb_direction_from_string (const char *str); +hb_direction_from_string (const char *str, int len); const char * hb_direction_to_string (hb_direction_t direction); -#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 0) -#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 2) -#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 0) -#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 1) -#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) +#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */ /* hb_language_t */ typedef struct _hb_language_t *hb_language_t; +/* len=-1 means str is NUL-terminated */ hb_language_t -hb_language_from_string (const char *str); +hb_language_from_string (const char *str, int len); const char * hb_language_to_string (hb_language_t language); @@ -171,119 +179,138 @@ typedef enum /* hb_script_t */ /* http://unicode.org/iso15924/ */ +/* http://goo.gl/x9ilM */ typedef enum { - HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), - HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), - HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), - HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), - HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), - HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), - HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), - HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), - HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), - HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), - HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), - HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), - HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), - HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), - HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), - HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), - HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), - HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), - HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), - HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), - HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), - HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), - HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), - HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), - HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), - HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), - HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), - HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), - HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), - HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), - HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), - HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), - HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), - HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), - HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), - HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), - HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), - HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), - HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), - HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), - HB_SCRIPT_CANADIAN_ABORIGINAL = HB_TAG ('C','a','n','s'), - HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), - HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), - HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), - HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), - HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + /* Unicode-1.1 additions */ + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), + HB_SCRIPT_CANADIAN_ABORIGINAL = HB_TAG ('C','a','n','s'), + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + + /* Unicode-2.0 additions */ + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + + /* Unicode-3.0 additions */ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), + + /* Unicode-3.1 additions */ + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + + /* Unicode-3.2 additions */ + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /* Unicode-4.0 additions */ - HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), - HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), - HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), - HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), - HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), - HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), - HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), - HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /* Unicode-4.1 additions */ - HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), - HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), - HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), - HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), - HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), - HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), - HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /* Unicode-5.0 additions */ - HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), - HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), - HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), - HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), - HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), - HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /* Unicode-5.1 additions */ - HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), - HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), - HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), - HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), - HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), - HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), - HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), - HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), - HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), - HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), - HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /* Unicode-5.2 additions */ - HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), - HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), - HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), - HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), - HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), - HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), - HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), - HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), - HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), - HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), - HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), - HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /* Unicode-6.0 additions */ - HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), - HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), - HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + + /* Unicode-6.1 additions */ + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /* No script set */ - HB_SCRIPT_INVALID = HB_TAG_NONE + HB_SCRIPT_INVALID = HB_TAG_NONE } hb_script_t; @@ -293,8 +320,9 @@ hb_script_t hb_script_from_iso15924_tag (hb_tag_t tag); /* suger for tag_from_string() then script_from_iso15924_tag */ +/* len=-1 means s is NUL-terminated */ hb_script_t -hb_script_from_string (const char *s); +hb_script_from_string (const char *s, int len); hb_tag_t hb_script_to_iso15924_tag (hb_script_t script); @@ -306,6 +334,7 @@ hb_script_get_horizontal_direction (hb_script_t script); /* User data */ typedef struct _hb_user_data_key_t { + /*< private >*/ char unused; } hb_user_data_key_t; diff --git a/src/hb-fallback-shape-private.hh b/src/hb-fallback-shape-private.hh new file mode 100644 index 0000000..159456d --- /dev/null +++ b/src/hb-fallback-shape-private.hh @@ -0,0 +1,47 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FALLBACK_SHAPE_PRIVATE_HH +#define HB_FALLBACK_SHAPE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-shape.h" + + +HB_BEGIN_DECLS + + +HB_INTERNAL hb_bool_t +_hb_fallback_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + + +HB_END_DECLS + +#endif /* HB_FALLBACK_SHAPE_PRIVATE_HH */ diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc new file mode 100644 index 0000000..5939887 --- /dev/null +++ b/src/hb-fallback-shape.cc @@ -0,0 +1,61 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-fallback-shape-private.hh" + +#include "hb-buffer-private.hh" + +hb_bool_t +_hb_fallback_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + buffer->guess_properties (); + + unsigned int count = buffer->len; + + for (unsigned int i = 0; i < count; i++) + hb_font_get_glyph (font, buffer->info[i].codepoint, 0, &buffer->info[i].codepoint); + + buffer->clear_positions (); + + for (unsigned int i = 0; i < count; i++) { + hb_font_get_glyph_advance_for_direction (font, buffer->info[i].codepoint, + buffer->props.direction, + &buffer->pos[i].x_advance, + &buffer->pos[i].y_advance); + hb_font_subtract_glyph_origin_for_direction (font, buffer->info[i].codepoint, + buffer->props.direction, + &buffer->pos[i].x_offset, + &buffer->pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + hb_buffer_reverse (buffer); + + return true; +} diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh index dd71b67..91a4304 100644 --- a/src/hb-font-private.hh +++ b/src/hb-font-private.hh @@ -34,7 +34,6 @@ #include "hb-font.h" #include "hb-object-private.hh" -HB_BEGIN_DECLS /* @@ -51,10 +50,13 @@ HB_BEGIN_DECLS HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ /* ^--- Add new callbacks here */ struct _hb_font_funcs_t { hb_object_header_t header; + ASSERT_POD (); hb_bool_t immutable; @@ -86,15 +88,17 @@ struct _hb_font_funcs_t { struct _hb_face_t { hb_object_header_t header; + ASSERT_POD (); hb_bool_t immutable; - hb_get_table_func_t get_table; - void *user_data; - hb_destroy_func_t destroy; + hb_reference_table_func_t reference_table; + void *user_data; + hb_destroy_func_t destroy; struct hb_ot_layout_t *ot_layout; + unsigned int index; unsigned int upem; }; @@ -105,6 +109,7 @@ struct _hb_face_t { struct _hb_font_t { hb_object_header_t header; + ASSERT_POD (); hb_bool_t immutable; @@ -155,10 +160,9 @@ struct _hb_font_t { private: - inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / this->face->upem; } + inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); } }; -HB_END_DECLS #endif /* HB_FONT_PRIVATE_HH */ diff --git a/src/hb-font.cc b/src/hb-font.cc index 0406e10..109caff 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -31,10 +31,12 @@ #include "hb-font-private.hh" #include "hb-blob.h" #include "hb-open-file-private.hh" +#include "hb-ot-head-table.hh" + +#include "hb-cache-private.hh" #include -HB_BEGIN_DECLS /* @@ -42,7 +44,7 @@ HB_BEGIN_DECLS */ static hb_bool_t -hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t unicode, hb_codepoint_t variation_selector, @@ -53,11 +55,11 @@ hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED, return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph); *glyph = 0; - return FALSE; + return false; } static hb_position_t -hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_h_advance_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, void *user_data HB_UNUSED) @@ -69,7 +71,7 @@ hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED, } static hb_position_t -hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_v_advance_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, void *user_data HB_UNUSED) @@ -81,7 +83,7 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED, } static hb_bool_t -hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_h_origin_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, hb_position_t *x, @@ -89,20 +91,18 @@ hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, - glyph, - x, y); + hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; } *x = *y = 0; - return FALSE; + return false; } static hb_bool_t -hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_v_origin_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, hb_position_t *x, @@ -110,20 +110,18 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, - glyph, - x, y); + hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; } *x = *y = 0; - return FALSE; + return false; } static hb_position_t -hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_h_kerning_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, @@ -136,7 +134,7 @@ hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, } static hb_position_t -hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_v_kerning_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph, @@ -149,7 +147,7 @@ hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, } static hb_bool_t -hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_extents_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, hb_glyph_extents_t *extents, @@ -167,11 +165,11 @@ hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, } memset (extents, 0, sizeof (*extents)); - return FALSE; + return false; } static hb_bool_t -hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, +hb_font_get_glyph_contour_point_nil (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, unsigned int point_index, @@ -180,23 +178,49 @@ hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { if (font->parent) { - hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, - glyph, point_index, - x, y); + hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, glyph, point_index, x, y); if (ret) font->parent_scale_position (x, y); return ret; } *x = *y = 0; - return FALSE; + return false; +} + +static hb_bool_t +hb_font_get_glyph_name_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + if (font->parent) + return hb_font_get_glyph_name (font->parent, glyph, name, size); + + snprintf (name, size, "gid%u", glyph); + return false; +} + +static hb_bool_t +hb_font_get_glyph_from_name_nil (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return hb_font_get_glyph_from_name (font->parent, name, len, glyph); + + *glyph = 0; + return false; } -static hb_font_funcs_t _hb_font_funcs_nil = { +static const hb_font_funcs_t _hb_font_funcs_nil = { HB_OBJECT_HEADER_STATIC, - TRUE, /* immutable */ + true, /* immutable */ { #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, @@ -212,7 +236,7 @@ hb_font_funcs_create (void) hb_font_funcs_t *ffuncs; if (!(ffuncs = hb_object_create ())) - return &_hb_font_funcs_nil; + return hb_font_funcs_get_empty (); ffuncs->get = _hb_font_funcs_nil.get; @@ -222,7 +246,7 @@ hb_font_funcs_create (void) hb_font_funcs_t * hb_font_funcs_get_empty (void) { - return &_hb_font_funcs_nil; + return const_cast (&_hb_font_funcs_nil); } hb_font_funcs_t * @@ -248,9 +272,10 @@ hb_bool_t hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (ffuncs, key, data, destroy); + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); } void * @@ -267,7 +292,7 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) if (hb_object_is_inert (ffuncs)) return; - ffuncs->immutable = TRUE; + ffuncs->immutable = true; } hb_bool_t @@ -285,8 +310,11 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (ffuncs->immutable) \ + if (ffuncs->immutable) { \ + if (destroy) \ + destroy (user_data); \ return; \ + } \ \ if (ffuncs->destroy.name) \ ffuncs->destroy.name (ffuncs->user_data.name); \ @@ -399,6 +427,28 @@ hb_font_get_glyph_contour_point (hb_font_t *font, font->klass->user_data.glyph_contour_point); } +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size) +{ + return font->klass->get.glyph_name (font, font->user_data, + glyph, + name, size, + font->klass->user_data.glyph_name); +} + +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->klass->get.glyph_from_name (font, font->user_data, + name, len, + glyph, + font->klass->user_data.glyph_from_name); +} + /* A bit higher-level, and with fallback */ @@ -528,41 +578,42 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, * hb_face_t */ -static hb_face_t _hb_face_nil = { +static const hb_face_t _hb_face_nil = { HB_OBJECT_HEADER_STATIC, - TRUE, /* immutable */ + true, /* immutable */ - NULL, /* get_table */ + NULL, /* reference_table */ NULL, /* user_data */ NULL, /* destroy */ NULL, /* ot_layout */ - 1000 + 0, /* index */ + 1000 /* upem */ }; hb_face_t * -hb_face_create_for_tables (hb_get_table_func_t get_table, - void *user_data, - hb_destroy_func_t destroy) +hb_face_create_for_tables (hb_reference_table_func_t reference_table, + void *user_data, + hb_destroy_func_t destroy) { hb_face_t *face; - if (!get_table || !(face = hb_object_create ())) { + if (!reference_table || !(face = hb_object_create ())) { if (destroy) destroy (user_data); - return &_hb_face_nil; + return hb_face_get_empty (); } - face->get_table = get_table; + face->reference_table = reference_table; face->user_data = user_data; face->destroy = destroy; face->ot_layout = _hb_ot_layout_create (face); - face->upem = _hb_ot_layout_get_upem (face); + face->upem = 0; return face; } @@ -596,10 +647,13 @@ _hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) } static hb_blob_t * -_hb_face_for_data_get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + if (tag == HB_TAG_NONE) + return hb_blob_reference (data->blob); + const OpenTypeFontFile &ot_file = *Sanitizer::lock_instance (data->blob); const OpenTypeFontFace &ot_face = ot_file.get_face (data->index); @@ -614,23 +668,29 @@ hb_face_t * hb_face_create (hb_blob_t *blob, unsigned int index) { + hb_face_t *face; + if (unlikely (!blob || !hb_blob_get_length (blob))) - return &_hb_face_nil; + return hb_face_get_empty (); hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer::sanitize (hb_blob_reference (blob)), index); if (unlikely (!closure)) - return &_hb_face_nil; + return hb_face_get_empty (); - return hb_face_create_for_tables (_hb_face_for_data_get_table, + face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, (hb_destroy_func_t) _hb_face_for_data_closure_destroy); + + hb_face_set_index (face, index); + + return face; } hb_face_t * hb_face_get_empty (void) { - return &_hb_face_nil; + return const_cast (&_hb_face_nil); } @@ -657,9 +717,10 @@ hb_bool_t hb_face_set_user_data (hb_face_t *face, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (face, key, data, destroy); + return hb_object_set_user_data (face, key, data, destroy, replace); } void * @@ -691,45 +752,64 @@ hb_face_reference_table (hb_face_t *face, { hb_blob_t *blob; - if (unlikely (!face || !face->get_table)) + if (unlikely (!face || !face->reference_table)) return hb_blob_get_empty (); - blob = face->get_table (face, tag, face->user_data); + blob = face->reference_table (face, tag, face->user_data); if (unlikely (!blob)) return hb_blob_get_empty (); return blob; } -unsigned int -hb_face_get_upem (hb_face_t *face) +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) { - return _hb_ot_layout_get_upem (face); + return hb_face_reference_table (face, HB_TAG_NONE); } +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (hb_object_is_inert (face)) + return; -/* - * hb_font_t - */ + face->index = index; +} -static hb_font_t _hb_font_nil = { - HB_OBJECT_HEADER_STATIC, +unsigned int +hb_face_get_index (hb_face_t *face) +{ + return face->index; +} - TRUE, /* immutable */ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (hb_object_is_inert (face)) + return; - NULL, /* parent */ - &_hb_face_nil, + face->upem = upem; +} - 0, /* x_scale */ - 0, /* y_scale */ +unsigned int +hb_face_get_upem (hb_face_t *face) +{ + if (unlikely (!face->upem)) { + hb_blob_t *head_blob = Sanitizer::sanitize (hb_face_reference_table (face, HB_OT_TAG_head)); + const head *head_table = Sanitizer::lock_instance (head_blob); + face->upem = head_table->get_upem (); + hb_blob_destroy (head_blob); + } + return face->upem; +} - 0, /* x_ppem */ - 0, /* y_ppem */ - &_hb_font_funcs_nil, /* klass */ - NULL, /* user_data */ - NULL /* destroy */ -}; +/* + * hb_font_t + */ hb_font_t * hb_font_create (hb_face_t *face) @@ -737,15 +817,15 @@ hb_font_create (hb_face_t *face) hb_font_t *font; if (unlikely (!face)) - face = &_hb_face_nil; + face = hb_face_get_empty (); if (unlikely (hb_object_is_inert (face))) - return &_hb_font_nil; + return hb_font_get_empty (); if (!(font = hb_object_create ())) - return &_hb_font_nil; + return hb_font_get_empty (); hb_face_make_immutable (face); font->face = hb_face_reference (face); - font->klass = &_hb_font_funcs_nil; + font->klass = hb_font_funcs_get_empty (); return font; } @@ -754,7 +834,7 @@ hb_font_t * hb_font_create_sub_font (hb_font_t *parent) { if (unlikely (!parent)) - return &_hb_font_nil; + return hb_font_get_empty (); hb_font_t *font = hb_font_create (parent->face); @@ -769,15 +849,32 @@ hb_font_create_sub_font (hb_font_t *parent) font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; - font->klass = &_hb_font_funcs_nil; - return font; } hb_font_t * hb_font_get_empty (void) { - return &_hb_font_nil; + static const hb_font_t _hb_font_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* parent */ + const_cast (&_hb_face_nil), + + 0, /* x_scale */ + 0, /* y_scale */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + + const_cast (&_hb_font_funcs_nil), /* klass */ + NULL, /* user_data */ + NULL /* destroy */ + }; + + return const_cast (&_hb_font_nil); } hb_font_t * @@ -804,9 +901,10 @@ hb_bool_t hb_font_set_user_data (hb_font_t *font, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (font, key, data, destroy); + return hb_object_set_user_data (font, key, data, destroy, replace); } void * @@ -850,14 +948,17 @@ hb_font_set_funcs (hb_font_t *font, void *user_data, hb_destroy_func_t destroy) { - if (font->immutable) + if (font->immutable) { + if (destroy) + destroy (user_data); return; + } if (font->destroy) font->destroy (font->user_data); if (!klass) - klass = &_hb_font_funcs_nil; + klass = hb_font_funcs_get_empty (); hb_font_funcs_reference (klass); hb_font_funcs_destroy (font->klass); @@ -866,6 +967,25 @@ hb_font_set_funcs (hb_font_t *font, font->destroy = destroy; } +void +hb_font_set_funcs_data (hb_font_t *font, + void *user_data, + hb_destroy_func_t destroy) +{ + /* Destroy user_data? */ + if (font->immutable) { + if (destroy) + destroy (user_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = user_data; + font->destroy = destroy; +} + void hb_font_set_scale (hb_font_t *font, @@ -910,4 +1030,3 @@ hb_font_get_ppem (hb_font_t *font, } -HB_END_DECLS diff --git a/src/hb-font.h b/src/hb-font.h index 37d36b4..b98759b 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -24,6 +24,10 @@ * Red Hat Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_FONT_H #define HB_FONT_H @@ -44,13 +48,13 @@ hb_face_t * hb_face_create (hb_blob_t *blob, unsigned int index); -typedef hb_blob_t * (*hb_get_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); +typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); /* calls destroy() when not needing user_data anymore */ hb_face_t * -hb_face_create_for_tables (hb_get_table_func_t get_table, - void *user_data, - hb_destroy_func_t destroy); +hb_face_create_for_tables (hb_reference_table_func_t reference_table, + void *user_data, + hb_destroy_func_t destroy); hb_face_t * hb_face_get_empty (void); @@ -65,7 +69,8 @@ hb_bool_t hb_face_set_user_data (hb_face_t *face, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace); void * @@ -83,6 +88,20 @@ hb_blob_t * hb_face_reference_table (hb_face_t *face, hb_tag_t tag); +hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +unsigned int +hb_face_get_index (hb_face_t *face); + +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + unsigned int hb_face_get_upem (hb_face_t *face); @@ -109,7 +128,8 @@ hb_bool_t hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace); void * @@ -172,6 +192,16 @@ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, vo void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data); + + /* func setters */ void @@ -215,6 +245,15 @@ hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_contour_point_func_t func, void *user_data, hb_destroy_func_t destroy); +void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t glyph_func, + void *user_data, hb_destroy_func_t destroy); +void +hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_from_name_func_t glyph_func, + void *user_data, hb_destroy_func_t destroy); + /* func dispatch */ @@ -256,6 +295,15 @@ hb_font_get_glyph_contour_point (hb_font_t *font, hb_codepoint_t glyph, unsigned int point_index, hb_position_t *x, hb_position_t *y); +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + /* high-level funcs, with fallback */ @@ -324,7 +372,8 @@ hb_bool_t hb_font_set_user_data (hb_font_t *font, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace); void * @@ -350,6 +399,12 @@ hb_font_set_funcs (hb_font_t *font, void *font_data, hb_destroy_func_t destroy); +/* Be *very* careful with this function! */ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy); + void hb_font_set_scale (hb_font_t *font, diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 4a180ee..0589c9e 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -31,9 +31,14 @@ #include "hb-font-private.hh" +#include FT_ADVANCES_H #include FT_TRUETYPE_TABLES_H -HB_BEGIN_DECLS + + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif /* TODO: @@ -43,15 +48,21 @@ HB_BEGIN_DECLS * * - We don't handle any load_flags. That definitely has API implications. :( * I believe hb_ft_font_create() should take load_flags input. + * In particular, FT_Get_Advance() without the NO_HINTING flag seems to be + * buggy. * * - We don't handle / allow for emboldening / obliqueing. * + * - Rounding, etc? + * * - In the future, we should add constructors to create fonts in font space. * * - I believe transforms are not correctly implemented. FreeType does not * provide any API to get to the transform/delta set on the face. :( * * - Always use FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH? + * + * - FT_Load_Glyph() is exteremely costly. Do something about it? */ @@ -70,7 +81,7 @@ hb_ft_get_glyph (hb_font_t *font HB_UNUSED, if (unlikely (variation_selector)) { *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector); if (*glyph) - return TRUE; + return true; } #endif @@ -85,12 +96,13 @@ hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { FT_Face ft_face = (FT_Face) font_data; - int load_flags = FT_LOAD_DEFAULT; + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + FT_Fixed v; - if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) + if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v))) return 0; - return ft_face->glyph->metrics.horiAdvance; + return v >> 10; } static hb_position_t @@ -100,14 +112,15 @@ hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { FT_Face ft_face = (FT_Face) font_data; - int load_flags = FT_LOAD_DEFAULT; + int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_VERTICAL_LAYOUT; + FT_Fixed v; - if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) + if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v))) return 0; /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ - return -ft_face->glyph->metrics.vertAdvance; + return -v >> 10; } static hb_bool_t @@ -119,7 +132,7 @@ hb_ft_get_glyph_h_origin (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { /* We always work in the horizontal coordinates. */ - return TRUE; + return true; } static hb_bool_t @@ -134,14 +147,14 @@ hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, int load_flags = FT_LOAD_DEFAULT; if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) - return FALSE; + return false; /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); - return TRUE; + return true; } static hb_position_t @@ -182,13 +195,13 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, int load_flags = FT_LOAD_DEFAULT; if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) - return FALSE; + return false; extents->x_bearing = ft_face->glyph->metrics.horiBearingX; extents->y_bearing = ft_face->glyph->metrics.horiBearingY; extents->width = ft_face->glyph->metrics.width; extents->height = ft_face->glyph->metrics.height; - return TRUE; + return true; } static hb_bool_t @@ -204,55 +217,88 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, int load_flags = FT_LOAD_DEFAULT; if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) - return FALSE; + return false; if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) - return FALSE; + return false; if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) - return FALSE; + return false; *x = ft_face->glyph->outline.points[point_index].x; *y = ft_face->glyph->outline.points[point_index].y; - return TRUE; + return true; } -static hb_font_funcs_t ft_ffuncs = { - HB_OBJECT_HEADER_STATIC, +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + FT_Face ft_face = (FT_Face) font_data; - TRUE, /* immutable */ + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); + if (!ret) + snprintf (name, size, "gid%u", glyph); - { - hb_ft_get_glyph, - hb_ft_get_glyph_h_advance, - hb_ft_get_glyph_v_advance, - hb_ft_get_glyph_h_origin, - hb_ft_get_glyph_v_origin, - hb_ft_get_glyph_h_kerning, - hb_ft_get_glyph_v_kerning, - hb_ft_get_glyph_extents, - hb_ft_get_glyph_contour_point, + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + FT_Face ft_face = (FT_Face) font_data; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = MIN (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); } -}; -hb_font_funcs_t * -hb_ft_get_font_funcs (void) + return *glyph != 0; +} + + +static hb_font_funcs_t * +_hb_ft_get_font_funcs (void) { - return &ft_ffuncs; + static const hb_font_funcs_t ft_ffuncs = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + }; + + return const_cast (&ft_ffuncs); } static hb_blob_t * -get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { FT_Face ft_face = (FT_Face) user_data; FT_Byte *buffer; FT_ULong length = 0; FT_Error error; - if (unlikely (tag == HB_TAG_NONE)) - return NULL; + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length); if (error) @@ -292,9 +338,12 @@ hb_ft_face_create (FT_Face ft_face, face = hb_face_create (blob, ft_face->face_index); hb_blob_destroy (blob); } else { - face = hb_face_create_for_tables (get_table, ft_face, destroy); + face = hb_face_create_for_tables (reference_table, ft_face, destroy); } + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + return face; } @@ -319,6 +368,11 @@ hb_ft_face_create_cached (FT_Face ft_face) return hb_face_reference ((hb_face_t *) ft_face->generic.data); } +static void +_do_nothing (void) +{ +} + hb_font_t * hb_ft_font_create (FT_Face ft_face, @@ -331,8 +385,8 @@ hb_ft_font_create (FT_Face ft_face, font = hb_font_create (face); hb_face_destroy (face); hb_font_set_funcs (font, - hb_ft_get_font_funcs (), - ft_face, NULL); + _hb_ft_get_font_funcs (), + ft_face, (hb_destroy_func_t) _do_nothing); hb_font_set_scale (font, ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM) >> 16, ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM) >> 16); @@ -344,4 +398,91 @@ hb_ft_font_create (FT_Face ft_face, } -HB_END_DECLS +/* Thread-safe, lock-free, FT_Library */ + +static FT_Library ft_library; + +static +void free_ft_library (void) +{ + FT_Done_FreeType (ft_library); +} + +static FT_Library +get_ft_library (void) +{ +retry: + FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library); + + if (unlikely (!library)) + { + /* Not found; allocate one. */ + if (FT_Init_FreeType (&library)) + return NULL; + + if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) { + FT_Done_FreeType (library); + goto retry; + } + +#ifdef HAVE_ATEXIT + atexit (free_ft_library); /* First person registers atexit() callback. */ +#endif + } + + return library; +} + +static void +_release_blob (FT_Face ft_face) +{ + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = NULL; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + FT_Set_Char_Size (ft_face, + font->x_scale, font->y_scale, + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale); + + ft_face->generic.data = blob; + ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + + hb_font_set_funcs (font, + _hb_ft_get_font_funcs (), + ft_face, + (hb_destroy_func_t) FT_Done_Face); +} + +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (font->destroy == (hb_destroy_func_t) FT_Done_Face || + font->destroy == (hb_destroy_func_t) _do_nothing) + return (FT_Face) font->user_data; + + return NULL; +} diff --git a/src/hb-ft.h b/src/hb-ft.h index 54cce2a..696251e 100644 --- a/src/hb-ft.h +++ b/src/hb-ft.h @@ -29,8 +29,6 @@ #include "hb.h" -#include "hb-font.h" - #include #include FT_FREETYPE_H @@ -38,9 +36,6 @@ HB_BEGIN_DECLS /* Note: FreeType is not thread-safe. Hence, these functions are not either. */ -hb_font_funcs_t * -hb_ft_get_font_funcs (void); - hb_face_t * hb_ft_face_create (FT_Face ft_face, hb_destroy_func_t destroy); @@ -53,6 +48,15 @@ hb_ft_font_create (FT_Face ft_face, hb_destroy_func_t destroy); + +/* Makes an hb_font_t use FreeType internally to implement font functions. */ +void +hb_ft_font_set_funcs (hb_font_t *font); + +FT_Face +hb_ft_font_get_face (hb_font_t *font); + + HB_END_DECLS #endif /* HB_FT_H */ diff --git a/src/hb-glib.cc b/src/hb-glib.cc index 76e1dfd..6b655dd 100644 --- a/src/hb-glib.cc +++ b/src/hb-glib.cc @@ -32,9 +32,6 @@ #include "hb-unicode-private.hh" -#include - -HB_BEGIN_DECLS #if !GLIB_CHECK_VERSION(2,29,14) static const hb_script_t @@ -147,7 +144,16 @@ glib_script_to_script[] = /* Unicode-6.0 additions */ HB_SCRIPT_BATAK, HB_SCRIPT_BRAHMI, - HB_SCRIPT_MANDAIC + HB_SCRIPT_MANDAIC, + + /* Unicode-6.1 additions */ + HB_SCRIPT_CHAKMA, + HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, + HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, + HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI }; #endif @@ -245,7 +251,7 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, * sees it and makes sure it's compilable. */ if (!a || !b) - return FALSE; + return false; gchar utf8[12]; gchar *normalized; @@ -255,13 +261,15 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, len = g_unichar_to_utf8 (a, utf8); len += g_unichar_to_utf8 (b, utf8 + len); normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC); - len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + if (len == 1) { *ab = g_utf8_get_char (normalized); - ret = TRUE; + ret = true; } else { - ret = FALSE; + ret = false; } g_free (normalized); @@ -289,8 +297,10 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, len = g_unichar_to_utf8 (ab, utf8); normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD); - len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + if (len == 1) { *a = g_utf8_get_char (normalized); *b = 0; @@ -308,7 +318,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, *b = 0; } g_free (recomposed); - ret = TRUE; + ret = true; } else { /* If decomposed to more than two characters, take the last one, * and recompose the rest to get the first component. */ @@ -319,7 +329,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, /* We expect that recomposed has exactly one character now. */ *a = g_utf8_get_char (recomposed); g_free (recomposed); - ret = TRUE; + ret = true; } g_free (normalized); @@ -327,12 +337,12 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, } -extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_glib; -hb_unicode_funcs_t _hb_glib_unicode_funcs = { +extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs; +const hb_unicode_funcs_t _hb_glib_unicode_funcs = { HB_OBJECT_HEADER_STATIC, NULL, /* parent */ - TRUE, /* immutable */ + true, /* immutable */ { #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name, HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS @@ -343,8 +353,6 @@ hb_unicode_funcs_t _hb_glib_unicode_funcs = { hb_unicode_funcs_t * hb_glib_get_unicode_funcs (void) { - return &_hb_glib_unicode_funcs; + return const_cast (&_hb_glib_unicode_funcs); } - -HB_END_DECLS diff --git a/src/hb-glib.h b/src/hb-glib.h index 3bc3ebf..63a9d33 100644 --- a/src/hb-glib.h +++ b/src/hb-glib.h @@ -30,6 +30,7 @@ #define HB_GLIB_H #include "hb.h" + #include HB_BEGIN_DECLS diff --git a/src/hb-gobject-enums.cc.tmpl b/src/hb-gobject-enums.cc.tmpl new file mode 100644 index 0000000..05abd89 --- /dev/null +++ b/src/hb-gobject-enums.cc.tmpl @@ -0,0 +1,74 @@ +/*** BEGIN file-header ***/ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +inline static /* TODO(behdad) disable these for now until we fix them... */ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +/*** END value-tail ***/ diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc new file mode 100644 index 0000000..cec4854 --- /dev/null +++ b/src/hb-gobject-structs.cc @@ -0,0 +1,63 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +#define _HB_DEFINE_BOXED_TYPE(Name,underscore_name,copy_func,free_func) \ +GType \ +underscore_name##_get_type (void) \ +{ \ + static volatile gsize type = 0; \ + if (g_once_init_enter (&type)) { \ + GType t = g_boxed_type_register_static (g_intern_static_string (#Name), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type, t); \ + } \ + return type; \ +} + +#define HB_DEFINE_BOXED_TYPE(name) \ + _HB_DEFINE_BOXED_TYPE (hb_##name, hb_gobject_##name, hb_##name##_reference, hb_##name##_destroy); + +HB_DEFINE_BOXED_TYPE (buffer) +HB_DEFINE_BOXED_TYPE (blob) +HB_DEFINE_BOXED_TYPE (face) +HB_DEFINE_BOXED_TYPE (font) +HB_DEFINE_BOXED_TYPE (font_funcs) +HB_DEFINE_BOXED_TYPE (unicode_funcs) + diff --git a/src/hb-gobject.h b/src/hb-gobject.h new file mode 100644 index 0000000..4f23fdd --- /dev/null +++ b/src/hb-gobject.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GOBJECT_H +#define HB_GOBJECT_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/* Objects */ + +#define HB_GOBJECT_TYPE_BLOB hb_gobject_blob_get_type () +GType +hb_gobject_blob_get_type (void); + +#define HB_GOBJECT_TYPE_BUFFER hb_gobject_buffer_get_type () +GType +hb_gobject_buffer_get_type (void); + +#define HB_GOBJECT_TYPE_FACE hb_gobject_face_get_type () +GType +hb_gobject_face_get_type (void); + +#define HB_GOBJECT_TYPE_FONT hb_gobject_font_get_type () +GType +hb_gobject_font_get_type (void); + +#define HB_GOBJECT_TYPE_FONT_FUNCS hb_gobject_font_funcs_get_type () +GType +hb_gobject_font_funcs_get_type (void); + +#define HB_GOBJECT_TYPE_UNICODE_FUNCS hb_gobject_unicode_funcs_get_type () +GType +hb_gobject_unicode_funcs_get_type (void); + + +/* Enums */ + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/src/hb-graphite2-private.hh b/src/hb-graphite2-private.hh new file mode 100644 index 0000000..644ea75 --- /dev/null +++ b/src/hb-graphite2-private.hh @@ -0,0 +1,42 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GRAPHITE2_PRIVATE_HH +#define HB_GRAPHITE2_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-graphite2.h" + + +HB_INTERNAL hb_bool_t +_hb_graphite2_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + + +#endif /* HB_GRAPHITE2_PRIVATE_HH */ diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc new file mode 100644 index 0000000..3fa9f79 --- /dev/null +++ b/src/hb-graphite2.cc @@ -0,0 +1,350 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-graphite2.h" + +#include "hb-buffer-private.hh" +#include "hb-font-private.hh" +#include "hb-ot-tag.h" + +#include +#include + + +struct hb_gr_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; +}; + + +typedef struct hb_gr_tablelist_t { + hb_blob_t *blob; + struct hb_gr_tablelist_t *next; + unsigned int tag; +} hb_gr_tablelist_t; + +static struct hb_gr_face_data_t { + hb_face_t *face; + gr_face *grface; + hb_gr_tablelist_t *tlist; +} _hb_gr_face_data_nil = {NULL, NULL}; + +static struct hb_gr_font_data_t { + gr_font *grfont; + gr_face *grface; +} _hb_gr_font_data_nil = {NULL, NULL}; + + +static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_gr_tablelist_t *pl = NULL, *p; + hb_gr_face_data_t *face = (hb_gr_face_data_t *) data; + hb_gr_tablelist_t *tlist = face->tlist; + + for (p = tlist; p; p = p->next) + if (p->tag == tag ) { + unsigned int tlen; + const char *d = hb_blob_get_data (p->blob, &tlen); + *len = tlen; + return d; + } else + pl = p; + + if (!face->face) + return NULL; + hb_blob_t *blob = hb_face_reference_table (face->face, tag); + + if (!pl || pl->blob) + { + p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t)); + if (!p) { + hb_blob_destroy (blob); + return NULL; + } + p->next = NULL; + if (pl) + pl->next = p; + else + face->tlist = p; + pl = p; + } + pl->blob = blob; + pl->tag = tag; + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +static float hb_gr_get_advance (const void *hb_font, unsigned short gid) +{ + return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid); +} + +static void _hb_gr_face_data_destroy (void *data) +{ + hb_gr_face_data_t *f = (hb_gr_face_data_t *) data; + hb_gr_tablelist_t *tlist = f->tlist; + while (tlist) + { + hb_gr_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + free (old); + } + gr_face_destroy (f->grface); +} + +static void _hb_gr_font_data_destroy (void *data) +{ + hb_gr_font_data_t *f = (hb_gr_font_data_t *) data; + + gr_font_destroy (f->grfont); + free (f); +} + +static hb_user_data_key_t hb_gr_data_key; + +static hb_gr_face_data_t * +_hb_gr_face_get_data (hb_face_t *face) +{ + hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key); + if (likely (data)) return data; + + data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t)); + if (unlikely (!data)) + return &_hb_gr_face_data_nil; + + + hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf); + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return &_hb_gr_face_data_nil; + } + + data->face = face; + data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default); + + + if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data, + (hb_destroy_func_t) _hb_gr_face_data_destroy, + false))) + { + _hb_gr_face_data_destroy (data); + data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key); + if (data) + return data; + else + return &_hb_gr_face_data_nil; + } + + return data; +} + +static hb_gr_font_data_t * +_hb_gr_font_get_data (hb_font_t *font) +{ + hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key); + if (likely (data)) return data; + + data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t)); + if (unlikely (!data)) + return &_hb_gr_font_data_nil; + + + hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf); + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return &_hb_gr_font_data_nil; + } + + data->grface = _hb_gr_face_get_data (font->face)->grface; + int scale; + hb_font_get_scale (font, &scale, NULL); + data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance, data->grface); + + + if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data, + (hb_destroy_func_t) _hb_gr_font_data_destroy, + false))) + { + _hb_gr_font_data_destroy (data); + data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key); + if (data) + return data; + else + return &_hb_gr_font_data_nil; + } + + return data; +} + + +hb_bool_t +_hb_graphite_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + + buffer->guess_properties (); + + /* XXX We do a hell of a lot of stuff just to figure out this font + * is not graphite! Shouldn't do. */ + + hb_gr_font_data_t *data = _hb_gr_font_get_data (font); + if (!data->grface) return false; + + unsigned int charlen; + hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen); + + int success = 0; + + if (!charlen) return true; + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = strchr (lang, '-'); + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + while (num_features--) + { + const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag); + if (fref) + gr_fref_set_feature_value (fref, features->value, feats); + features++; + } + + hb_codepoint_t *gids = NULL, *pg; + hb_gr_cluster_t *clusters = NULL; + gr_segment *seg = NULL; + uint32_t *text = NULL; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + float curradvx = 0., curradvy = 0.; + unsigned int glyphlen = 0; + unsigned int *p; + + text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t)); + if (!text) goto dieout; + + p = text; + for (unsigned int i = 0; i < charlen; ++i) + *p++ = bufferi++->codepoint; + *p = 0; + + hb_tag_t script_tag[2]; + hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]); + + seg = gr_make_seg (data->grfont, data->grface, + script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], + feats, + gr_utf32, text, charlen, + 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); + if (!seg) goto dieout; + + glyphlen = gr_seg_n_slots (seg); + clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t)); + if (!glyphlen || !clusters) goto dieout; + + gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t)); + if (!gids) goto dieout; + + pg = gids; + for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) + { + unsigned int before = gr_slot_before (is); + unsigned int after = gr_slot_after (is); + *pg = gr_slot_gid (is); + pg++; + while (clusters[ci].base_char > before && ci) + { + clusters[ci-1].num_chars += clusters[ci].num_chars; + clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_gr_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + ci++; + } + clusters[ci].num_glyphs++; + + if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) + clusters[ci].num_chars = after + 1 - clusters[ci].base_char; + } + ci++; + + buffer->clear_output (); + for (unsigned int i = 0; i < ci; ++i) + buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph); + buffer->swap_buffers (); + + hb_glyph_position_t *pPos; + for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg); + is; pPos++, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X(is) - curradvx; + pPos->y_offset = gr_slot_origin_Y(is) - curradvy; + pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont); + pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont); +// if (pPos->x_advance < 0 && gr_slot_attached_to(is)) +// pPos->x_advance = 0; + curradvx += pPos->x_advance; + curradvy += pPos->y_advance; + } + pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx; + + /* TODO(behdad): + * This shaper is badly broken with RTL text. It returns glyphs + * in the logical order! + */ +// if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) +// hb_buffer_reverse (buffer); + + success = 1; + +dieout: + if (gids) free (gids); + if (clusters) free (clusters); + if (seg) gr_seg_destroy (seg); + if (text) free (text); + return success; +} diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h new file mode 100644 index 0000000..2d16cc8 --- /dev/null +++ b/src/hb-graphite2.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Martin Hosken + * Copyright (C) 2011 SIL International + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_GRAPHITE2_H +#define HB_GRAPHITE2_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f') + +/* TODO add gr_font/face etc getters and other glue API */ + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/src/hb-icu.cc b/src/hb-icu.cc index 7fe78d2..aead6dd 100644 --- a/src/hb-icu.cc +++ b/src/hb-icu.cc @@ -36,9 +36,8 @@ #include #include #include -#include +#include -HB_BEGIN_DECLS hb_script_t @@ -47,7 +46,7 @@ hb_icu_script_to_script (UScriptCode script) if (unlikely (script == USCRIPT_INVALID_CODE)) return HB_SCRIPT_INVALID; - return hb_script_from_string (uscript_getShortName (script)); + return hb_script_from_string (uscript_getShortName (script), -1); } UScriptCode @@ -159,7 +158,7 @@ hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, UErrorCode status = U_ZERO_ERROR; UScriptCode scriptCode = uscript_getScript(unicode, &status); - if (unlikely (status != U_ZERO_ERROR)) + if (unlikely (U_FAILURE (status))) return HB_SCRIPT_UNKNOWN; return hb_icu_script_to_script (scriptCode); @@ -173,30 +172,29 @@ hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, void *user_data HB_UNUSED) { if (!a || !b) - return FALSE; + return false; UChar utf16[4], normalized[5]; - gint len; + int len; hb_bool_t ret, err; UErrorCode icu_err; len = 0; - err = FALSE; + err = false; U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err); - if (err) return FALSE; + if (err) return false; U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err); - if (err) return FALSE; + if (err) return false; icu_err = U_ZERO_ERROR; len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); - if (icu_err) - return FALSE; - normalized[len] = 0; - if (u_strlen (normalized) == 1) { + if (U_FAILURE (icu_err)) + return false; + if (u_countChar32 (normalized, len) == 1) { U16_GET_UNSAFE (normalized, 0, *ab); - ret = TRUE; + ret = true; } else { - ret = FALSE; + ret = false; } return ret; @@ -210,7 +208,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, void *user_data HB_UNUSED) { UChar utf16[2], normalized[20]; - gint len; + int len; hb_bool_t ret, err; UErrorCode icu_err; @@ -219,17 +217,16 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, /* Watchout for the dragons. Err, watchout for macros changing len. */ len = 0; - err = FALSE; + err = false; U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err); - if (err) return FALSE; + if (err) return false; icu_err = U_ZERO_ERROR; len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); - if (icu_err) - return FALSE; + if (U_FAILURE (icu_err)) + return false; - normalized[len] = 0; - len = u_strlen (normalized); + len = u_countChar32 (normalized, len); if (len == 1) { U16_GET_UNSAFE (normalized, 0, *a); @@ -246,15 +243,15 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, UChar recomposed[20]; icu_err = U_ZERO_ERROR; unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); - if (icu_err) - return FALSE; + if (U_FAILURE (icu_err)) + return false; hb_codepoint_t c; U16_GET_UNSAFE (recomposed, 0, c); if (c != *a && c != ab) { *a = c; *b = 0; } - ret = TRUE; + ret = true; } else { /* If decomposed to more than two characters, take the last one, * and recompose the rest to get the first component. */ @@ -262,22 +259,23 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, UChar recomposed[20]; icu_err = U_ZERO_ERROR; len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); - if (icu_err) - return FALSE; + if (U_FAILURE (icu_err)) + return false; /* We expect that recomposed has exactly one character now. */ U16_GET_UNSAFE (recomposed, 0, *a); - ret = TRUE; + ret = true; } return ret; } -extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_icu; -hb_unicode_funcs_t _hb_icu_unicode_funcs = { + +extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs; +const hb_unicode_funcs_t _hb_icu_unicode_funcs = { HB_OBJECT_HEADER_STATIC, NULL, /* parent */ - TRUE, /* immutable */ + true, /* immutable */ { #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name, HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS @@ -288,8 +286,7 @@ hb_unicode_funcs_t _hb_icu_unicode_funcs = { hb_unicode_funcs_t * hb_icu_get_unicode_funcs (void) { - return &_hb_icu_unicode_funcs; + return const_cast (&_hb_icu_unicode_funcs); } -HB_END_DECLS diff --git a/src/hb-icu.h b/src/hb-icu.h index ecabec2..d22a8e1 100644 --- a/src/hb-icu.h +++ b/src/hb-icu.h @@ -30,6 +30,7 @@ #define HB_ICU_H #include "hb.h" + #include diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh index 91c9438..f9bd679 100644 --- a/src/hb-mutex-private.hh +++ b/src/hb-mutex-private.hh @@ -1,7 +1,7 @@ /* * Copyright © 2007 Chris Wilson * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -34,79 +34,107 @@ #include "hb-private.hh" -HB_BEGIN_DECLS - /* mutex */ /* We need external help for these */ -#ifdef HAVE_GLIB +#if 0 + + +#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__) + +#include +typedef CRITICAL_SECTION hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 } +#define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) -#include +#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER +#define hb_mutex_impl_init(M) pthread_mutex_init (M, NULL) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && defined(HAVE_GLIB) + +#include typedef GStaticMutex hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT G_STATIC_MUTEX_INIT #define hb_mutex_impl_init(M) g_static_mutex_init (M) #define hb_mutex_impl_lock(M) g_static_mutex_lock (M) #define hb_mutex_impl_unlock(M) g_static_mutex_unlock (M) -#define hb_mutex_impl_free(M) g_static_mutex_free (M) +#define hb_mutex_impl_finish(M) g_static_mutex_free (M) -#elif defined(_MSC_VER) +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) -#include +#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) +# include +# define HB_SCHED_YIELD() sched_yield () +#else +# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END +#endif -typedef CRITICAL_SECTION hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 } -#define hb_mutex_impl_init(M) InitializeCriticalSection (M) -#define hb_mutex_impl_lock(M) EnterCriticalSection (M) -#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) -#define hb_mutex_impl_free(M) DeleteCriticalSection (M) +/* This actually is not a totally awful implementation. */ +typedef volatile int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) *(M) = 0 +#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END +#define hb_mutex_impl_unlock(M) __sync_lock_release (M) +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END -#else +#elif !defined(HB_NO_MT) -#warning "Could not find any system to define platform macros, library will NOT be thread-safe" +#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) +# include +# define HB_SCHED_YIELD() sched_yield () +#else +# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END +#endif +#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ typedef volatile int hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) ((void) (*(M) = 0)) -#define hb_mutex_impl_lock(M) ((void) (*(M) = 1)) -#define hb_mutex_impl_unlock(M) ((void) (*(M) = 0)) -#define hb_mutex_impl_free(M) ((void) (*(M) = 2)) +#define hb_mutex_impl_init(M) *(M) = 0 +#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END +#define hb_mutex_impl_unlock(M) (*(M))--; +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + +#else /* HB_NO_MT */ + +typedef int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END #endif +#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} struct hb_mutex_t { + /* TODO Add tracing. */ + hb_mutex_impl_t m; inline void init (void) { hb_mutex_impl_init (&m); } inline void lock (void) { hb_mutex_impl_lock (&m); } inline void unlock (void) { hb_mutex_impl_unlock (&m); } - inline void free (void) { hb_mutex_impl_free (&m); } -}; - -#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} -#define hb_mutex_init(M) (M)->init () -#define hb_mutex_lock(M) (M)->lock () -#define hb_mutex_unlock(M) (M)->unlock () -#define hb_mutex_free(M) (M)->free () - - -struct hb_static_mutex_t : hb_mutex_t -{ - hb_static_mutex_t (void) { this->init (); } - ~hb_static_mutex_t (void) { this->free (); } - - private: - NO_COPY (hb_static_mutex_t); + inline void finish (void) { hb_mutex_impl_finish (&m); } }; -HB_END_DECLS - #endif /* HB_MUTEX_PRIVATE_HH */ diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh index 1b25443..96d1bd3 100644 --- a/src/hb-object-private.hh +++ b/src/hb-object-private.hh @@ -1,7 +1,7 @@ /* * Copyright © 2007 Chris Wilson * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -34,10 +34,9 @@ #include "hb-private.hh" +#include "hb-atomic-private.hh" #include "hb-mutex-private.hh" -HB_BEGIN_DECLS - /* Debug */ @@ -46,67 +45,30 @@ HB_BEGIN_DECLS #endif -/* atomic_int */ - -/* We need external help for these */ - -#ifdef HAVE_GLIB - -#include - -typedef volatile int hb_atomic_int_t; -#define hb_atomic_int_fetch_and_add(AI, V) g_atomic_int_exchange_and_add (&(AI), V) -#define hb_atomic_int_get(AI) g_atomic_int_get (&(AI)) -#define hb_atomic_int_set(AI, V) g_atomic_int_set (&(AI), V) - - -#elif defined(_MSC_VER) - -#include - -typedef long hb_atomic_int_t; -#define hb_atomic_int_fetch_and_add(AI, V) _InterlockedExchangeAdd (&(AI), V) -#define hb_atomic_int_get(AI) (_ReadBarrier (), (AI)) -#define hb_atomic_int_set(AI, V) ((void) _InterlockedExchange (&(AI), (V))) - - -#else - -#warning "Could not find any system to define atomic_int macros, library will NOT be thread-safe" - -typedef volatile int hb_atomic_int_t; -#define hb_atomic_int_fetch_and_add(AI, V) ((AI) += (V), (AI) - (V)) -#define hb_atomic_int_get(AI) (AI) -#define hb_atomic_int_set(AI, V) ((void) ((AI) = (V))) - - -#endif - - - - /* reference_count */ -typedef struct { - hb_atomic_int_t ref_count; - #define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1) #define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE} +struct hb_reference_count_t +{ + hb_atomic_int_t ref_count; - inline void init (int v) { ref_count = v; /* non-atomic is fine */ } - inline int inc (void) { return hb_atomic_int_fetch_and_add (ref_count, 1); } - inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); } - inline void set (int v) { hb_atomic_int_set (ref_count, v); } + inline void init (int v) { ref_count = v; } + inline int inc (void) { return hb_atomic_int_add (const_cast (ref_count), 1); } + inline int dec (void) { return hb_atomic_int_add (const_cast (ref_count), -1); } + inline void finish (void) { ref_count = HB_REFERENCE_COUNT_INVALID_VALUE; } - inline int get (void) const { return hb_atomic_int_get (ref_count); } - inline bool is_invalid (void) const { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; } + inline bool is_invalid (void) const { return ref_count == HB_REFERENCE_COUNT_INVALID_VALUE; } -} hb_reference_count_t; +}; /* user_data */ -struct hb_user_data_array_t { +#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT} +struct hb_user_data_array_t +{ + /* TODO Add tracing. */ struct hb_user_data_item_t { hb_user_data_key_t *key; @@ -119,27 +81,32 @@ struct hb_user_data_array_t { void finish (void) { if (destroy) destroy (data); } }; - hb_lockable_set_t items; + hb_lockable_set_t items; + + inline void init (void) { items.init (); } HB_INTERNAL bool set (hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); + hb_destroy_func_t destroy, + hb_bool_t replace, + hb_mutex_t &lock); - HB_INTERNAL void *get (hb_user_data_key_t *key); + HB_INTERNAL void *get (hb_user_data_key_t *key, + hb_mutex_t &lock); - HB_INTERNAL void finish (void); + HB_INTERNAL void finish (hb_mutex_t &lock); }; /* object_header */ -typedef struct _hb_object_header_t hb_object_header_t; - -struct _hb_object_header_t { +struct hb_object_header_t +{ hb_reference_count_t ref_count; + hb_mutex_t lock; hb_user_data_array_t user_data; -#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID} +#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT} static inline void *create (unsigned int size) { hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size); @@ -152,6 +119,8 @@ struct _hb_object_header_t { inline void init (void) { ref_count.init (1); + lock.init (); + user_data.init (); } inline bool is_inert (void) const { @@ -170,39 +139,45 @@ struct _hb_object_header_t { if (ref_count.dec () != 1) return false; - ref_count.init (HB_REFERENCE_COUNT_INVALID_VALUE); - - user_data.finish (); + ref_count.finish (); /* Do this before user_data */ + user_data.finish (lock); + lock.finish (); return true; } inline bool set_user_data (hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy_func) { + hb_destroy_func_t destroy_func, + hb_bool_t replace) { if (unlikely (!this || this->is_inert ())) return false; - return user_data.set (key, data, destroy_func); + return user_data.set (key, data, destroy_func, replace, lock); } inline void *get_user_data (hb_user_data_key_t *key) { - return user_data.get (key); + if (unlikely (!this || this->is_inert ())) + return NULL; + + return user_data.get (key, lock); } inline void trace (const char *function) const { + if (unlikely (!this)) return; + /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only + * prints the class name and throws away the template info. */ DEBUG_MSG (OBJECT, (void *) this, - "refcount=%d %s", - this ? ref_count.get () : 0, - function); + "%s refcount=%d", + function, + this ? ref_count.ref_count : 0); } + private: + ASSERT_POD (); }; -HB_END_DECLS - - /* object */ template @@ -239,9 +214,10 @@ template static inline bool hb_object_set_user_data (Type *obj, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return obj->header.set_user_data (key, data, destroy); + return obj->header.set_user_data (key, data, destroy, replace); } template @@ -252,9 +228,4 @@ static inline void *hb_object_get_user_data (Type *obj, } -HB_BEGIN_DECLS - - -HB_END_DECLS - #endif /* HB_OBJECT_PRIVATE_HH */ diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index 7d43e46..ce18580 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -1,5 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #ifndef HB_OPEN_FILE_PRIVATE_HH @@ -29,7 +31,6 @@ #include "hb-open-type-private.hh" -HB_BEGIN_DECLS /* @@ -52,7 +53,7 @@ typedef struct TableRecord { inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } Tag tag; /* 4-byte identifier. */ @@ -101,8 +102,7 @@ typedef struct OffsetTable public: inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && c->check_array (tables, TableRecord::static_size, numTables); + return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables)); } private: @@ -130,7 +130,7 @@ struct TTCHeaderVersion1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return table.sanitize (c, this); + return TRACE_RETURN (table.sanitize (c, this)); } private: @@ -169,11 +169,11 @@ struct TTCHeader inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!u.header.version.sanitize (c))) return false; + if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false); switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.version1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -231,14 +231,14 @@ struct OpenTypeFontFile inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!u.tag.sanitize (c))) return false; + if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false); switch (u.tag) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: - case TrueTypeTag: return u.fontFace.sanitize (c); - case TTCTag: return u.ttcHeader.sanitize (c); - default: return true; + case TrueTypeTag: return TRACE_RETURN (u.fontFace.sanitize (c)); + case TTCTag: return TRACE_RETURN (u.ttcHeader.sanitize (c)); + default: return TRACE_RETURN (true); } } @@ -253,6 +253,5 @@ struct OpenTypeFontFile }; -HB_END_DECLS #endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index ab2a346..5d90e5b 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -1,5 +1,6 @@ /* * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #ifndef HB_OPEN_TYPE_PRIVATE_HH @@ -31,8 +33,6 @@ #include "hb-blob.h" -HB_BEGIN_DECLS -HB_END_DECLS /* @@ -80,17 +80,25 @@ inline Type& StructAfter(TObject &X) */ /* Check _assertion in a method environment */ -#define _DEFINE_SIZE_ASSERTION(_assertion) \ - inline void _size_assertion (void) const \ - { ASSERT_STATIC (_assertion); } +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + inline void _instance_assertion_on_line_##_line (void) const \ + { \ + ASSERT_STATIC (_assertion); \ + ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ + } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + /* Check that _code compiles in a method environment */ -#define _DEFINE_COMPILES_ASSERTION(_code) \ - inline void _compiles_assertion (void) const \ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + inline void _compiles_assertion_on_line_##_line (void) const \ { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) #define DEFINE_SIZE_STATIC(size) \ - _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size)); \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ static const unsigned int static_size = (size); \ static const unsigned int min_size = (size) @@ -98,21 +106,21 @@ inline Type& StructAfter(TObject &X) #define VAR 1 #define DEFINE_SIZE_UNION(size, _member) \ - _DEFINE_SIZE_ASSERTION (this->u._member.static_size == (size)); \ + DEFINE_INSTANCE_ASSERTION (this->u._member.static_size == (size)); \ static const unsigned int min_size = (size) #define DEFINE_SIZE_MIN(size) \ - _DEFINE_SIZE_ASSERTION (sizeof (*this) >= (size)); \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ static const unsigned int min_size = (size) #define DEFINE_SIZE_ARRAY(size, array) \ - _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ - _DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ static const unsigned int min_size = (size) #define DEFINE_SIZE_ARRAY2(size, array1, array2) \ - _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ - _DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ static const unsigned int min_size = (size) @@ -155,7 +163,7 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type)) #define TRACE_SANITIZE() \ - hb_auto_trace_t trace (&c->debug_depth, "SANITIZE", this, NULL, HB_FUNC); + hb_auto_trace_t trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, ""); struct hb_sanitize_context_t @@ -166,24 +174,24 @@ struct hb_sanitize_context_t this->writable = false; } - inline void setup (void) + inline void start_processing (void) { this->start = hb_blob_get_data (this->blob, NULL); this->end = this->start + hb_blob_get_length (this->blob); this->edit_count = 0; this->debug_depth = 0; - DEBUG_MSG (SANITIZE, this->blob, - "init [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); + DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); } - inline void finish (void) + inline void end_processing (void) { - DEBUG_MSG (SANITIZE, this->blob, - "fini [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); + DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); hb_blob_destroy (this->blob); this->blob = NULL; @@ -193,18 +201,13 @@ struct hb_sanitize_context_t inline bool check_range (const void *base, unsigned int len) const { const char *p = (const char *) base; - bool ret = this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len; - - DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth, - "%-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->debug_depth, this->debug_depth, - p, p + len, len, - this->start, this->end, - ret ? "pass" : "FAIL"); - return likely (ret); + hb_auto_trace_t trace (&this->debug_depth, "SANITIZE", this->blob, NULL, + "check_range [%p..%p] (%d bytes) in [%p..%p]", + p, p + len, len, + this->start, this->end); + + return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len)); } inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const @@ -212,14 +215,12 @@ struct hb_sanitize_context_t const char *p = (const char *) base; bool overflows = _hb_unsigned_int_mul_overflows (len, record_size); - DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth, - "%-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s", - this->debug_depth, this->debug_depth, - p, p + (record_size * len), record_size, len, (unsigned long) record_size * len, - this->start, this->end, - !overflows ? "does not overflow" : "OVERFLOWS FAIL"); + hb_auto_trace_t trace (&this->debug_depth, "SANITIZE", this->blob, NULL, + "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]", + p, p + (record_size * len), record_size, len, (unsigned long) record_size * len, + this->start, this->end); - return likely (!overflows && this->check_range (base, record_size * len)); + return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len))); } template @@ -228,23 +229,21 @@ struct hb_sanitize_context_t return likely (this->check_range (obj, obj->min_size)); } - inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) + inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) { const char *p = (const char *) base; this->edit_count++; - DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth, - "%-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->debug_depth, this->debug_depth, - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "granted" : "REJECTED"); + hb_auto_trace_t trace (&this->debug_depth, "SANITIZE", this->blob, NULL, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end); - return this->writable; + return TRACE_RETURN (this->writable); } - unsigned int debug_depth; + mutable unsigned int debug_depth; const char *start, *end; bool writable; unsigned int edit_count; @@ -268,10 +267,10 @@ struct Sanitizer retry: DEBUG_MSG_FUNC (SANITIZE, blob, "start"); - c->setup (); + c->start_processing (); if (unlikely (!c->start)) { - c->finish (); + c->end_processing (); return blob; } @@ -305,7 +304,7 @@ struct Sanitizer } } - c->finish (); + c->end_processing (); DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED"); if (sane) @@ -340,12 +339,12 @@ struct Sanitizer */ -template class BEInt; +template struct BEInt; /* LONGTERMTODO: On machines allowing unaligned access, we can make the * following tighter by using byteswap instructions on ints directly. */ template -class BEInt +struct BEInt { public: inline void set (Type i) { hb_be_uint16_put (v,i); } @@ -355,7 +354,7 @@ class BEInt private: uint8_t v[2]; }; template -class BEInt +struct BEInt { public: inline void set (Type i) { hb_be_uint32_put (v,i); } @@ -376,7 +375,7 @@ struct IntType inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return likely (c->check_struct (this)); + return TRACE_RETURN (likely (c->check_struct (this))); } protected: BEInt v; @@ -384,18 +383,29 @@ struct IntType DEFINE_SIZE_STATIC (sizeof (Type)); }; +/* Typedef these to avoid clash with windows.h */ +#define USHORT HB_USHORT +#define SHORT HB_SHORT +#define ULONG HB_ULONG +#define LONG HB_LONG typedef IntType USHORT; /* 16-bit unsigned integer. */ typedef IntType SHORT; /* 16-bit signed integer. */ typedef IntType ULONG; /* 32-bit unsigned integer. */ typedef IntType LONG; /* 32-bit signed integer. */ +/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */ +typedef SHORT FWORD; + +/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */ +typedef USHORT UFWORD; + /* Date represented in number of seconds since 12:00 midnight, January 1, * 1904. The value is represented as a signed 64-bit integer. */ struct LONGDATETIME { inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return likely (c->check_struct (this)); + return TRACE_RETURN (likely (c->check_struct (this))); } private: LONG major; @@ -459,7 +469,7 @@ struct FixedVersion inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } USHORT major; @@ -487,26 +497,26 @@ struct GenericOffsetTo : OffsetType inline bool sanitize (hb_sanitize_context_t *c, void *base) { TRACE_SANITIZE (); - if (unlikely (!c->check_struct (this))) return false; + if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false); unsigned int offset = *this; - if (unlikely (!offset)) return true; + if (unlikely (!offset)) return TRACE_RETURN (true); Type &obj = StructAtOffset (base, offset); - return likely (obj.sanitize (c)) || neuter (c); + return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c)); } template inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) { TRACE_SANITIZE (); - if (unlikely (!c->check_struct (this))) return false; + if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false); unsigned int offset = *this; - if (unlikely (!offset)) return true; + if (unlikely (!offset)) return TRACE_RETURN (true); Type &obj = StructAtOffset (base, offset); - return likely (obj.sanitize (c, user_data)) || neuter (c); + return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c)); } private: /* Set the offset to Null */ inline bool neuter (hb_sanitize_context_t *c) { - if (c->can_edit (this, this->static_size)) { + if (c->may_edit (this, this->static_size)) { this->set (0); /* 0 is Null offset */ return true; } @@ -552,7 +562,7 @@ struct GenericArrayOf inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!sanitize_shallow (c))) return false; + if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); /* Note: for structs that do not reference other structs, * we do not need to call their sanitize() as we already did @@ -563,33 +573,32 @@ struct GenericArrayOf */ (void) (false && array[0].sanitize (c)); - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c, void *base) { TRACE_SANITIZE (); - if (unlikely (!sanitize_shallow (c))) return false; + if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (unlikely (!array[i].sanitize (c, base))) - return false; - return true; + return TRACE_RETURN (false); + return TRACE_RETURN (true); } template inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) { TRACE_SANITIZE (); - if (unlikely (!sanitize_shallow (c))) return false; + if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (unlikely (!array[i].sanitize (c, base, user_data))) - return false; - return true; + return TRACE_RETURN (false); + return TRACE_RETURN (true); } private: inline bool sanitize_shallow (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && c->check_array (this, Type::static_size, len); + return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len)); } public: @@ -631,12 +640,12 @@ struct OffsetListOf : OffsetArrayOf inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return OffsetArrayOf::sanitize (c, this); + return TRACE_RETURN (OffsetArrayOf::sanitize (c, this)); } template inline bool sanitize (hb_sanitize_context_t *c, T user_data) { TRACE_SANITIZE (); - return OffsetArrayOf::sanitize (c, this, user_data); + return TRACE_RETURN (OffsetArrayOf::sanitize (c, this, user_data)); } }; @@ -661,7 +670,7 @@ struct HeadlessArrayOf inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!sanitize_shallow (c))) return false; + if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false); /* Note: for structs that do not reference other structs, * we do not need to call their sanitize() as we already did @@ -672,7 +681,7 @@ struct HeadlessArrayOf */ (void) (false && array[0].sanitize (c)); - return true; + return TRACE_RETURN (true); } USHORT len; @@ -688,8 +697,8 @@ struct SortedArrayOf : ArrayOf { template inline int search (const SearchType &x) const { - class Cmp { - public: static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); } + struct Cmp { + static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); } }; const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp); return p ? p - this->array : -1; @@ -697,7 +706,5 @@ struct SortedArrayOf : ArrayOf { }; -HB_BEGIN_DECLS -HB_END_DECLS #endif /* HB_OPEN_TYPE_PRIVATE_HH */ diff --git a/src/hb-ot-head-private.hh b/src/hb-ot-head-table.hh similarity index 95% rename from src/hb-ot-head-private.hh rename to src/hb-ot-head-table.hh index 436666f..32d64ca 100644 --- a/src/hb-ot-head-private.hh +++ b/src/hb-ot-head-table.hh @@ -1,5 +1,6 @@ /* * Copyright © 2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,18 +23,18 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_HEAD_PRIVATE_HH -#define HB_OT_HEAD_PRIVATE_HH +#ifndef HB_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_HH #include "hb-open-type-private.hh" -HB_BEGIN_DECLS /* - * head + * head -- Font Header */ #define HB_OT_TAG_head HB_TAG('h','e','a','d') @@ -50,7 +51,7 @@ struct head inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) && likely (version.major == 1); + return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1)); } private: @@ -140,6 +141,5 @@ struct head }; -HB_END_DECLS -#endif /* HB_OT_HEAD_PRIVATE_HH */ +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh new file mode 100644 index 0000000..2eea05a --- /dev/null +++ b/src/hb-ot-hhea-table.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HHEA_TABLE_HH +#define HB_OT_HHEA_TABLE_HH + +#include "hb-open-type-private.hh" + + + +/* + * hhea -- The Horizontal Header Table + */ + +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') + + +struct hhea +{ + static const hb_tag_t Tag = HB_OT_TAG_hhea; + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1)); + } + + private: + FixedVersion version; /* 0x00010000 for version 1.0. */ + FWORD ascender; /* Typographic ascent. + * (Distance from baseline of highest + * ascender) */ + FWORD descender; /* Typographic descent. + * (Distance from baseline of lowest + * descender) */ + FWORD lineGap; /* Typographic line gap. Negative + * LineGap values are treated as zero + * in Windows 3.1, System 6, and + * System 7. */ + UFWORD advanceWidthMax; /* Maximum advance width value in + * 'hmtx' table. */ + FWORD minLeftSideBearing; /* Minimum left sidebearing value in + * 'hmtx' table. */ + FWORD minRightSideBearing; /* Minimum right sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)). */ + FWORD xMaxExtent; /* Max(lsb + (xMax - xMin)). */ + SHORT caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical. */ + SHORT caretSlopeRun; /* 0 for vertical. */ + SHORT caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non--slanted fonts */ + SHORT reserved1; /* set to 0 */ + SHORT reserved2; /* set to 0 */ + SHORT reserved3; /* set to 0 */ + SHORT reserved4; /* set to 0 */ + SHORT metricDataFormat; /* 0 for current format. */ + USHORT numberOfHMetrics; /* Number of hMetric entries in 'hmtx' + * table */ + public: + DEFINE_SIZE_STATIC (36); +}; + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh new file mode 100644 index 0000000..35cfb48 --- /dev/null +++ b/src/hb-ot-hmtx-table.hh @@ -0,0 +1,86 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type-private.hh" + + + +/* + * hmtx -- The Horizontal Metrics Table + */ + +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') + + +struct LongHorMetric +{ + USHORT advanceWidth; + SHORT lsb; + public: + DEFINE_SIZE_STATIC (4); +}; + +struct hmtx +{ + static const hb_tag_t Tag = HB_OT_TAG_hmtx; + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return TRACE_RETURN (true); + } + + private: + LongHorMetric longHorMetric[VAR]; /* Paired advance width and left side + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ + SHORT leftSideBearingX[VAR]; /* Here the advanceWidth is assumed + * to be the same as the advanceWidth + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfHMetrics. This + * generally is used with a run of + * monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the left side bearing + * values for each glyph. */ + public: + DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX); +}; + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh index ce47e22..2943a7f 100644 --- a/src/hb-ot-layout-common-private.hh +++ b/src/hb-ot-layout-common-private.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2010 Google, Inc. + * Copyright © 2010,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -30,16 +30,13 @@ #define HB_OT_LAYOUT_COMMON_PRIVATE_HH #include "hb-ot-layout-private.hh" - #include "hb-open-type-private.hh" +#include "hb-set-private.hh" -#define NO_CONTEXT ((unsigned int) 0x110000) #define NOT_COVERED ((unsigned int) 0x110000) #define MAX_NESTING_LEVEL 8 -HB_BEGIN_DECLS -HB_END_DECLS /* @@ -62,8 +59,7 @@ struct Record inline bool sanitize (hb_sanitize_context_t *c, void *base) { TRACE_SANITIZE (); - return c->check_struct (this) - && offset.sanitize (c, base); + return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base)); } Tag tag; /* 4-byte Tag identifier */ @@ -117,7 +113,7 @@ struct RecordListOf : RecordArrayOf inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return RecordArrayOf::sanitize (c, this); + return TRACE_RETURN (RecordArrayOf::sanitize (c, this)); } }; @@ -131,7 +127,11 @@ struct RangeRecord inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); + } + + inline bool intersects (const hb_set_t *glyphs) const { + return glyphs->intersects (start, end); } GlyphID start; /* First GlyphID in the range */ @@ -186,8 +186,7 @@ struct LangSys inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && featureIndex.sanitize (c); + return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c)); } Offset lookupOrder; /* = Null (reserved for an offset to a @@ -225,8 +224,7 @@ struct Script inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return defaultLangSys.sanitize (c, this) - && langSys.sanitize (c, this); + return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); } private: @@ -256,8 +254,7 @@ struct Feature inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && lookupIndex.sanitize (c); + return TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c)); } Offset featureParams; /* Offset to Feature Parameters table (if one @@ -274,7 +271,7 @@ typedef RecordListOf FeatureList; struct LookupFlag : USHORT { - enum { + enum Flags { RightToLeft = 0x0001u, IgnoreBaseGlyphs = 0x0002u, IgnoreLigatures = 0x0004u, @@ -311,14 +308,13 @@ struct Lookup inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); /* Real sanitize of the subtables is done by GSUB/GPOS/... */ - if (!(c->check_struct (this) - && subTable.sanitize (c))) return false; + if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false); if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet)) { USHORT &markFilteringSet = StructAfter (subTable); - if (!markFilteringSet.sanitize (c)) return false; + if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false); } - return true; + return TRACE_RETURN (true); } USHORT lookupType; /* Different enumerations for GSUB and GPOS */ @@ -354,9 +350,25 @@ struct CoverageFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return glyphArray.sanitize (c); + return TRACE_RETURN (glyphArray.sanitize (c)); } + inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { + return glyphs->has (glyphArray[index]); + } + + struct Iter { + inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; }; + inline bool more (void) { return i < c->glyphArray.len; } + inline void next (void) { i++; } + inline uint16_t get_glyph (void) { return c->glyphArray[i]; } + inline uint16_t get_coverage (void) { return i; } + + private: + const struct CoverageFormat1 *c; + unsigned int i; + }; + private: USHORT coverageFormat; /* Format identifier--format = 1 */ SortedArrayOf @@ -382,9 +394,50 @@ struct CoverageFormat2 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return rangeRecord.sanitize (c); + return TRACE_RETURN (rangeRecord.sanitize (c)); + } + + inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { + unsigned int i; + unsigned int count = rangeRecord.len; + for (i = 0; i < count; i++) { + const RangeRecord &range = rangeRecord[i]; + if (range.value <= index && + index < (unsigned int) range.value + (range.end - range.start) && + range.intersects (glyphs)) + return true; + else if (index < range.value) + return false; + } + return false; } + struct Iter { + inline void init (const CoverageFormat2 &c_) { + c = &c_; + coverage = 0; + i = 0; + j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0; + } + inline bool more (void) { return i < c->rangeRecord.len; } + inline void next (void) { + coverage++; + if (j == c->rangeRecord[i].end) { + i++; + if (more ()) + j = c->rangeRecord[i].start; + return; + } + j++; + } + inline uint16_t get_glyph (void) { return j; } + inline uint16_t get_coverage (void) { return coverage; } + + private: + const struct CoverageFormat2 *c; + unsigned int i, j, coverage; + }; + private: USHORT coverageFormat; /* Format identifier--format = 2 */ SortedArrayOf @@ -410,14 +463,79 @@ struct Coverage inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + inline bool intersects (const hb_set_t *glyphs) const { + /* TODO speed this up */ + Coverage::Iter iter; + for (iter.init (*this); iter.more (); iter.next ()) { + if (glyphs->has (iter.get_glyph ())) + return true; + } + return false; + } + + inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - default:return true; + case 1: return u.format1.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); + default:return false; } } + struct Iter { + Iter (void) : format (0) {}; + inline void init (const Coverage &c_) { + format = c_.u.format; + switch (format) { + case 1: return u.format1.init (c_.u.format1); + case 2: return u.format2.init (c_.u.format2); + default:return; + } + } + inline bool more (void) { + switch (format) { + case 1: return u.format1.more (); + case 2: return u.format2.more (); + default:return true; + } + } + inline void next (void) { + switch (format) { + case 1: u.format1.next (); break; + case 2: u.format2.next (); break; + default: break; + } + } + inline uint16_t get_glyph (void) { + switch (format) { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); + default:return true; + } + } + inline uint16_t get_coverage (void) { + switch (format) { + case 1: return u.format1.get_coverage (); + case 2: return u.format2.get_coverage (); + default:return true; + } + } + + private: + unsigned int format; + union { + CoverageFormat1::Iter format1; + CoverageFormat2::Iter format2; + } u; + }; + private: union { USHORT format; /* Format identifier */ @@ -447,8 +565,15 @@ struct ClassDefFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && classValue.sanitize (c); + return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c)); + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { + unsigned int count = classValue.len; + for (unsigned int i = 0; i < count; i++) + if (classValue[i] == klass && glyphs->has (startGlyph + i)) + return true; + return false; } USHORT classFormat; /* Format identifier--format = 1 */ @@ -474,7 +599,15 @@ struct ClassDefFormat2 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return rangeRecord.sanitize (c); + return TRACE_RETURN (rangeRecord.sanitize (c)); + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { + unsigned int count = rangeRecord.len; + for (unsigned int i = 0; i < count; i++) + if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs)) + return true; + return false; } USHORT classFormat; /* Format identifier--format = 2 */ @@ -500,11 +633,19 @@ struct ClassDef inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - default:return true; + case 1: return u.format1.intersects_class (glyphs, klass); + case 2: return u.format2.intersects_class (glyphs, klass); + default:return false; } } @@ -576,8 +717,7 @@ struct Device inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && c->check_range (this, this->get_size ()); + return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ())); } private: @@ -594,7 +734,5 @@ struct Device }; -HB_BEGIN_DECLS -HB_END_DECLS #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */ diff --git a/src/hb-ot-layout-gdef-private.hh b/src/hb-ot-layout-gdef-table.hh similarity index 89% rename from src/hb-ot-layout-gdef-private.hh rename to src/hb-ot-layout-gdef-table.hh index 08fd757..f29fc14 100644 --- a/src/hb-ot-layout-gdef-private.hh +++ b/src/hb-ot-layout-gdef-table.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -26,14 +26,13 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_LAYOUT_GDEF_PRIVATE_HH -#define HB_OT_LAYOUT_GDEF_PRIVATE_HH +#ifndef HB_OT_LAYOUT_GDEF_TABLE_HH +#define HB_OT_LAYOUT_GDEF_TABLE_HH #include "hb-ot-layout-common-private.hh" #include "hb-font-private.hh" -HB_BEGIN_DECLS /* @@ -72,8 +71,7 @@ struct AttachList inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && attachPoint.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); } private: @@ -103,7 +101,7 @@ struct CaretValueFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } private: @@ -129,7 +127,7 @@ struct CaretValueFormat2 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } private: @@ -152,8 +150,7 @@ struct CaretValueFormat3 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && deviceTable.sanitize (c, this); + return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this)); } private: @@ -181,12 +178,12 @@ struct CaretValue inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - case 3: return u.format3.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + case 3: return TRACE_RETURN (u.format3.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -222,7 +219,7 @@ struct LigGlyph inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return carets.sanitize (c, this); + return TRACE_RETURN (carets.sanitize (c, this)); } private: @@ -256,8 +253,7 @@ struct LigCaretList inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && ligGlyph.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); } private: @@ -279,7 +275,7 @@ struct MarkGlyphSetsFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this)); } private: @@ -303,10 +299,10 @@ struct MarkGlyphSets inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -321,14 +317,14 @@ struct MarkGlyphSets /* - * GDEF + * GDEF -- The Glyph Definition Table */ struct GDEF { static const hb_tag_t Tag = HB_OT_TAG_GDEF; - enum { + enum GlyphClasses { UnclassifiedGlyph = 0, BaseGlyph = 1, LigatureGlyph = 2, @@ -366,12 +362,13 @@ struct GDEF inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return version.sanitize (c) && likely (version.major == 1) - && glyphClassDef.sanitize (c, this) - && attachList.sanitize (c, this) - && ligCaretList.sanitize (c, this) - && markAttachClassDef.sanitize (c, this) - && (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this)); + return TRACE_RETURN (version.sanitize (c) && + likely (version.major == 1) && + glyphClassDef.sanitize (c, this) && + attachList.sanitize (c, this) && + ligCaretList.sanitize (c, this) && + markAttachClassDef.sanitize (c, this) && + (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this))); } @@ -424,6 +421,5 @@ struct GDEF }; -HB_END_DECLS -#endif /* HB_OT_LAYOUT_GDEF_PRIVATE_HH */ +#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */ diff --git a/src/hb-ot-layout-gpos-private.hh b/src/hb-ot-layout-gpos-table.hh similarity index 73% rename from src/hb-ot-layout-gpos-private.hh rename to src/hb-ot-layout-gpos-table.hh index e5edb3c..71c13a2 100644 --- a/src/hb-ot-layout-gpos-private.hh +++ b/src/hb-ot-layout-gpos-table.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2010 Google, Inc. + * Copyright © 2010,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -26,12 +26,11 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH -#define HB_OT_LAYOUT_GPOS_PRIVATE_HH +#ifndef HB_OT_LAYOUT_GPOS_TABLE_HH +#define HB_OT_LAYOUT_GPOS_TABLE_HH #include "hb-ot-layout-gsubgpos-private.hh" -HB_BEGIN_DECLS /* buffer **position** var allocations */ @@ -47,8 +46,7 @@ typedef Value ValueRecord[VAR]; struct ValueFormat : USHORT { - enum - { + enum Flags { xPlacement = 0x0001, /* Includes horizontal adjustment for placement */ yPlacement = 0x0002, /* Includes vertical adjustment for placement */ xAdvance = 0x0004, /* Includes horizontal adjustment for advance */ @@ -173,40 +171,39 @@ struct ValueFormat : USHORT inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) { TRACE_SANITIZE (); - return c->check_range (values, get_size ()) - && (!has_device () || sanitize_value_devices (c, base, values)); + return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); } inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) { TRACE_SANITIZE (); unsigned int len = get_len (); - if (!c->check_array (values, get_size (), count)) return false; + if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false); - if (!has_device ()) return true; + if (!has_device ()) return TRACE_RETURN (true); for (unsigned int i = 0; i < count; i++) { if (!sanitize_value_devices (c, base, values)) - return false; + return TRACE_RETURN (false); values += len; } - return true; + return TRACE_RETURN (true); } /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) { TRACE_SANITIZE (); - if (!has_device ()) return true; + if (!has_device ()) return TRACE_RETURN (true); for (unsigned int i = 0; i < count; i++) { if (!sanitize_value_devices (c, base, values)) - return false; + return TRACE_RETURN (false); values += stride; } - return true; + return TRACE_RETURN (true); } }; @@ -225,7 +222,7 @@ struct AnchorFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } private: @@ -257,7 +254,7 @@ struct AnchorFormat2 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } private: @@ -288,9 +285,7 @@ struct AnchorFormat3 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && xDeviceTable.sanitize (c, this) - && yDeviceTable.sanitize (c, this); + return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); } private: @@ -325,12 +320,12 @@ struct Anchor inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - case 3: return u.format3.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + case 3: return TRACE_RETURN (u.format3.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -355,13 +350,13 @@ struct AnchorMatrix inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) { TRACE_SANITIZE (); - if (!c->check_struct (this)) return false; - if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false; + if (!c->check_struct (this)) return TRACE_RETURN (false); + if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false); unsigned int count = rows * cols; - if (!c->check_array (matrix, matrix[0].static_size, count)) return false; + if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false); for (unsigned int i = 0; i < count; i++) - if (!matrix[i].sanitize (c, this)) return false; - return true; + if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false); + return TRACE_RETURN (true); } USHORT rows; /* Number of rows */ @@ -380,8 +375,7 @@ struct MarkRecord inline bool sanitize (hb_sanitize_context_t *c, void *base) { TRACE_SANITIZE (); - return c->check_struct (this) - && markAnchor.sanitize (c, base); + return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base)); } private: @@ -409,21 +403,21 @@ struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage orde hb_position_t mark_x, mark_y, base_x, base_y; - mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->idx].codepoint, &mark_x, &mark_y); + mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y); glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y); - hb_glyph_position_t &o = c->buffer->pos[c->buffer->idx]; + hb_glyph_position_t &o = c->buffer->cur_pos(); o.x_offset = base_x - mark_x; o.y_offset = base_y - mark_y; o.attach_lookback() = c->buffer->idx - glyph_pos; c->buffer->idx++; - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return ArrayOf::sanitize (c, this); + return TRACE_RETURN (ArrayOf::sanitize (c, this)); } }; @@ -438,22 +432,19 @@ struct SinglePosFormat1 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); valueFormat.apply_value (c->font, c->direction, this, - values, c->buffer->pos[c->buffer->idx]); + values, c->buffer->cur_pos()); c->buffer->idx++; - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && coverage.sanitize (c, this) - && valueFormat.sanitize_value (c, this, values); + return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values)); } private: @@ -478,26 +469,22 @@ struct SinglePosFormat2 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); - if (likely (index >= valueCount)) - return false; + if (likely (index >= valueCount)) return TRACE_RETURN (false); valueFormat.apply_value (c->font, c->direction, this, &values[index * valueFormat.get_len ()], - c->buffer->pos[c->buffer->idx]); + c->buffer->cur_pos()); c->buffer->idx++; - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && coverage.sanitize (c, this) - && valueFormat.sanitize_values (c, this, values, valueCount); + return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount)); } private: @@ -523,19 +510,19 @@ struct SinglePos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - case 2: return u.format2.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + case 2: return TRACE_RETURN (u.format2.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -582,18 +569,18 @@ struct PairSet if (c->buffer->info[pos].codepoint == record->secondGlyph) { valueFormats[0].apply_value (c->font, c->direction, this, - &record->values[0], c->buffer->pos[c->buffer->idx]); + &record->values[0], c->buffer->cur_pos()); valueFormats[1].apply_value (c->font, c->direction, this, &record->values[len1], c->buffer->pos[pos]); if (len2) pos++; c->buffer->idx = pos; - return true; + return TRACE_RETURN (true); } record = &StructAtOffset (record, record_size); } - return false; + return TRACE_RETURN (false); } struct sanitize_closure_t { @@ -606,12 +593,12 @@ struct PairSet inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) { TRACE_SANITIZE (); if (!(c->check_struct (this) - && c->check_array (array, USHORT::static_size * closure->stride, len))) return false; + && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false); unsigned int count = len; PairValueRecord *record = CastP (array); - return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) - && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride); + return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) + && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); } private: @@ -630,23 +617,15 @@ struct PairPosFormat1 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (c->buffer->idx + 2 > end)) - return false; + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); - unsigned int j = c->buffer->idx + 1; - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL)) - { - if (unlikely (j == end)) - return false; - j++; - } + if (!skippy_iter.next ()) return TRACE_RETURN (false); - return (this+pairSet[index]).apply (c, &valueFormat1, j); + return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx)); } inline bool sanitize (hb_sanitize_context_t *c) { @@ -661,9 +640,7 @@ struct PairPosFormat1 1 + len1 + len2 }; - return c->check_struct (this) - && coverage.sanitize (c, this) - && pairSet.sanitize (c, this, &closure); + return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); } private: @@ -692,42 +669,33 @@ struct PairPosFormat2 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (c->buffer->idx + 2 > end)) - return false; + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); - unsigned int j = c->buffer->idx + 1; - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL)) - { - if (unlikely (j == end)) - return false; - j++; - } + if (!skippy_iter.next ()) return TRACE_RETURN (false); unsigned int len1 = valueFormat1.get_len (); unsigned int len2 = valueFormat2.get_len (); unsigned int record_len = len1 + len2; - unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->idx].codepoint); - unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint); - if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) - return false; + unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint); + unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false); const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; valueFormat1.apply_value (c->font, c->direction, this, - v, c->buffer->pos[c->buffer->idx]); + v, c->buffer->cur_pos()); valueFormat2.apply_value (c->font, c->direction, this, - v + len1, c->buffer->pos[j]); + v + len1, c->buffer->pos[skippy_iter.idx]); + c->buffer->idx = skippy_iter.idx; if (len2) - j++; - c->buffer->idx = j; + c->buffer->idx++; - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) { @@ -735,16 +703,16 @@ struct PairPosFormat2 if (!(c->check_struct (this) && coverage.sanitize (c, this) && classDef1.sanitize (c, this) - && classDef2.sanitize (c, this))) return false; + && classDef2.sanitize (c, this))) return TRACE_RETURN (false); unsigned int len1 = valueFormat1.get_len (); unsigned int len2 = valueFormat2.get_len (); unsigned int stride = len1 + len2; unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; - return c->check_array (values, record_size, count) && - valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && - valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride); + return TRACE_RETURN (c->check_array (values, record_size, count) && + valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && + valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); } private: @@ -786,19 +754,19 @@ struct PairPos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - case 2: return u.format2.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + case 2: return TRACE_RETURN (u.format2.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -817,8 +785,7 @@ struct EntryExitRecord inline bool sanitize (hb_sanitize_context_t *c, void *base) { TRACE_SANITIZE (); - return entryAnchor.sanitize (c, base) - && exitAnchor.sanitize (c, base); + return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); } private: @@ -844,30 +811,21 @@ struct CursivePosFormat1 TRACE_APPLY (); /* We don't handle mark glyphs here. */ - if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) - return false; + if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false); - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (c->buffer->idx + 2 > end)) - return false; + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); - const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->idx].codepoint)]; - if (!this_record.exitAnchor) - return false; + const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)]; + if (!this_record.exitAnchor) return TRACE_RETURN (false); - unsigned int j = c->buffer->idx + 1; - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL)) - { - if (unlikely (j == end)) - return false; - j++; - } + if (!skippy_iter.next ()) return TRACE_RETURN (false); - const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)]; - if (!next_record.entryAnchor) - return false; + const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)]; + if (!next_record.entryAnchor) return TRACE_RETURN (false); unsigned int i = c->buffer->idx; + unsigned int j = skippy_iter.idx; hb_position_t entry_x, entry_y, exit_x, exit_y; (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, &exit_x, &exit_y); @@ -927,13 +885,12 @@ struct CursivePosFormat1 } c->buffer->idx = j; - return true; + return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && entryExitRecord.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); } private: @@ -957,17 +914,17 @@ struct CursivePos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -992,38 +949,27 @@ struct MarkBasePosFormat1 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (mark_index == NOT_COVERED)) - return false; + unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false); /* now we search backwards for a non-mark glyph */ unsigned int property; - unsigned int j = c->buffer->idx; - do - { - if (unlikely (!j)) - return false; - j--; - } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property)); + hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false); /* The following assertion is too strong, so we've disabled it. */ - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) - {/*return false;*/} + if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/} - unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint); - if (base_index == NOT_COVERED) - return false; + unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint); + if (base_index == NOT_COVERED) return TRACE_RETURN (false); - return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j); + return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && markCoverage.sanitize (c, this) - && baseCoverage.sanitize (c, this) - && markArray.sanitize (c, this) - && baseArray.sanitize (c, this, (unsigned int) classCount); + return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount)); } private: @@ -1054,17 +1000,17 @@ struct MarkBasePos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -1094,59 +1040,51 @@ struct MarkLigPosFormat1 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (mark_index == NOT_COVERED)) - return false; + unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false); /* now we search backwards for a non-mark glyph */ unsigned int property; - unsigned int j = c->buffer->idx; - do - { - if (unlikely (!j)) - return false; - j--; - } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property)); + hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false); /* The following assertion is too strong, so we've disabled it. */ - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) - {/*return false;*/} + if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/} + unsigned int j = skippy_iter.idx; unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint); - if (lig_index == NOT_COVERED) - return false; + if (lig_index == NOT_COVERED) return TRACE_RETURN (false); const LigatureArray& lig_array = this+ligatureArray; const LigatureAttach& lig_attach = lig_array[lig_index]; /* Find component to attach to */ unsigned int comp_count = lig_attach.rows; - if (unlikely (!comp_count)) - return false; + if (unlikely (!comp_count)) return TRACE_RETURN (false); + unsigned int comp_index; /* We must now check whether the ligature ID of the current mark glyph * is identical to the ligature ID of the found ligature. If yes, we * can directly use the component index. If not, we attach the mark * glyph to the last component of the ligature. */ - if (c->buffer->info[j].lig_id() && c->buffer->info[j].lig_id() == c->buffer->info[c->buffer->idx].lig_id() && c->buffer->info[c->buffer->idx].lig_comp()) + if (get_lig_id (c->buffer->info[j]) && + get_lig_id (c->buffer->cur()) && + get_lig_comp (c->buffer->cur()) > 0) { - comp_index = c->buffer->info[c->buffer->idx].lig_comp() - 1; + comp_index = get_lig_comp (c->buffer->cur()) - 1; if (comp_index >= comp_count) comp_index = comp_count - 1; } else comp_index = comp_count - 1; - return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j); + return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && markCoverage.sanitize (c, this) - && ligatureCoverage.sanitize (c, this) - && markArray.sanitize (c, this) - && ligatureArray.sanitize (c, this, (unsigned int) classCount); + return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount)); } private: @@ -1178,17 +1116,17 @@ struct MarkLigPos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -1213,44 +1151,37 @@ struct MarkMarkPosFormat1 inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (mark1_index == NOT_COVERED)) - return false; + unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false); /* now we search backwards for a suitable mark glyph until a non-mark glyph */ unsigned int property; - unsigned int j = c->buffer->idx; - do - { - if (unlikely (!j)) - return false; - j--; - } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property)); + hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1); + if (!skippy_iter.prev (&property)) return TRACE_RETURN (false); - if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) - return false; + if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false); + + unsigned int j = skippy_iter.idx; /* Two marks match only if they belong to the same base, or same component * of the same ligature. That is, the component numbers must match, and * if those are non-zero, the ligid number should also match. */ - if ((c->buffer->info[j].lig_comp() != c->buffer->info[c->buffer->idx].lig_comp()) || - (c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->idx].lig_id())) - return false; + if ((get_lig_comp (c->buffer->cur())) || + (get_lig_comp (c->buffer->info[j]) > 0 && + get_lig_id (c->buffer->cur()))) + return TRACE_RETURN (false); unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint); - if (mark2_index == NOT_COVERED) - return false; + if (mark2_index == NOT_COVERED) return TRACE_RETURN (false); - return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j); + return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) - && mark1Coverage.sanitize (c, this) - && mark2Coverage.sanitize (c, this) - && mark1Array.sanitize (c, this) - && mark2Array.sanitize (c, this, (unsigned int) classCount); + return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this) + && mark2Array.sanitize (c, this, (unsigned int) classCount)); } private: @@ -1283,17 +1214,17 @@ struct MarkMarkPos { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -1305,9 +1236,7 @@ struct MarkMarkPos }; -HB_BEGIN_DECLS static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index); -HB_END_DECLS struct ContextPos : Context { @@ -1317,7 +1246,7 @@ struct ContextPos : Context inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - return Context::apply (c, position_lookup); + return TRACE_RETURN (Context::apply (c, position_lookup)); } }; @@ -1329,7 +1258,7 @@ struct ChainContextPos : ChainContext inline bool apply (hb_apply_context_t *c) const { TRACE_APPLY (); - return ChainContext::apply (c, position_lookup); + return TRACE_RETURN (ChainContext::apply (c, position_lookup)); } }; @@ -1362,7 +1291,7 @@ struct PosLookupSubTable { friend struct PosLookup; - enum { + enum Type { Single = 1, Pair = 2, Cursive = 3, @@ -1378,32 +1307,32 @@ struct PosLookupSubTable { TRACE_APPLY (); switch (lookup_type) { - case Single: return u.single.apply (c); - case Pair: return u.pair.apply (c); - case Cursive: return u.cursive.apply (c); - case MarkBase: return u.markBase.apply (c); - case MarkLig: return u.markLig.apply (c); - case MarkMark: return u.markMark.apply (c); - case Context: return u.c.apply (c); - case ChainContext: return u.chainContext.apply (c); - case Extension: return u.extension.apply (c); - default:return false; + case Single: return TRACE_RETURN (u.single.apply (c)); + case Pair: return TRACE_RETURN (u.pair.apply (c)); + case Cursive: return TRACE_RETURN (u.cursive.apply (c)); + case MarkBase: return TRACE_RETURN (u.markBase.apply (c)); + case MarkLig: return TRACE_RETURN (u.markLig.apply (c)); + case MarkMark: return TRACE_RETURN (u.markMark.apply (c)); + case Context: return TRACE_RETURN (u.c.apply (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); + case Extension: return TRACE_RETURN (u.extension.apply (c)); + default: return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { TRACE_SANITIZE (); switch (lookup_type) { - case Single: return u.single.sanitize (c); - case Pair: return u.pair.sanitize (c); - case Cursive: return u.cursive.sanitize (c); - case MarkBase: return u.markBase.sanitize (c); - case MarkLig: return u.markLig.sanitize (c); - case MarkMark: return u.markMark.sanitize (c); - case Context: return u.c.sanitize (c); - case ChainContext: return u.chainContext.sanitize (c); - case Extension: return u.extension.sanitize (c); - default:return true; + case Single: return TRACE_RETURN (u.single.sanitize (c)); + case Pair: return TRACE_RETURN (u.pair.sanitize (c)); + case Cursive: return TRACE_RETURN (u.cursive.sanitize (c)); + case MarkBase: return TRACE_RETURN (u.markBase.sanitize (c)); + case MarkLig: return TRACE_RETURN (u.markLig.sanitize (c)); + case MarkMark: return TRACE_RETURN (u.markMark.sanitize (c)); + case Context: return TRACE_RETURN (u.c.sanitize (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); + case Extension: return TRACE_RETURN (u.extension.sanitize (c)); + default: return TRACE_RETURN (true); } } @@ -1430,25 +1359,11 @@ struct PosLookup : Lookup inline const PosLookupSubTable& get_subtable (unsigned int i) const { return this+CastR > (subTable)[i]; } - inline bool apply_once (hb_font_t *font, - hb_buffer_t *buffer, - hb_mask_t lookup_mask, - unsigned int context_length, - unsigned int nesting_level_left) const + inline bool apply_once (hb_apply_context_t *c) const { unsigned int lookup_type = get_type (); - hb_apply_context_t c[1] = {{0}}; - - c->font = font; - c->face = font->face; - c->buffer = buffer; - c->direction = buffer->props.direction; - c->lookup_mask = lookup_mask; - c->context_length = context_length; - c->nesting_level_left = nesting_level_left; - c->lookup_props = get_props (); - - if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property)) + + if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property)) return false; for (unsigned int i = 0; i < get_subtable_count (); i++) @@ -1458,23 +1373,22 @@ struct PosLookup : Lookup return false; } - inline bool apply_string (hb_font_t *font, - hb_buffer_t *buffer, - hb_mask_t mask) const + inline bool apply_string (hb_apply_context_t *c) const { bool ret = false; - if (unlikely (!buffer->len)) + if (unlikely (!c->buffer->len)) return false; - buffer->idx = 0; - while (buffer->idx < buffer->len) + c->set_lookup (*this); + + c->buffer->idx = 0; + while (c->buffer->idx < c->buffer->len) { - if ((buffer->info[buffer->idx].mask & mask) && - apply_once (font, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) + if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) ret = true; else - buffer->idx++; + c->buffer->idx++; } return ret; @@ -1482,16 +1396,16 @@ struct PosLookup : Lookup inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!Lookup::sanitize (c))) return false; + if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); OffsetArrayOf &list = CastR > (subTable); - return list.sanitize (c, this, get_type ()); + return TRACE_RETURN (list.sanitize (c, this, get_type ())); } }; typedef OffsetListOf PosLookupList; /* - * GPOS + * GPOS -- The Glyph Positioning Table */ struct GPOS : GSUBGPOS @@ -1501,20 +1415,17 @@ struct GPOS : GSUBGPOS inline const PosLookup& get_lookup (unsigned int i) const { return CastR (GSUBGPOS::get_lookup (i)); } - inline bool position_lookup (hb_font_t *font, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask) const - { return get_lookup (lookup_index).apply_string (font, buffer, mask); } + inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const + { return get_lookup (lookup_index).apply_string (c); } static inline void position_start (hb_buffer_t *buffer); static inline void position_finish (hb_buffer_t *buffer); inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!GSUBGPOS::sanitize (c))) return false; + if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); OffsetTo &list = CastR > (lookupList); - return list.sanitize (c, this); + return TRACE_RETURN (list.sanitize (c, this)); } public: DEFINE_SIZE_STATIC (10); @@ -1590,8 +1501,8 @@ GPOS::position_finish (hb_buffer_t *buffer) for (unsigned int i = 0; i < len; i++) fix_mark_attachment (pos, i, direction); - HB_BUFFER_DEALLOCATE_VAR (buffer, lig_comp); - HB_BUFFER_DEALLOCATE_VAR (buffer, lig_id); + HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); + HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props); HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache); } @@ -1601,16 +1512,16 @@ GPOS::position_finish (hb_buffer_t *buffer) inline bool ExtensionPos::apply (hb_apply_context_t *c) const { TRACE_APPLY (); - return get_subtable ().apply (c, get_type ()); + return TRACE_RETURN (get_subtable ().apply (c, get_type ())); } inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (unlikely (!Extension::sanitize (c))) return false; + if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); unsigned int offset = get_offset (); - if (unlikely (!offset)) return true; - return StructAtOffset (this, offset).sanitize (c, get_type ()); + if (unlikely (!offset)) return TRACE_RETURN (true); + return TRACE_RETURN (StructAtOffset (this, offset).sanitize (c, get_type ())); } static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) @@ -1621,10 +1532,10 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i if (unlikely (c->nesting_level_left == 0)) return false; - if (unlikely (c->context_length < 1)) - return false; - - return l.apply_once (c->font, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1); + hb_apply_context_t new_c (*c); + new_c.nesting_level_left--; + new_c.set_lookup (l); + return l.apply_once (&new_c); } @@ -1632,6 +1543,5 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i #undef cursive_chain -HB_END_DECLS -#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */ +#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */ diff --git a/src/hb-ot-layout-gsub-private.hh b/src/hb-ot-layout-gsub-private.hh deleted file mode 100644 index aca45ff..0000000 --- a/src/hb-ot-layout-gsub-private.hh +++ /dev/null @@ -1,947 +0,0 @@ -/* - * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2010 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH -#define HB_OT_LAYOUT_GSUB_PRIVATE_HH - -#include "hb-ot-layout-gsubgpos-private.hh" - -HB_BEGIN_DECLS - - -struct SingleSubstFormat1 -{ - friend struct SingleSubst; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; - unsigned int index = (this+coverage) (glyph_id); - if (likely (index == NOT_COVERED)) - return false; - - glyph_id += deltaGlyphID; - c->replace_glyph (glyph_id); - - return true; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && deltaGlyphID.sanitize (c); - } - - private: - USHORT format; /* Format identifier--format = 1 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of Substitution table */ - SHORT deltaGlyphID; /* Add to original GlyphID to get - * substitute GlyphID */ - public: - DEFINE_SIZE_STATIC (6); -}; - -struct SingleSubstFormat2 -{ - friend struct SingleSubst; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; - unsigned int index = (this+coverage) (glyph_id); - if (likely (index == NOT_COVERED)) - return false; - - if (unlikely (index >= substitute.len)) - return false; - - glyph_id = substitute[index]; - c->replace_glyph (glyph_id); - - return true; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && substitute.sanitize (c); - } - - private: - USHORT format; /* Format identifier--format = 2 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of Substitution table */ - ArrayOf - substitute; /* Array of substitute - * GlyphIDs--ordered by Coverage Index */ - public: - DEFINE_SIZE_ARRAY (6, substitute); -}; - -struct SingleSubst -{ - friend struct SubstLookupSubTable; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return u.format1.apply (c); - case 2: return u.format2.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; - switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT format; /* Format identifier */ - SingleSubstFormat1 format1; - SingleSubstFormat2 format2; - } u; -}; - - -struct Sequence -{ - friend struct MultipleSubstFormat1; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - if (unlikely (!substitute.len)) - return false; - - if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) - c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH); - c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array); - - return true; - } - - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return substitute.sanitize (c); - } - - private: - ArrayOf - substitute; /* String of GlyphIDs to substitute */ - public: - DEFINE_SIZE_ARRAY (2, substitute); -}; - -struct MultipleSubstFormat1 -{ - friend struct MultipleSubst; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; - - return (this+sequence[index]).apply (c); - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && sequence.sanitize (c, this); - } - - private: - USHORT format; /* Format identifier--format = 1 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of Substitution table */ - OffsetArrayOf - sequence; /* Array of Sequence tables - * ordered by Coverage Index */ - public: - DEFINE_SIZE_ARRAY (6, sequence); -}; - -struct MultipleSubst -{ - friend struct SubstLookupSubTable; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; - switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT format; /* Format identifier */ - MultipleSubstFormat1 format1; - } u; -}; - - -typedef ArrayOf AlternateSet; /* Array of alternate GlyphIDs--in - * arbitrary order */ - -struct AlternateSubstFormat1 -{ - friend struct AlternateSubst; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; - hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask; - hb_mask_t lookup_mask = c->lookup_mask; - - unsigned int index = (this+coverage) (glyph_id); - if (likely (index == NOT_COVERED)) - return false; - - const AlternateSet &alt_set = this+alternateSet[index]; - - if (unlikely (!alt_set.len)) - return false; - - /* Note: This breaks badly if two features enabled this lookup together. */ - unsigned int shift = _hb_ctz (lookup_mask); - unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); - - if (unlikely (alt_index > alt_set.len || alt_index == 0)) - return false; - - glyph_id = alt_set[alt_index - 1]; - - c->replace_glyph (glyph_id); - - return true; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && alternateSet.sanitize (c, this); - } - - private: - USHORT format; /* Format identifier--format = 1 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of Substitution table */ - OffsetArrayOf - alternateSet; /* Array of AlternateSet tables - * ordered by Coverage Index */ - public: - DEFINE_SIZE_ARRAY (6, alternateSet); -}; - -struct AlternateSubst -{ - friend struct SubstLookupSubTable; - - private: - - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; - switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT format; /* Format identifier */ - AlternateSubstFormat1 format1; - } u; -}; - - -struct Ligature -{ - friend struct LigatureSet; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - unsigned int i, j; - unsigned int count = component.len; - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (count < 2 || c->buffer->idx + count > end)) - return false; - - bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); - bool found_non_mark = false; - - for (i = 1, j = c->buffer->idx + 1; i < count; i++, j++) - { - unsigned int property; - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property)) - { - if (unlikely (j + count - i == end)) - return false; - j++; - } - - found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); - - if (likely (c->buffer->info[j].codepoint != component[i])) - return false; - } - - if (first_was_mark && found_non_mark) - c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE); - - /* Allocate new ligature id */ - unsigned int lig_id = allocate_lig_id (c->buffer); - c->buffer->info[c->buffer->idx].lig_comp() = 0; - c->buffer->info[c->buffer->idx].lig_id() = lig_id; - - if (j == c->buffer->idx + i) /* No input glyphs skipped */ - { - c->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph); - } - else - { - c->replace_glyph (ligGlyph); - - /* Now we must do a second loop to copy the skipped glyphs to - `out' and assign component values to it. We start with the - glyph after the first component. Glyphs between component - i and i+1 belong to component i. Together with the lig_id - value it is later possible to check whether a specific - component value really belongs to a given ligature. */ - - for (i = 1; i < count; i++) - { - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL)) - { - c->buffer->info[c->buffer->idx].lig_comp() = i; - c->buffer->info[c->buffer->idx].lig_id() = lig_id; - c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint); - } - - /* Skip the base glyph */ - c->buffer->idx++; - } - } - - return true; - } - - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return ligGlyph.sanitize (c) - && component.sanitize (c); - } - - private: - GlyphID ligGlyph; /* GlyphID of ligature to substitute */ - HeadlessArrayOf - component; /* Array of component GlyphIDs--start - * with the second component--ordered - * in writing direction */ - public: - DEFINE_SIZE_ARRAY (4, component); -}; - -struct LigatureSet -{ - friend struct LigatureSubstFormat1; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - { - const Ligature &lig = this+ligature[i]; - if (lig.apply (c)) - return true; - } - - return false; - } - - public: - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return ligature.sanitize (c, this); - } - - private: - OffsetArrayOf - ligature; /* Array LigatureSet tables - * ordered by preference */ - public: - DEFINE_SIZE_ARRAY (2, ligature); -}; - -struct LigatureSubstFormat1 -{ - friend struct LigatureSubst; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; - - unsigned int index = (this+coverage) (glyph_id); - if (likely (index == NOT_COVERED)) - return false; - - const LigatureSet &lig_set = this+ligatureSet[index]; - return lig_set.apply (c); - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && ligatureSet.sanitize (c, this); - } - - private: - USHORT format; /* Format identifier--format = 1 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of Substitution table */ - OffsetArrayOf - ligatureSet; /* Array LigatureSet tables - * ordered by Coverage Index */ - public: - DEFINE_SIZE_ARRAY (6, ligatureSet); -}; - -struct LigatureSubst -{ - friend struct SubstLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; - switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT format; /* Format identifier */ - LigatureSubstFormat1 format1; - } u; -}; - - -HB_BEGIN_DECLS -static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); -HB_END_DECLS - -struct ContextSubst : Context -{ - friend struct SubstLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return Context::apply (c, substitute_lookup); - } -}; - -struct ChainContextSubst : ChainContext -{ - friend struct SubstLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - return ChainContext::apply (c, substitute_lookup); - } -}; - - -struct ExtensionSubst : Extension -{ - friend struct SubstLookupSubTable; - friend struct SubstLookup; - - private: - inline const struct SubstLookupSubTable& get_subtable (void) const - { - unsigned int offset = get_offset (); - if (unlikely (!offset)) return Null(SubstLookupSubTable); - return StructAtOffset (this, offset); - } - - inline bool apply (hb_apply_context_t *c) const; - - inline bool sanitize (hb_sanitize_context_t *c); - - inline bool is_reverse (void) const; -}; - - -struct ReverseChainSingleSubstFormat1 -{ - friend struct ReverseChainSingleSubst; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - if (unlikely (c->context_length != NO_CONTEXT)) - return false; /* No chaining to this type */ - - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; - - const OffsetArrayOf &lookahead = StructAfter > (backtrack); - const ArrayOf &substitute = StructAfter > (lookahead); - - if (match_backtrack (c, - backtrack.len, (USHORT *) backtrack.array, - match_coverage, this) && - match_lookahead (c, - lookahead.len, (USHORT *) lookahead.array, - match_coverage, this, - 1)) - { - c->buffer->info[c->buffer->idx].codepoint = substitute[index]; - c->buffer->idx--; /* Reverse! */ - return true; - } - - return false; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!(coverage.sanitize (c, this) - && backtrack.sanitize (c, this))) - return false; - OffsetArrayOf &lookahead = StructAfter > (backtrack); - if (!lookahead.sanitize (c, this)) - return false; - ArrayOf &substitute = StructAfter > (lookahead); - return substitute.sanitize (c); - } - - private: - USHORT format; /* Format identifier--format = 1 */ - OffsetTo - coverage; /* Offset to Coverage table--from - * beginning of table */ - OffsetArrayOf - backtrack; /* Array of coverage tables - * in backtracking sequence, in glyph - * sequence order */ - OffsetArrayOf - lookaheadX; /* Array of coverage tables - * in lookahead sequence, in glyph - * sequence order */ - ArrayOf - substituteX; /* Array of substitute - * GlyphIDs--ordered by Coverage Index */ - public: - DEFINE_SIZE_MIN (10); -}; - -struct ReverseChainSingleSubst -{ - friend struct SubstLookupSubTable; - - private: - inline bool apply (hb_apply_context_t *c) const - { - TRACE_APPLY (); - switch (u.format) { - case 1: return u.format1.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; - switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT format; /* Format identifier */ - ReverseChainSingleSubstFormat1 format1; - } u; -}; - - - -/* - * SubstLookup - */ - -struct SubstLookupSubTable -{ - friend struct SubstLookup; - - enum { - Single = 1, - Multiple = 2, - Alternate = 3, - Ligature = 4, - Context = 5, - ChainContext = 6, - Extension = 7, - ReverseChainSingle = 8 - }; - - inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const - { - TRACE_APPLY (); - switch (lookup_type) { - case Single: return u.single.apply (c); - case Multiple: return u.multiple.apply (c); - case Alternate: return u.alternate.apply (c); - case Ligature: return u.ligature.apply (c); - case Context: return u.c.apply (c); - case ChainContext: return u.chainContext.apply (c); - case Extension: return u.extension.apply (c); - case ReverseChainSingle: return u.reverseChainContextSingle.apply (c); - default:return false; - } - } - - inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { - TRACE_SANITIZE (); - switch (lookup_type) { - case Single: return u.single.sanitize (c); - case Multiple: return u.multiple.sanitize (c); - case Alternate: return u.alternate.sanitize (c); - case Ligature: return u.ligature.sanitize (c); - case Context: return u.c.sanitize (c); - case ChainContext: return u.chainContext.sanitize (c); - case Extension: return u.extension.sanitize (c); - case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c); - default:return true; - } - } - - private: - union { - USHORT sub_format; - SingleSubst single; - MultipleSubst multiple; - AlternateSubst alternate; - LigatureSubst ligature; - ContextSubst c; - ChainContextSubst chainContext; - ExtensionSubst extension; - ReverseChainSingleSubst reverseChainContextSingle; - } u; - public: - DEFINE_SIZE_UNION (2, sub_format); -}; - - -struct SubstLookup : Lookup -{ - inline const SubstLookupSubTable& get_subtable (unsigned int i) const - { return this+CastR > (subTable)[i]; } - - inline static bool lookup_type_is_reverse (unsigned int lookup_type) - { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } - - inline bool is_reverse (void) const - { - unsigned int type = get_type (); - if (unlikely (type == SubstLookupSubTable::Extension)) - return CastR (get_subtable(0)).is_reverse (); - return lookup_type_is_reverse (type); - } - - - inline bool apply_once (hb_face_t *face, - hb_buffer_t *buffer, - hb_mask_t lookup_mask, - unsigned int context_length, - unsigned int nesting_level_left) const - { - unsigned int lookup_type = get_type (); - hb_apply_context_t c[1] = {{0}}; - - c->face = face; - c->buffer = buffer; - c->direction = buffer->props.direction; - c->lookup_mask = lookup_mask; - c->context_length = context_length; - c->nesting_level_left = nesting_level_left; - c->lookup_props = get_props (); - - if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property)) - return false; - - if (unlikely (lookup_type == SubstLookupSubTable::Extension)) - { - /* The spec says all subtables should have the same type. - * This is specially important if one has a reverse type! - * - * This is rather slow to do this here for every glyph, - * but it's easiest, and who uses extension lookups anyway?!*/ - unsigned int count = get_subtable_count (); - unsigned int type = get_subtable(0).u.extension.get_type (); - for (unsigned int i = 1; i < count; i++) - if (get_subtable(i).u.extension.get_type () != type) - return false; - } - - unsigned int count = get_subtable_count (); - for (unsigned int i = 0; i < count; i++) - if (get_subtable (i).apply (c, lookup_type)) - return true; - - return false; - } - - inline bool apply_string (hb_face_t *face, - hb_buffer_t *buffer, - hb_mask_t mask) const - { - bool ret = false; - - if (unlikely (!buffer->len)) - return false; - - if (likely (!is_reverse ())) - { - /* in/out forward substitution */ - buffer->clear_output (); - buffer->idx = 0; - while (buffer->idx < buffer->len) - { - if ((buffer->info[buffer->idx].mask & mask) && - apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) - ret = true; - else - buffer->next_glyph (); - - } - if (ret) - buffer->swap_buffers (); - } - else - { - /* in-place backward substitution */ - buffer->idx = buffer->len - 1; - do - { - if ((buffer->info[buffer->idx].mask & mask) && - apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) - ret = true; - else - buffer->idx--; - - } - while ((int) buffer->idx >= 0); - } - - return ret; - } - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (unlikely (!Lookup::sanitize (c))) return false; - OffsetArrayOf &list = CastR > (subTable); - return list.sanitize (c, this, get_type ()); - } -}; - -typedef OffsetListOf SubstLookupList; - -/* - * GSUB - */ - -struct GSUB : GSUBGPOS -{ - static const hb_tag_t Tag = HB_OT_TAG_GSUB; - - inline const SubstLookup& get_lookup (unsigned int i) const - { return CastR (GSUBGPOS::get_lookup (i)); } - - inline bool substitute_lookup (hb_face_t *face, - hb_buffer_t *buffer, - unsigned int lookup_index, - hb_mask_t mask) const - { return get_lookup (lookup_index).apply_string (face, buffer, mask); } - - static inline void substitute_start (hb_buffer_t *buffer); - static inline void substitute_finish (hb_buffer_t *buffer); - - inline bool sanitize (hb_sanitize_context_t *c) { - TRACE_SANITIZE (); - if (unlikely (!GSUBGPOS::sanitize (c))) return false; - OffsetTo &list = CastR > (lookupList); - return list.sanitize (c, this); - } - public: - DEFINE_SIZE_STATIC (10); -}; - - -void -GSUB::substitute_start (hb_buffer_t *buffer) -{ - HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); - HB_BUFFER_ALLOCATE_VAR (buffer, lig_id); - HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp); - - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0; -} - -void -GSUB::substitute_finish (hb_buffer_t *buffer) -{ -} - - -/* Out-of-class implementation for methods recursing */ - -inline bool ExtensionSubst::apply (hb_apply_context_t *c) const -{ - TRACE_APPLY (); - return get_subtable ().apply (c, get_type ()); -} - -inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) -{ - TRACE_SANITIZE (); - if (unlikely (!Extension::sanitize (c))) return false; - unsigned int offset = get_offset (); - if (unlikely (!offset)) return true; - return StructAtOffset (this, offset).sanitize (c, get_type ()); -} - -inline bool ExtensionSubst::is_reverse (void) const -{ - unsigned int type = get_type (); - if (unlikely (type == SubstLookupSubTable::Extension)) - return CastR (get_subtable()).is_reverse (); - return SubstLookup::lookup_type_is_reverse (type); -} - -static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) -{ - const GSUB &gsub = *(c->face->ot_layout->gsub); - const SubstLookup &l = gsub.get_lookup (lookup_index); - - if (unlikely (c->nesting_level_left == 0)) - return false; - - if (unlikely (c->context_length < 1)) - return false; - - return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1); -} - - -HB_END_DECLS - -#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */ diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh new file mode 100644 index 0000000..f6a7575 --- /dev/null +++ b/src/hb-ot-layout-gsub-table.hh @@ -0,0 +1,1253 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH +#define HB_OT_LAYOUT_GSUB_TABLE_HH + +#include "hb-ot-layout-gsubgpos-private.hh" + + + +struct SingleSubstFormat1 +{ + friend struct SingleSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + hb_codepoint_t glyph_id = iter.get_glyph (); + if (c->glyphs->has (glyph_id)) + c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF); + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + return (this+coverage) (glyph_id) != NOT_COVERED; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage) (glyph_id); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + /* According to the Adobe Annotated OpenType Suite, result is always + * limited to 16bit. */ + glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF; + c->replace_glyph (glyph_id); + + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); + } + + private: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + SHORT deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct SingleSubstFormat2 +{ + friend struct SingleSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + if (c->glyphs->has (iter.get_glyph ())) + c->glyphs->add (substitute[iter.get_coverage ()]); + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + return (this+coverage) (glyph_id) != NOT_COVERED; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage) (glyph_id); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + if (unlikely (index >= substitute.len)) return TRACE_RETURN (false); + + glyph_id = substitute[index]; + c->replace_glyph (glyph_id); + + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c)); + } + + private: + USHORT format; /* Format identifier--format = 2 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + ArrayOf + substitute; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, substitute); +}; + +struct SingleSubst +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c); break; + case 2: u.format2.closure (c); break; + default: break; + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.would_apply (glyph_id); + case 2: return u.format2.would_apply (glyph_id); + default:return false; + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.apply (c)); + case 2: return TRACE_RETURN (u.format2.apply (c)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + private: + union { + USHORT format; /* Format identifier */ + SingleSubstFormat1 format1; + SingleSubstFormat2 format2; + } u; +}; + + +struct Sequence +{ + friend struct MultipleSubstFormat1; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + unsigned int count = substitute.len; + for (unsigned int i = 0; i < count; i++) + c->glyphs->add (substitute[i]); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + if (unlikely (!substitute.len)) return TRACE_RETURN (false); + + unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0; + c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass); + + return TRACE_RETURN (true); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (substitute.sanitize (c)); + } + + private: + ArrayOf + substitute; /* String of GlyphIDs to substitute */ + public: + DEFINE_SIZE_ARRAY (2, substitute); +}; + +struct MultipleSubstFormat1 +{ + friend struct MultipleSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + if (c->glyphs->has (iter.get_glyph ())) + (this+sequence[iter.get_coverage ()]).closure (c); + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + return (this+coverage) (glyph_id) != NOT_COVERED; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + return TRACE_RETURN ((this+sequence[index]).apply (c)); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this)); + } + + private: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + OffsetArrayOf + sequence; /* Array of Sequence tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, sequence); +}; + +struct MultipleSubst +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c); break; + default: break; + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.would_apply (glyph_id); + default:return false; + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + private: + union { + USHORT format; /* Format identifier */ + MultipleSubstFormat1 format1; + } u; +}; + + +typedef ArrayOf AlternateSet; /* Array of alternate GlyphIDs--in + * arbitrary order */ + +struct AlternateSubstFormat1 +{ + friend struct AlternateSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + if (c->glyphs->has (iter.get_glyph ())) { + const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()]; + unsigned int count = alt_set.len; + for (unsigned int i = 0; i < count; i++) + c->glyphs->add (alt_set[i]); + } + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + return (this+coverage) (glyph_id) != NOT_COVERED; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + + unsigned int index = (this+coverage) (glyph_id); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + const AlternateSet &alt_set = this+alternateSet[index]; + + if (unlikely (!alt_set.len)) return TRACE_RETURN (false); + + hb_mask_t glyph_mask = c->buffer->cur().mask; + hb_mask_t lookup_mask = c->lookup_mask; + + /* Note: This breaks badly if two features enabled this lookup together. */ + unsigned int shift = _hb_ctz (lookup_mask); + unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); + + if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false); + + glyph_id = alt_set[alt_index - 1]; + + c->replace_glyph (glyph_id); + + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); + } + + private: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + OffsetArrayOf + alternateSet; /* Array of AlternateSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, alternateSet); +}; + +struct AlternateSubst +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c); break; + default: break; + } + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.would_apply (glyph_id); + default:return false; + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + private: + union { + USHORT format; /* Format identifier */ + AlternateSubstFormat1 format1; + } u; +}; + + +struct Ligature +{ + friend struct LigatureSet; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + unsigned int count = component.len; + for (unsigned int i = 1; i < count; i++) + if (!c->glyphs->has (component[i])) + return; + c->glyphs->add (ligGlyph); + } + + inline bool would_apply (hb_codepoint_t second) const + { + return component.len == 2 && component[1] == second; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + unsigned int count = component.len; + if (unlikely (count < 2)) return TRACE_RETURN (false); + + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1); + if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); + + bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); + bool found_non_mark = false; + + for (unsigned int i = 1; i < count; i++) + { + unsigned int property; + + if (!skippy_iter.next (&property)) return TRACE_RETURN (false); + + found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); + + if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false); + } + + unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0; + + /* Allocate new ligature id */ + unsigned int lig_id = allocate_lig_id (c->buffer); + set_lig_props (c->buffer->cur(), lig_id, 0); + + if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */ + { + c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass); + } + else + { + c->replace_glyph (ligGlyph); + + /* Now we must do a second loop to copy the skipped glyphs to + `out' and assign component values to it. We start with the + glyph after the first component. Glyphs between component + i and i+1 belong to component i. Together with the lig_id + value it is later possible to check whether a specific + component value really belongs to a given ligature. */ + + for (unsigned int i = 1; i < count; i++) + { + while (c->should_mark_skip_current_glyph ()) + { + set_lig_props (c->buffer->cur(), lig_id, i); + c->replace_glyph (c->buffer->cur().codepoint); + } + + /* Skip the base glyph */ + c->buffer->idx++; + } + } + + return TRACE_RETURN (true); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c)); + } + + private: + GlyphID ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf + component; /* Array of component GlyphIDs--start + * with the second component--ordered + * in writing direction */ + public: + DEFINE_SIZE_ARRAY (4, component); +}; + +struct LigatureSet +{ + friend struct LigatureSubstFormat1; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + (this+ligature[i]).closure (c); + } + + inline bool would_apply (hb_codepoint_t second) const + { + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + { + const Ligature &lig = this+ligature[i]; + if (lig.would_apply (second)) + return true; + } + return false; + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + { + const Ligature &lig = this+ligature[i]; + if (lig.apply (c)) return TRACE_RETURN (true); + } + + return TRACE_RETURN (false); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (ligature.sanitize (c, this)); + } + + private: + OffsetArrayOf + ligature; /* Array LigatureSet tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, ligature); +}; + +struct LigatureSubstFormat1 +{ + friend struct LigatureSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + if (c->glyphs->has (iter.get_glyph ())) + (this+ligatureSet[iter.get_coverage ()]).closure (c); + } + } + + inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + { + unsigned int index; + return (index = (this+coverage) (first)) != NOT_COVERED && + (this+ligatureSet[index]).would_apply (second); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + + unsigned int index = (this+coverage) (glyph_id); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + const LigatureSet &lig_set = this+ligatureSet[index]; + return TRACE_RETURN (lig_set.apply (c)); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); + } + + private: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + OffsetArrayOf + ligatureSet; /* Array LigatureSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (6, ligatureSet); +}; + +struct LigatureSubst +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c); break; + default: break; + } + } + + inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + { + switch (u.format) { + case 1: return u.format1.would_apply (first, second); + default:return false; + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + private: + union { + USHORT format; /* Format identifier */ + LigatureSubstFormat1 format1; + } u; +}; + + +static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); +static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index); + +struct ContextSubst : Context +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + return Context::closure (c, closure_lookup); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + return TRACE_RETURN (Context::apply (c, substitute_lookup)); + } +}; + +struct ChainContextSubst : ChainContext +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + return ChainContext::closure (c, closure_lookup); + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + return TRACE_RETURN (ChainContext::apply (c, substitute_lookup)); + } +}; + + +struct ExtensionSubst : Extension +{ + friend struct SubstLookupSubTable; + friend struct SubstLookup; + + private: + inline const struct SubstLookupSubTable& get_subtable (void) const + { + unsigned int offset = get_offset (); + if (unlikely (!offset)) return Null(SubstLookupSubTable); + return StructAtOffset (this, offset); + } + + inline void closure (hb_closure_context_t *c) const; + inline bool would_apply (hb_codepoint_t glyph_id) const; + inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const; + + inline bool apply (hb_apply_context_t *c) const; + + inline bool sanitize (hb_sanitize_context_t *c); + + inline bool is_reverse (void) const; +}; + + +struct ReverseChainSingleSubstFormat1 +{ + friend struct ReverseChainSingleSubst; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + const OffsetArrayOf &lookahead = StructAfter > (backtrack); + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+backtrack[i]).intersects (c->glyphs)) + return; + + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+lookahead[i]).intersects (c->glyphs)) + return; + + const ArrayOf &substitute = StructAfter > (lookahead); + Coverage::Iter iter; + for (iter.init (this+coverage); iter.more (); iter.next ()) { + if (c->glyphs->has (iter.get_glyph ())) + c->glyphs->add (substitute[iter.get_coverage ()]); + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL)) + return TRACE_RETURN (false); /* No chaining to this type */ + + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); + + const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const ArrayOf &substitute = StructAfter > (lookahead); + + if (match_backtrack (c, + backtrack.len, (USHORT *) backtrack.array, + match_coverage, this) && + match_lookahead (c, + lookahead.len, (USHORT *) lookahead.array, + match_coverage, this, + 1)) + { + c->buffer->cur().codepoint = substitute[index]; + c->buffer->idx--; /* Reverse! */ + return TRACE_RETURN (true); + } + + return TRACE_RETURN (false); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) + return TRACE_RETURN (false); + OffsetArrayOf &lookahead = StructAfter > (backtrack); + if (!lookahead.sanitize (c, this)) + return TRACE_RETURN (false); + ArrayOf &substitute = StructAfter > (lookahead); + return TRACE_RETURN (substitute.sanitize (c)); + } + + private: + USHORT format; /* Format identifier--format = 1 */ + OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of table */ + OffsetArrayOf + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + OffsetArrayOf + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + ArrayOf + substituteX; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + public: + DEFINE_SIZE_MIN (10); +}; + +struct ReverseChainSingleSubst +{ + friend struct SubstLookupSubTable; + + private: + + inline void closure (hb_closure_context_t *c) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c); break; + default: break; + } + } + + inline bool apply (hb_apply_context_t *c) const + { + TRACE_APPLY (); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.apply (c)); + default:return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (!u.format.sanitize (c)) return TRACE_RETURN (false); + switch (u.format) { + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); + } + } + + private: + union { + USHORT format; /* Format identifier */ + ReverseChainSingleSubstFormat1 format1; + } u; +}; + + + +/* + * SubstLookup + */ + +struct SubstLookupSubTable +{ + friend struct SubstLookup; + + enum Type { + Single = 1, + Multiple = 2, + Alternate = 3, + Ligature = 4, + Context = 5, + ChainContext = 6, + Extension = 7, + ReverseChainSingle = 8 + }; + + inline void closure (hb_closure_context_t *c, + unsigned int lookup_type) const + { + TRACE_CLOSURE (); + switch (lookup_type) { + case Single: u.single.closure (c); break; + case Multiple: u.multiple.closure (c); break; + case Alternate: u.alternate.closure (c); break; + case Ligature: u.ligature.closure (c); break; + case Context: u.c.closure (c); break; + case ChainContext: u.chainContext.closure (c); break; + case Extension: u.extension.closure (c); break; + case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break; + default: break; + } + } + + inline bool would_apply (hb_codepoint_t glyph_id, + unsigned int lookup_type) const + { + switch (lookup_type) { + case Single: return u.single.would_apply (glyph_id); + case Multiple: return u.multiple.would_apply (glyph_id); + case Alternate: return u.alternate.would_apply (glyph_id); + case Extension: return u.extension.would_apply (glyph_id); + default: return false; + } + } + inline bool would_apply (hb_codepoint_t first, + hb_codepoint_t second, + unsigned int lookup_type) const + { + switch (lookup_type) { + case Ligature: return u.ligature.would_apply (first, second); + case Extension: return u.extension.would_apply (first, second); + default: return false; + } + } + + inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const + { + TRACE_APPLY (); + switch (lookup_type) { + case Single: return TRACE_RETURN (u.single.apply (c)); + case Multiple: return TRACE_RETURN (u.multiple.apply (c)); + case Alternate: return TRACE_RETURN (u.alternate.apply (c)); + case Ligature: return TRACE_RETURN (u.ligature.apply (c)); + case Context: return TRACE_RETURN (u.c.apply (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); + case Extension: return TRACE_RETURN (u.extension.apply (c)); + case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c)); + default: return TRACE_RETURN (false); + } + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { + TRACE_SANITIZE (); + switch (lookup_type) { + case Single: return TRACE_RETURN (u.single.sanitize (c)); + case Multiple: return TRACE_RETURN (u.multiple.sanitize (c)); + case Alternate: return TRACE_RETURN (u.alternate.sanitize (c)); + case Ligature: return TRACE_RETURN (u.ligature.sanitize (c)); + case Context: return TRACE_RETURN (u.c.sanitize (c)); + case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); + case Extension: return TRACE_RETURN (u.extension.sanitize (c)); + case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c)); + default: return TRACE_RETURN (true); + } + } + + private: + union { + USHORT sub_format; + SingleSubst single; + MultipleSubst multiple; + AlternateSubst alternate; + LigatureSubst ligature; + ContextSubst c; + ChainContextSubst chainContext; + ExtensionSubst extension; + ReverseChainSingleSubst reverseChainContextSingle; + } u; + public: + DEFINE_SIZE_UNION (2, sub_format); +}; + + +struct SubstLookup : Lookup +{ + inline const SubstLookupSubTable& get_subtable (unsigned int i) const + { return this+CastR > (subTable)[i]; } + + inline static bool lookup_type_is_reverse (unsigned int lookup_type) + { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } + + inline bool is_reverse (void) const + { + unsigned int type = get_type (); + if (unlikely (type == SubstLookupSubTable::Extension)) + return CastR (get_subtable(0)).is_reverse (); + return lookup_type_is_reverse (type); + } + + inline void closure (hb_closure_context_t *c) const + { + unsigned int lookup_type = get_type (); + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) + get_subtable (i).closure (c, lookup_type); + } + + inline bool would_apply (hb_codepoint_t glyph_id) const + { + unsigned int lookup_type = get_type (); + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) + if (get_subtable (i).would_apply (glyph_id, lookup_type)) + return true; + return false; + } + inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const + { + unsigned int lookup_type = get_type (); + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) + if (get_subtable (i).would_apply (first, second, lookup_type)) + return true; + return false; + } + + inline bool apply_once (hb_apply_context_t *c) const + { + unsigned int lookup_type = get_type (); + + if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property)) + return false; + + if (unlikely (lookup_type == SubstLookupSubTable::Extension)) + { + /* The spec says all subtables should have the same type. + * This is specially important if one has a reverse type! + * + * This is rather slow to do this here for every glyph, + * but it's easiest, and who uses extension lookups anyway?!*/ + unsigned int type = get_subtable(0).u.extension.get_type (); + unsigned int count = get_subtable_count (); + for (unsigned int i = 1; i < count; i++) + if (get_subtable(i).u.extension.get_type () != type) + return false; + } + + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) + if (get_subtable (i).apply (c, lookup_type)) + return true; + + return false; + } + + inline bool apply_string (hb_apply_context_t *c) const + { + bool ret = false; + + if (unlikely (!c->buffer->len)) + return false; + + c->set_lookup (*this); + + if (likely (!is_reverse ())) + { + /* in/out forward substitution */ + c->buffer->clear_output (); + c->buffer->idx = 0; + while (c->buffer->idx < c->buffer->len) + { + if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) + ret = true; + else + c->buffer->next_glyph (); + + } + if (ret) + c->buffer->swap_buffers (); + } + else + { + /* in-place backward substitution */ + c->buffer->idx = c->buffer->len - 1; + do + { + if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) + ret = true; + else + c->buffer->idx--; + + } + while ((int) c->buffer->idx >= 0); + } + + return ret; + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); + OffsetArrayOf &list = CastR > (subTable); + return TRACE_RETURN (list.sanitize (c, this, get_type ())); + } +}; + +typedef OffsetListOf SubstLookupList; + +/* + * GSUB -- The Glyph Substitution Table + */ + +struct GSUB : GSUBGPOS +{ + static const hb_tag_t Tag = HB_OT_TAG_GSUB; + + inline const SubstLookup& get_lookup (unsigned int i) const + { return CastR (GSUBGPOS::get_lookup (i)); } + + inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const + { return get_lookup (lookup_index).apply_string (c); } + + static inline void substitute_start (hb_buffer_t *buffer); + static inline void substitute_finish (hb_buffer_t *buffer); + + inline void closure_lookup (hb_closure_context_t *c, + unsigned int lookup_index) const + { return get_lookup (lookup_index).closure (c); } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); + OffsetTo &list = CastR > (lookupList); + return TRACE_RETURN (list.sanitize (c, this)); + } + public: + DEFINE_SIZE_STATIC (10); +}; + + +void +GSUB::substitute_start (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); + HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0; +} + +void +GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED) +{ +} + + +/* Out-of-class implementation for methods recursing */ + +inline void ExtensionSubst::closure (hb_closure_context_t *c) const +{ + get_subtable ().closure (c, get_type ()); +} + +inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const +{ + return get_subtable ().would_apply (glyph_id, get_type ()); +} + +inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const +{ + return get_subtable ().would_apply (first, second, get_type ()); +} + +inline bool ExtensionSubst::apply (hb_apply_context_t *c) const +{ + TRACE_APPLY (); + return TRACE_RETURN (get_subtable ().apply (c, get_type ())); +} + +inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) +{ + TRACE_SANITIZE (); + if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); + unsigned int offset = get_offset (); + if (unlikely (!offset)) return TRACE_RETURN (true); + return TRACE_RETURN (StructAtOffset (this, offset).sanitize (c, get_type ())); +} + +inline bool ExtensionSubst::is_reverse (void) const +{ + unsigned int type = get_type (); + if (unlikely (type == SubstLookupSubTable::Extension)) + return CastR (get_subtable()).is_reverse (); + return SubstLookup::lookup_type_is_reverse (type); +} + +static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index) +{ + const GSUB &gsub = *(c->face->ot_layout->gsub); + const SubstLookup &l = gsub.get_lookup (lookup_index); + + if (unlikely (c->nesting_level_left == 0)) + return; + + c->nesting_level_left--; + l.closure (c); + c->nesting_level_left++; +} + +static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) +{ + const GSUB &gsub = *(c->face->ot_layout->gsub); + const SubstLookup &l = gsub.get_lookup (lookup_index); + + if (unlikely (c->nesting_level_left == 0)) + return false; + + hb_apply_context_t new_c (*c); + new_c.nesting_level_left--; + new_c.set_lookup (l); + return l.apply_once (&new_c); +} + + + +#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh index 3dcfb21..12cb694 100644 --- a/src/hb-ot-layout-gsubgpos-private.hh +++ b/src/hb-ot-layout-gsubgpos-private.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2010 Google, Inc. + * Copyright © 2010,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -30,84 +30,259 @@ #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH #include "hb-buffer-private.hh" -#include "hb-ot-layout-gdef-private.hh" +#include "hb-ot-layout-gdef-table.hh" -HB_BEGIN_DECLS -/* buffer var allocations */ -#define lig_id() var2.u8[2] /* unique ligature id */ -#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */ + +/* unique ligature id */ +/* component number in the ligature (0 = base) */ +static inline void +set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp) +{ + info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F); +} +static inline unsigned int +get_lig_id (hb_glyph_info_t &info) +{ + return info.lig_props() >> 4; +} +static inline unsigned int +get_lig_comp (hb_glyph_info_t &info) +{ + return info.lig_props() & 0x0F; +} static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) { - uint8_t lig_id = buffer->next_serial (); - if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */ + uint8_t lig_id = buffer->next_serial () & 0x0F; + if (unlikely (!lig_id)) + lig_id = allocate_lig_id (buffer); /* in case of overflow */ return lig_id; } +#ifndef HB_DEBUG_CLOSURE +#define HB_DEBUG_CLOSURE (HB_DEBUG+0) +#endif + +#define TRACE_CLOSURE() \ + hb_auto_trace_t trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, ""); + + +/* TODO Add TRACE_RETURN annotation for would_apply */ + + +struct hb_closure_context_t +{ + hb_face_t *face; + hb_set_t *glyphs; + unsigned int nesting_level_left; + unsigned int debug_depth; + + + hb_closure_context_t (hb_face_t *face_, + hb_set_t *glyphs_, + unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) : + face (face_), glyphs (glyphs_), + nesting_level_left (nesting_level_left_), + debug_depth (0) {} +}; + + + #ifndef HB_DEBUG_APPLY #define HB_DEBUG_APPLY (HB_DEBUG+0) #endif #define TRACE_APPLY() \ - hb_auto_trace_t trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC); + hb_auto_trace_t trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint); -HB_BEGIN_DECLS struct hb_apply_context_t { - unsigned int debug_depth; hb_font_t *font; hb_face_t *face; hb_buffer_t *buffer; hb_direction_t direction; hb_mask_t lookup_mask; - unsigned int context_length; unsigned int nesting_level_left; unsigned int lookup_props; unsigned int property; /* propety of first glyph */ + unsigned int debug_depth; - inline void replace_glyph (hb_codepoint_t glyph_index) const - { - clear_property (); - buffer->replace_glyph (glyph_index); + hb_apply_context_t (hb_font_t *font_, + hb_face_t *face_, + hb_buffer_t *buffer_, + hb_mask_t lookup_mask_) : + font (font_), face (face_), buffer (buffer_), + direction (buffer_->props.direction), + lookup_mask (lookup_mask_), + nesting_level_left (MAX_NESTING_LEVEL), + lookup_props (0), property (0), debug_depth (0) {} + + void set_lookup (const Lookup &l) { + lookup_props = l.get_props (); } - inline void replace_glyphs_be16 (unsigned int num_in, - unsigned int num_out, - const uint16_t *glyph_data_be) const + + struct mark_skipping_forward_iterator_t { - clear_property (); - buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be); - } + inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_, + unsigned int start_index_, + unsigned int num_items_, + bool context_match = false) + { + c = c_; + idx = start_index_; + num_items = num_items_; + mask = context_match ? -1 : c->lookup_mask; + syllable = context_match ? 0 : c->buffer->cur().syllable (); + end = c->buffer->len; + } + inline bool has_no_chance (void) const + { + return unlikely (num_items && idx + num_items >= end); + } + inline bool next (unsigned int *property_out, + unsigned int lookup_props) + { + assert (num_items > 0); + do + { + if (has_no_chance ()) + return false; + idx++; + } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out)); + num_items--; + return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ()); + } + inline bool next (unsigned int *property_out = NULL) + { + return next (property_out, c->lookup_props); + } - inline void guess_glyph_class (unsigned int klass) + unsigned int idx; + private: + hb_apply_context_t *c; + unsigned int num_items; + hb_mask_t mask; + uint8_t syllable; + unsigned int end; + }; + + struct mark_skipping_backward_iterator_t { - /* XXX if ! has gdef */ - buffer->info[buffer->idx].props_cache() = klass; + inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_, + unsigned int start_index_, + unsigned int num_items_, + hb_mask_t mask_ = 0, + bool match_syllable_ = true) + { + c = c_; + idx = start_index_; + num_items = num_items_; + mask = mask_ ? mask_ : c->lookup_mask; + syllable = match_syllable_ ? c->buffer->cur().syllable () : 0; + } + inline bool has_no_chance (void) const + { + return unlikely (idx < num_items); + } + inline bool prev (unsigned int *property_out, + unsigned int lookup_props) + { + assert (num_items > 0); + do + { + if (has_no_chance ()) + return false; + idx--; + } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out)); + num_items--; + return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ()); + } + inline bool prev (unsigned int *property_out = NULL) + { + return prev (property_out, c->lookup_props); + } + + unsigned int idx; + private: + hb_apply_context_t *c; + unsigned int num_items; + hb_mask_t mask; + uint8_t syllable; + }; + + inline bool should_mark_skip_current_glyph (void) const + { + return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL); } - private: - inline void clear_property (void) const + + + inline void replace_glyph (hb_codepoint_t glyph_index, + unsigned int klass = 0) const { - /* XXX if has gdef */ - buffer->info[buffer->idx].props_cache() = 0; + buffer->cur().props_cache() = klass; /*XXX if has gdef? */ + buffer->replace_glyph (glyph_index); + } + inline void replace_glyphs_be16 (unsigned int num_in, + unsigned int num_out, + const uint16_t *glyph_data_be, + unsigned int klass = 0) const + { + buffer->cur().props_cache() = klass; /* XXX if has gdef? */ + buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be); } }; +typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data); typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data); +typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index); typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index); -struct ContextFuncs +struct ContextClosureFuncs +{ + intersects_func_t intersects; + closure_lookup_func_t closure; +}; +struct ContextApplyFuncs { match_func_t match; apply_lookup_func_t apply; }; +static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED) +{ + return glyphs->has (value); +} +static inline bool intersects_class (hb_set_t *glyphs, const USHORT &value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return class_def.intersects_class (glyphs, value); +} +static inline bool intersects_coverage (hb_set_t *glyphs, const USHORT &value, const void *data) +{ + const OffsetTo &coverage = (const OffsetTo&)value; + return (data+coverage).intersects (glyphs); +} + +static inline bool intersects_array (hb_closure_context_t *c, + unsigned int count, + const USHORT values[], + intersects_func_t intersects_func, + const void *intersects_data) +{ + for (unsigned int i = 0; i < count; i++) + if (likely (!intersects_func (c->glyphs, values[i], intersects_data))) + return false; + return true; +} + static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED) { @@ -123,7 +298,7 @@ static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, co static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data) { const OffsetTo &coverage = (const OffsetTo&)value; - return (data+coverage) (glyph_id) != NOT_COVERED; + return (data+coverage).get_coverage (glyph_id) != NOT_COVERED; } @@ -132,27 +307,23 @@ static inline bool match_input (hb_apply_context_t *c, const USHORT input[], /* Array of input values--start with second glyph */ match_func_t match_func, const void *match_data, - unsigned int *context_length_out) + unsigned int *end_offset = NULL) { - unsigned int i, j; - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (c->buffer->idx + count > end)) + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1); + if (skippy_iter.has_no_chance ()) return false; - for (i = 1, j = c->buffer->idx + 1; i < count; i++, j++) + for (unsigned int i = 1; i < count; i++) { - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL)) - { - if (unlikely (j + count - i == end)) - return false; - j++; - } + if (!skippy_iter.next ()) + return false; - if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data))) + if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data))) return false; } - *context_length_out = j - c->buffer->idx; + if (end_offset) + *end_offset = skippy_iter.idx - c->buffer->idx + 1; return true; } @@ -163,19 +334,16 @@ static inline bool match_backtrack (hb_apply_context_t *c, match_func_t match_func, const void *match_data) { - if (unlikely (c->buffer->backtrack_len () < count)) + hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true); + if (skippy_iter.has_no_chance ()) return false; - for (unsigned int i = 0, j = c->buffer->backtrack_len () - 1; i < count; i++, j--) + for (unsigned int i = 0; i < count; i++) { - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[j], c->lookup_props, NULL)) - { - if (unlikely (j + 1 == count - i)) - return false; - j--; - } + if (!skippy_iter.prev ()) + return false; - if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data))) + if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data))) return false; } @@ -189,35 +357,29 @@ static inline bool match_lookahead (hb_apply_context_t *c, const void *match_data, unsigned int offset) { - unsigned int i, j; - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); - if (unlikely (c->buffer->idx + offset + count > end)) + hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true); + if (skippy_iter.has_no_chance ()) return false; - for (i = 0, j = c->buffer->idx + offset; i < count; i++, j++) + for (unsigned int i = 0; i < count; i++) { - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL)) - { - if (unlikely (j + count - i == end)) - return false; - j++; - } + if (!skippy_iter.next ()) + return false; - if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data))) + if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data))) return false; } return true; } -HB_END_DECLS struct LookupRecord { inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } USHORT sequenceIndex; /* Index into current glyph @@ -229,7 +391,14 @@ struct LookupRecord }; -HB_BEGIN_DECLS +static inline void closure_lookup (hb_closure_context_t *c, + unsigned int lookupCount, + const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ + closure_lookup_func_t closure_func) +{ + for (unsigned int i = 0; i < lookupCount; i++) + closure_func (c, lookupRecord->lookupListIndex); +} static inline bool apply_lookup (hb_apply_context_t *c, unsigned int count, /* Including the first glyph */ @@ -237,7 +406,7 @@ static inline bool apply_lookup (hb_apply_context_t *c, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ apply_lookup_func_t apply_func) { - unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length); + unsigned int end = c->buffer->len; if (unlikely (count == 0 || c->buffer->idx + count > end)) return false; @@ -250,12 +419,14 @@ static inline bool apply_lookup (hb_apply_context_t *c, */ for (unsigned int i = 0; i < count; /* NOP */) { - while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL)) + if (unlikely (c->buffer->idx == end)) + return true; + while (c->should_mark_skip_current_glyph ()) { - if (unlikely (c->buffer->idx == end)) - return true; /* No lookup applied for this index */ c->buffer->next_glyph (); + if (unlikely (c->buffer->idx == end)) + return true; } if (lookupCount && i == lookupRecord->sequenceIndex) @@ -287,30 +458,49 @@ static inline bool apply_lookup (hb_apply_context_t *c, return true; } -HB_END_DECLS /* Contextual lookups */ -struct ContextLookupContext +struct ContextClosureLookupContext { - ContextFuncs funcs; + ContextClosureFuncs funcs; + const void *intersects_data; +}; + +struct ContextApplyLookupContext +{ + ContextApplyFuncs funcs; const void *match_data; }; -static inline bool context_lookup (hb_apply_context_t *c, - unsigned int inputCount, /* Including the first glyph (not matched) */ - const USHORT input[], /* Array of input values--start with second glyph */ - unsigned int lookupCount, - const LookupRecord lookupRecord[], - ContextLookupContext &lookup_context) +static inline void context_closure_lookup (hb_closure_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ContextClosureLookupContext &lookup_context) +{ + if (intersects_array (c, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data)) + closure_lookup (c, + lookupCount, lookupRecord, + lookup_context.funcs.closure); +} + + +static inline bool context_apply_lookup (hb_apply_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ContextApplyLookupContext &lookup_context) { - hb_apply_context_t new_context = *c; return match_input (c, inputCount, input, - lookup_context.funcs.match, lookup_context.match_data, - &new_context.context_length) - && apply_lookup (&new_context, + lookup_context.funcs.match, lookup_context.match_data) + && apply_lookup (c, inputCount, lookupCount, lookupRecord, lookup_context.funcs.apply); @@ -321,14 +511,22 @@ struct Rule friend struct RuleSet; private: - inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const + + inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (); + const LookupRecord *lookupRecord = &StructAtOffset (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); + context_closure_lookup (c, + inputCount, input, + lookupCount, lookupRecord, + lookup_context); + } + + inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (); const LookupRecord *lookupRecord = &StructAtOffset (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); - return context_lookup (c, - inputCount, input, - lookupCount, lookupRecord, - lookup_context); + return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context)); } public: @@ -337,13 +535,13 @@ struct Rule return inputCount.sanitize (c) && lookupCount.sanitize (c) && c->check_range (input, - input[0].static_size * inputCount - + lookupRecordX[0].static_size * lookupCount); + input[0].static_size * inputCount + + lookupRecordX[0].static_size * lookupCount); } private: USHORT inputCount; /* Total number of glyphs in input - * glyph sequence--includes the first + * glyph sequence--includes the first * glyph */ USHORT lookupCount; /* Number of LookupRecords */ USHORT input[VAR]; /* Array of match inputs--start with @@ -356,22 +554,29 @@ struct Rule struct RuleSet { - inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const + inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).closure (c, lookup_context); + } + + inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (); unsigned int num_rules = rule.len; for (unsigned int i = 0; i < num_rules; i++) { if ((this+rule[i]).apply (c, lookup_context)) - return true; + return TRACE_RETURN (true); } - - return false; + return TRACE_RETURN (false); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return rule.sanitize (c, this); + return TRACE_RETURN (rule.sanitize (c, this)); } private: @@ -388,25 +593,44 @@ struct ContextFormat1 friend struct Context; private: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + + const Coverage &cov = (this+coverage); + + struct ContextClosureLookupContext lookup_context = { + {intersects_glyph, closure_func}, + NULL + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + if (cov.intersects_coverage (c->glyphs, i)) { + const RuleSet &rule_set = this+ruleSet[i]; + rule_set.closure (c, lookup_context); + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) - return false; + return TRACE_RETURN (false); const RuleSet &rule_set = this+ruleSet[index]; - struct ContextLookupContext lookup_context = { + struct ContextApplyLookupContext lookup_context = { {match_glyph, apply_func}, NULL }; - return rule_set.apply (c, lookup_context); + return TRACE_RETURN (rule_set.apply (c, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && ruleSet.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); } private: @@ -427,28 +651,47 @@ struct ContextFormat2 friend struct Context; private: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + if (!(this+coverage).intersects (c->glyphs)) + return; + + const ClassDef &class_def = this+classDef; + + struct ContextClosureLookupContext lookup_context = { + {intersects_class, closure_func}, + NULL + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + if (class_def.intersects_class (c->glyphs, i)) { + const RuleSet &rule_set = this+ruleSet[i]; + rule_set.closure (c, lookup_context); + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const ClassDef &class_def = this+classDef; - index = class_def (c->buffer->info[c->buffer->idx].codepoint); + index = class_def (c->buffer->cur().codepoint); const RuleSet &rule_set = this+ruleSet[index]; - struct ContextLookupContext lookup_context = { + struct ContextApplyLookupContext lookup_context = { {match_class, apply_func}, &class_def }; - return rule_set.apply (c, lookup_context); + return TRACE_RETURN (rule_set.apply (c, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && classDef.sanitize (c, this) - && ruleSet.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this)); } private: @@ -472,33 +715,47 @@ struct ContextFormat3 friend struct Context; private: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + if (!(this+coverage[0]).intersects (c->glyphs)) + return; + + const LookupRecord *lookupRecord = &StructAtOffset (coverage, coverage[0].static_size * glyphCount); + struct ContextClosureLookupContext lookup_context = { + {intersects_coverage, closure_func}, + this + }; + context_closure_lookup (c, + glyphCount, (const USHORT *) (coverage + 1), + lookupCount, lookupRecord, + lookup_context); + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); - unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const LookupRecord *lookupRecord = &StructAtOffset (coverage, coverage[0].static_size * glyphCount); - struct ContextLookupContext lookup_context = { + struct ContextApplyLookupContext lookup_context = { {match_coverage, apply_func}, this }; - return context_lookup (c, - glyphCount, (const USHORT *) (coverage + 1), - lookupCount, lookupRecord, - lookup_context); + return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!c->check_struct (this)) return false; + if (!c->check_struct (this)) return TRACE_RETURN (false); unsigned int count = glyphCount; - if (!c->check_array (coverage, coverage[0].static_size, count)) return false; + if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false); for (unsigned int i = 0; i < count; i++) - if (!coverage[i].sanitize (c, this)) return false; + if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false); LookupRecord *lookupRecord = &StructAtOffset (coverage, coverage[0].static_size * count); - return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount); + return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount)); } private: @@ -518,25 +775,37 @@ struct ContextFormat3 struct Context { protected: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c, closure_func); break; + case 2: u.format2.closure (c, closure_func); break; + case 3: u.format3.closure (c, closure_func); break; + default: break; + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c, apply_func); - case 2: return u.format2.apply (c, apply_func); - case 3: return u.format3.apply (c, apply_func); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c, apply_func)); + case 2: return TRACE_RETURN (u.format2.apply (c, apply_func)); + case 3: return TRACE_RETURN (u.format3.apply (c, apply_func)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - case 3: return u.format3.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + case 3: return TRACE_RETURN (u.format3.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -552,42 +821,67 @@ struct Context /* Chaining Contextual lookups */ -struct ChainContextLookupContext +struct ChainContextClosureLookupContext { - ContextFuncs funcs; + ContextClosureFuncs funcs; + const void *intersects_data[3]; +}; + +struct ChainContextApplyLookupContext +{ + ContextApplyFuncs funcs; const void *match_data[3]; }; -static inline bool chain_context_lookup (hb_apply_context_t *c, - unsigned int backtrackCount, - const USHORT backtrack[], - unsigned int inputCount, /* Including the first glyph (not matched) */ - const USHORT input[], /* Array of input values--start with second glyph */ - unsigned int lookaheadCount, - const USHORT lookahead[], - unsigned int lookupCount, - const LookupRecord lookupRecord[], - ChainContextLookupContext &lookup_context) +static inline void chain_context_closure_lookup (hb_closure_context_t *c, + unsigned int backtrackCount, + const USHORT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const USHORT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ChainContextClosureLookupContext &lookup_context) { - /* First guess */ - if (unlikely (c->buffer->backtrack_len () < backtrackCount || - c->buffer->idx + inputCount + lookaheadCount > c->buffer->len || - inputCount + lookaheadCount > c->context_length)) - return false; + if (intersects_array (c, + backtrackCount, backtrack, + lookup_context.funcs.intersects, lookup_context.intersects_data[0]) + && intersects_array (c, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data[1]) + && intersects_array (c, + lookaheadCount, lookahead, + lookup_context.funcs.intersects, lookup_context.intersects_data[2])) + closure_lookup (c, + lookupCount, lookupRecord, + lookup_context.funcs.closure); +} - hb_apply_context_t new_context = *c; +static inline bool chain_context_apply_lookup (hb_apply_context_t *c, + unsigned int backtrackCount, + const USHORT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const USHORT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const USHORT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ChainContextApplyLookupContext &lookup_context) +{ + unsigned int lookahead_offset; return match_backtrack (c, backtrackCount, backtrack, lookup_context.funcs.match, lookup_context.match_data[0]) && match_input (c, inputCount, input, lookup_context.funcs.match, lookup_context.match_data[1], - &new_context.context_length) + &lookahead_offset) && match_lookahead (c, lookaheadCount, lookahead, lookup_context.funcs.match, lookup_context.match_data[2], - new_context.context_length) - && apply_lookup (&new_context, + lookahead_offset) + && apply_lookup (c, inputCount, lookupCount, lookupRecord, lookup_context.funcs.apply); @@ -598,30 +892,44 @@ struct ChainRule friend struct ChainRuleSet; private: - inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const + + inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (); + const HeadlessArrayOf &input = StructAfter > (backtrack); + const ArrayOf &lookahead = StructAfter > (input); + const ArrayOf &lookup = StructAfter > (lookahead); + chain_context_closure_lookup (c, + backtrack.len, backtrack.array, + input.len, input.array, + lookahead.len, lookahead.array, + lookup.len, lookup.array, + lookup_context); + } + + inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (); const HeadlessArrayOf &input = StructAfter > (backtrack); const ArrayOf &lookahead = StructAfter > (input); const ArrayOf &lookup = StructAfter > (lookahead); - return chain_context_lookup (c, - backtrack.len, backtrack.array, - input.len, input.array, - lookahead.len, lookahead.array, - lookup.len, lookup.array, - lookup_context); + return TRACE_RETURN (chain_context_apply_lookup (c, + backtrack.len, backtrack.array, + input.len, input.array, + lookahead.len, lookahead.array, lookup.len, + lookup.array, lookup_context)); } public: inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!backtrack.sanitize (c)) return false; + if (!backtrack.sanitize (c)) return TRACE_RETURN (false); HeadlessArrayOf &input = StructAfter > (backtrack); - if (!input.sanitize (c)) return false; + if (!input.sanitize (c)) return TRACE_RETURN (false); ArrayOf &lookahead = StructAfter > (input); - if (!lookahead.sanitize (c)) return false; + if (!lookahead.sanitize (c)) return TRACE_RETURN (false); ArrayOf &lookup = StructAfter > (lookahead); - return lookup.sanitize (c); + return TRACE_RETURN (lookup.sanitize (c)); } private: @@ -644,22 +952,28 @@ struct ChainRule struct ChainRuleSet { - inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const + inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const + { + TRACE_CLOSURE (); + unsigned int num_rules = rule.len; + for (unsigned int i = 0; i < num_rules; i++) + (this+rule[i]).closure (c, lookup_context); + } + + inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (); unsigned int num_rules = rule.len; for (unsigned int i = 0; i < num_rules; i++) - { if ((this+rule[i]).apply (c, lookup_context)) - return true; - } + return TRACE_RETURN (true); - return false; + return TRACE_RETURN (false); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return rule.sanitize (c, this); + return TRACE_RETURN (rule.sanitize (c, this)); } private: @@ -675,25 +989,42 @@ struct ChainContextFormat1 friend struct ChainContext; private: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + const Coverage &cov = (this+coverage); + + struct ChainContextClosureLookupContext lookup_context = { + {intersects_glyph, closure_func}, + {NULL, NULL, NULL} + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + if (cov.intersects_coverage (c->glyphs, i)) { + const ChainRuleSet &rule_set = this+ruleSet[i]; + rule_set.closure (c, lookup_context); + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const ChainRuleSet &rule_set = this+ruleSet[index]; - struct ChainContextLookupContext lookup_context = { + struct ChainContextApplyLookupContext lookup_context = { {match_glyph, apply_func}, {NULL, NULL, NULL} }; - return rule_set.apply (c, lookup_context); + return TRACE_RETURN (rule_set.apply (c, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && ruleSet.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this)); } private: @@ -713,35 +1044,58 @@ struct ChainContextFormat2 friend struct ChainContext; private: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + if (!(this+coverage).intersects (c->glyphs)) + return; + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + struct ChainContextClosureLookupContext lookup_context = { + {intersects_class, closure_func}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + + unsigned int count = ruleSet.len; + for (unsigned int i = 0; i < count; i++) + if (input_class_def.intersects_class (c->glyphs, i)) { + const ChainRuleSet &rule_set = this+ruleSet[i]; + rule_set.closure (c, lookup_context); + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); - unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+coverage) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; - index = input_class_def (c->buffer->info[c->buffer->idx].codepoint); + index = input_class_def (c->buffer->cur().codepoint); const ChainRuleSet &rule_set = this+ruleSet[index]; - struct ChainContextLookupContext lookup_context = { + struct ChainContextApplyLookupContext lookup_context = { {match_class, apply_func}, {&backtrack_class_def, &input_class_def, &lookahead_class_def} }; - return rule_set.apply (c, lookup_context); + return TRACE_RETURN (rule_set.apply (c, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return coverage.sanitize (c, this) - && backtrackClassDef.sanitize (c, this) - && inputClassDef.sanitize (c, this) - && lookaheadClassDef.sanitize (c, this) - && ruleSet.sanitize (c, this); + return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) && + inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) && + ruleSet.sanitize (c, this)); } private: @@ -774,38 +1128,58 @@ struct ChainContextFormat3 private: + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + const OffsetArrayOf &input = StructAfter > (backtrack); + + if (!(this+input[0]).intersects (c->glyphs)) + return; + + const OffsetArrayOf &lookahead = StructAfter > (input); + const ArrayOf &lookup = StructAfter > (lookahead); + struct ChainContextClosureLookupContext lookup_context = { + {intersects_coverage, closure_func}, + {this, this, this} + }; + chain_context_closure_lookup (c, + backtrack.len, (const USHORT *) backtrack.array, + input.len, (const USHORT *) input.array + 1, + lookahead.len, (const USHORT *) lookahead.array, + lookup.len, lookup.array, + lookup_context); + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); const OffsetArrayOf &input = StructAfter > (backtrack); - unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint); - if (likely (index == NOT_COVERED)) - return false; + unsigned int index = (this+input[0]) (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); const OffsetArrayOf &lookahead = StructAfter > (input); const ArrayOf &lookup = StructAfter > (lookahead); - struct ChainContextLookupContext lookup_context = { + struct ChainContextApplyLookupContext lookup_context = { {match_coverage, apply_func}, {this, this, this} }; - return chain_context_lookup (c, - backtrack.len, (const USHORT *) backtrack.array, - input.len, (const USHORT *) input.array + 1, - lookahead.len, (const USHORT *) lookahead.array, - lookup.len, lookup.array, - lookup_context); + return TRACE_RETURN (chain_context_apply_lookup (c, + backtrack.len, (const USHORT *) backtrack.array, + input.len, (const USHORT *) input.array + 1, + lookahead.len, (const USHORT *) lookahead.array, + lookup.len, lookup.array, lookup_context)); } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!backtrack.sanitize (c, this)) return false; + if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false); OffsetArrayOf &input = StructAfter > (backtrack); - if (!input.sanitize (c, this)) return false; + if (!input.sanitize (c, this)) return TRACE_RETURN (false); OffsetArrayOf &lookahead = StructAfter > (input); - if (!lookahead.sanitize (c, this)) return false; + if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false); ArrayOf &lookup = StructAfter > (lookahead); - return lookup.sanitize (c); + return TRACE_RETURN (lookup.sanitize (c)); } private: @@ -832,25 +1206,37 @@ struct ChainContextFormat3 struct ChainContext { protected: + + inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const + { + TRACE_CLOSURE (); + switch (u.format) { + case 1: u.format1.closure (c, closure_func); break; + case 2: u.format2.closure (c, closure_func); break; + case 3: u.format3.closure (c, closure_func); break; + default: break; + } + } + inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const { TRACE_APPLY (); switch (u.format) { - case 1: return u.format1.apply (c, apply_func); - case 2: return u.format2.apply (c, apply_func); - case 3: return u.format3.apply (c, apply_func); - default:return false; + case 1: return TRACE_RETURN (u.format1.apply (c, apply_func)); + case 2: return TRACE_RETURN (u.format2.apply (c, apply_func)); + case 3: return TRACE_RETURN (u.format3.apply (c, apply_func)); + default:return TRACE_RETURN (false); } } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - case 2: return u.format2.sanitize (c); - case 3: return u.format3.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + case 2: return TRACE_RETURN (u.format2.sanitize (c)); + case 3: return TRACE_RETURN (u.format3.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -874,7 +1260,7 @@ struct ExtensionFormat1 inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this); + return TRACE_RETURN (c->check_struct (this)); } private: @@ -907,10 +1293,10 @@ struct Extension inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - if (!u.format.sanitize (c)) return false; + if (!u.format.sanitize (c)) return TRACE_RETURN (false); switch (u.format) { - case 1: return u.format1.sanitize (c); - default:return true; + case 1: return TRACE_RETURN (u.format1.sanitize (c)); + default:return TRACE_RETURN (true); } } @@ -964,10 +1350,10 @@ struct GSUBGPOS inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return version.sanitize (c) && likely (version.major == 1) - && scriptList.sanitize (c, this) - && featureList.sanitize (c, this) - && lookupList.sanitize (c, this); + return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) && + scriptList.sanitize (c, this) && + featureList.sanitize (c, this) && + lookupList.sanitize (c, this)); } protected: @@ -984,6 +1370,5 @@ struct GSUBGPOS }; -HB_END_DECLS #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh index ef24e8d..f860e7b 100644 --- a/src/hb-ot-layout-private.hh +++ b/src/hb-ot-layout-private.hh @@ -33,17 +33,14 @@ #include "hb-font-private.hh" #include "hb-buffer-private.hh" +#include "hb-ot-shape-complex-private.hh" -HB_BEGIN_DECLS /* * GDEF */ -/* buffer var allocations */ -#define props_cache() var1.u16[1] /* glyph_props cache */ - /* XXX cleanup */ typedef enum { HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0x0001, @@ -71,13 +68,6 @@ _hb_ot_layout_skip_mark (hb_face_t *face, unsigned int *property_out); -/* - * head - */ - -HB_INTERNAL unsigned int -_hb_ot_layout_get_upem (hb_face_t *face); - /* * hb_ot_layout_t @@ -88,12 +78,10 @@ struct hb_ot_layout_t hb_blob_t *gdef_blob; hb_blob_t *gsub_blob; hb_blob_t *gpos_blob; - hb_blob_t *head_blob; const struct GDEF *gdef; const struct GSUB *gsub; const struct GPOS *gpos; - const struct head *head; }; @@ -104,6 +92,5 @@ HB_INTERNAL void _hb_ot_layout_destroy (hb_ot_layout_t *layout); -HB_END_DECLS #endif /* HB_OT_LAYOUT_PRIVATE_HH */ diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index e7a1f98..0621f86 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -26,21 +26,17 @@ * Red Hat Author(s): Behdad Esfahbod */ -#define HB_OT_LAYOUT_CC - #include "hb-ot-layout-private.hh" -#include "hb-ot-layout-gdef-private.hh" -#include "hb-ot-layout-gsub-private.hh" -#include "hb-ot-layout-gpos-private.hh" -#include "hb-ot-head-private.hh" -#include "hb-ot-maxp-private.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-maxp-table.hh" #include #include -HB_BEGIN_DECLS hb_ot_layout_t * @@ -58,9 +54,6 @@ _hb_ot_layout_create (hb_face_t *face) layout->gpos_blob = Sanitizer::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS)); layout->gpos = Sanitizer::lock_instance (layout->gpos_blob); - layout->head_blob = Sanitizer::sanitize (hb_face_reference_table (face, HB_OT_TAG_head)); - layout->head = Sanitizer::lock_instance (layout->head_blob); - return layout; } @@ -70,7 +63,6 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout) hb_blob_destroy (layout->gdef_blob); hb_blob_destroy (layout->gsub_blob); hb_blob_destroy (layout->gpos_blob); - hb_blob_destroy (layout->head_blob); free (layout); } @@ -90,11 +82,6 @@ _get_gpos (hb_face_t *face) { return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS); } -static inline const head& -_get_head (hb_face_t *face) -{ - return likely (face->ot_layout && face->ot_layout->head) ? *face->ot_layout->head : Null(head); -} /* @@ -245,19 +232,19 @@ hb_ot_layout_table_find_script (hb_face_t *face, const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); if (g.find_script_index (script_tag, script_index)) - return TRUE; + return true; /* try finding 'DFLT' */ if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) - return FALSE; + return false; /* try with 'dflt'; MS site has had typos and many fonts use it now :(. * including many versions of DejaVu Sans Mono! */ if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) - return FALSE; + return false; if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; - return FALSE; + return false; } hb_bool_t @@ -275,7 +262,7 @@ hb_ot_layout_table_choose_script (hb_face_t *face, if (g.find_script_index (*script_tags, script_index)) { if (chosen_script) *chosen_script = *script_tags; - return TRUE; + return true; } script_tags++; } @@ -284,20 +271,29 @@ hb_ot_layout_table_choose_script (hb_face_t *face, if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { if (chosen_script) *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; - return FALSE; + return false; } /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { if (chosen_script) *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; - return FALSE; + return false; + } + + /* try with 'latn'; some old fonts put their features there even though + they're really trying to support Thai, for example :( */ +#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { + if (chosen_script) + *chosen_script = HB_OT_TAG_LATIN_SCRIPT; + return false; } if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; if (chosen_script) *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; - return FALSE; + return false; } unsigned int @@ -337,14 +333,14 @@ hb_ot_layout_script_find_language (hb_face_t *face, const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); if (s.find_lang_sys_index (language_tag, language_index)) - return TRUE; + return true; /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) - return FALSE; + return false; if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; - return FALSE; + return false; } hb_bool_t @@ -419,12 +415,12 @@ hb_ot_layout_language_find_feature (hb_face_t *face, if (feature_tag == g.get_feature_tag (f_index)) { if (feature_index) *feature_index = f_index; - return TRUE; + return true; } } if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; - return FALSE; + return false; } unsigned int @@ -464,7 +460,8 @@ hb_ot_layout_substitute_lookup (hb_face_t *face, unsigned int lookup_index, hb_mask_t mask) { - return _get_gsub (face).substitute_lookup (face, buffer, lookup_index, mask); + hb_apply_context_t c (NULL, face, buffer, mask); + return _get_gsub (face).substitute_lookup (&c, lookup_index); } void @@ -473,6 +470,14 @@ hb_ot_layout_substitute_finish (hb_buffer_t *buffer HB_UNUSED) GSUB::substitute_finish (buffer); } +void +hb_ot_layout_substitute_closure_lookup (hb_face_t *face, + hb_set_t *glyphs, + unsigned int lookup_index) +{ + hb_closure_context_t c (face, glyphs); + _get_gsub (face).closure_lookup (&c, lookup_index); +} /* * GPOS @@ -496,7 +501,8 @@ hb_ot_layout_position_lookup (hb_font_t *font, unsigned int lookup_index, hb_mask_t mask) { - return _get_gpos (font->face).position_lookup (font, buffer, lookup_index, mask); + hb_apply_context_t c (font, font->face, buffer, mask); + return _get_gpos (font->face).position_lookup (&c, lookup_index); } void @@ -506,15 +512,3 @@ hb_ot_layout_position_finish (hb_buffer_t *buffer) } -/* - * head - */ - -unsigned int -_hb_ot_layout_get_upem (hb_face_t *face) -{ - return _get_head (face).get_upem (); -} - - -HB_END_DECLS diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h index 447e35d..b8b5baf 100644 --- a/src/hb-ot-layout.h +++ b/src/hb-ot-layout.h @@ -24,12 +24,14 @@ * Red Hat Author(s): Behdad Esfahbod */ +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + #ifndef HB_OT_LAYOUT_H #define HB_OT_LAYOUT_H -#include "hb-common.h" -#include "hb-buffer.h" -#include "hb-font.h" +#include "hb.h" #include "hb-ot-tag.h" @@ -180,6 +182,12 @@ hb_ot_layout_substitute_lookup (hb_face_t *face, void hb_ot_layout_substitute_finish (hb_buffer_t *buffer); + +void +hb_ot_layout_substitute_closure_lookup (hb_face_t *face, + hb_set_t *glyphs, + unsigned int lookup_index); + /* * GPOS */ diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh index 5e6aca3..3811206 100644 --- a/src/hb-ot-map-private.hh +++ b/src/hb-ot-map-private.hh @@ -33,7 +33,6 @@ #include "hb-ot-layout.h" -HB_BEGIN_DECLS static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS}; @@ -44,6 +43,8 @@ struct hb_ot_map_t public: + hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); } + typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data); typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data); @@ -68,6 +69,10 @@ struct hb_ot_map_t inline void position (hb_font_t *font, hb_buffer_t *buffer) const { apply (1, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_position_lookup, font, buffer); } + HB_INTERNAL void substitute_closure (hb_face_t *face, + hb_set_t *glyphs) const; + + inline void finish (void) { features.finish (); lookups[0].finish (); @@ -137,6 +142,8 @@ struct hb_ot_map_builder_t { public: + hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); } + HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global); inline void add_bool_feature (hb_tag_t tag, bool global = true) @@ -184,6 +191,5 @@ struct hb_ot_map_builder_t }; -HB_END_DECLS #endif /* HB_OT_MAP_PRIVATE_HH */ diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc index ad1290f..bebf3ed 100644 --- a/src/hb-ot-map.cc +++ b/src/hb-ot-map.cc @@ -30,7 +30,6 @@ #include "hb-ot-shape-private.hh" -HB_BEGIN_DECLS void @@ -96,6 +95,21 @@ void hb_ot_map_t::apply (unsigned int table_index, apply_lookup_func (face_or_font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); } +void hb_ot_map_t::substitute_closure (hb_face_t *face, + hb_set_t *glyphs) const +{ + unsigned int table_index = 0; + unsigned int i = 0; + + for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) { + const pause_map_t *pause = &pauses[table_index][pause_index]; + for (; i < pause->num_lookups; i++) + hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index); + } + + for (; i < lookups[table_index].len; i++) + hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index); +} void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func, void *user_data) { @@ -273,4 +287,3 @@ hb_ot_map_builder_t::compile (hb_face_t *face, } -HB_END_DECLS diff --git a/src/hb-ot-maxp-private.hh b/src/hb-ot-maxp-table.hh similarity index 84% rename from src/hb-ot-maxp-private.hh rename to src/hb-ot-maxp-table.hh index fa2cb26..e270490 100644 --- a/src/hb-ot-maxp-private.hh +++ b/src/hb-ot-maxp-table.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,16 +24,15 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_MAXP_PRIVATE_HH -#define HB_OT_MAXP_PRIVATE_HH +#ifndef HB_OT_MAXP_TABLE_HH +#define HB_OT_MAXP_TABLE_HH #include "hb-open-type-private.hh" -HB_BEGIN_DECLS /* - * maxp + * maxp -- The Maximum Profile Table */ #define HB_OT_TAG_maxp HB_TAG('m','a','x','p') @@ -48,9 +47,8 @@ struct maxp inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); - return c->check_struct (this) && - likely (version.major == 1 || - (version.major == 0 && version.minor == 0x5000)); + return TRACE_RETURN (c->check_struct (this) && + likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000))); } /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */ @@ -63,6 +61,5 @@ struct maxp }; -HB_END_DECLS -#endif /* HB_OT_MAXP_PRIVATE_HH */ +#endif /* HB_OT_MAXP_TABLE_HH */ diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh new file mode 100644 index 0000000..9077c8c --- /dev/null +++ b/src/hb-ot-name-table.hh @@ -0,0 +1,128 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_NAME_TABLE_HH +#define HB_OT_NAME_TABLE_HH + +#include "hb-open-type-private.hh" + + + +/* + * name -- The Naming Table + */ + +#define HB_OT_TAG_name HB_TAG('n','a','m','e') + + +struct NameRecord +{ + static int cmp (const NameRecord *a, const NameRecord *b) + { + int ret; + ret = b->platformID.cmp (a->platformID); + if (ret) return ret; + ret = b->encodingID.cmp (a->encodingID); + if (ret) return ret; + ret = b->languageID.cmp (a->languageID); + if (ret) return ret; + ret = b->nameID.cmp (a->nameID); + if (ret) return ret; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c, void *base) { + TRACE_SANITIZE (); + /* We can check from base all the way up to the end of string... */ + return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset)); + } + + USHORT platformID; /* Platform ID. */ + USHORT encodingID; /* Platform-specific encoding ID. */ + USHORT languageID; /* Language ID. */ + USHORT nameID; /* Name ID. */ + USHORT length; /* String length (in bytes). */ + USHORT offset; /* String offset from start of storage area (in bytes). */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct name +{ + static const hb_tag_t Tag = HB_OT_TAG_name; + + inline unsigned int get_name (unsigned int platform_id, + unsigned int encoding_id, + unsigned int language_id, + unsigned int name_id, + void *buffer, + unsigned int buffer_length) const + { + NameRecord key; + key.platformID.set (platform_id); + key.encodingID.set (encoding_id); + key.languageID.set (language_id); + key.nameID.set (name_id); + NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), (hb_compare_func_t) NameRecord::cmp); + + if (!match) + return 0; + + unsigned int length = MIN (buffer_length, (unsigned int) match->length); + memcpy (buffer, (char *) this + stringOffset + match->offset, length); + return length; + } + + inline bool sanitize_records (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + char *string_pool = (char *) this + stringOffset; + unsigned int _count = count; + for (unsigned int i = 0; i < _count; i++) + if (!nameRecord[i].sanitize (c, string_pool)) return TRACE_RETURN (false); + return TRACE_RETURN (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) { + TRACE_SANITIZE (); + return TRACE_RETURN (c->check_struct (this) && + likely (format == 0 || format == 1) && + c->check_array (nameRecord, nameRecord[0].static_size, count) && + sanitize_records (c)); + } + + /* We only implement format 0 for now. */ + private: + USHORT format; /* Format selector (=0/1). */ + USHORT count; /* Number of name records. */ + Offset stringOffset; /* Offset to start of string storage (from start of table). */ + NameRecord nameRecord[VAR]; /* The name records where count is the number of records. */ + public: + DEFINE_SIZE_ARRAY (6, nameRecord); +}; + + + +#endif /* HB_OT_NAME_TABLE_HH */ diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh index 2b66dcc..df85086 100644 --- a/src/hb-ot-shape-complex-arabic-table.hh +++ b/src/hb-ot-shape-complex-arabic-table.hh @@ -1,47 +1,20 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH -#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH - -#include "hb-private.hh" - -HB_BEGIN_DECLS - /* == Start of generated table == */ /* * The following table is generated by running: * - * ./gen-arabic-table.py ArabicShaping.txt + * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt * * on files with these headers: * * # ArabicShaping-6.1.0.txt * # Date: 2011-04-15, 23:16:00 GMT [KW] + * UnicodeData.txt does not have a header. */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH + + static const uint8_t joining_table[] = { @@ -753,8 +726,217 @@ static const uint8_t joining_table[] = #define JOINING_TABLE_FIRST 0x0600 #define JOINING_TABLE_LAST 0x08AC -/* == End of generated table == */ -HB_END_DECLS +static const uint16_t shaping_table[][4] = +{ + {0x0621, 0x0621, 0x0621, 0xFE80}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */ + {0x0622, 0x0622, 0xFE82, 0xFE81}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ + {0x0623, 0x0623, 0xFE84, 0xFE83}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ + {0x0624, 0x0624, 0xFE86, 0xFE85}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ + {0x0625, 0x0625, 0xFE88, 0xFE87}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ + {0xFE8B, 0xFE8C, 0xFE8A, 0xFE89}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ + {0x0627, 0x0627, 0xFE8E, 0xFE8D}, /* U+0627 ARABIC LETTER ALEF */ + {0xFE91, 0xFE92, 0xFE90, 0xFE8F}, /* U+0628 ARABIC LETTER BEH */ + {0x0629, 0x0629, 0xFE94, 0xFE93}, /* U+0629 ARABIC LETTER TEH MARBUTA */ + {0xFE97, 0xFE98, 0xFE96, 0xFE95}, /* U+062A ARABIC LETTER TEH */ + {0xFE9B, 0xFE9C, 0xFE9A, 0xFE99}, /* U+062B ARABIC LETTER THEH */ + {0xFE9F, 0xFEA0, 0xFE9E, 0xFE9D}, /* U+062C ARABIC LETTER JEEM */ + {0xFEA3, 0xFEA4, 0xFEA2, 0xFEA1}, /* U+062D ARABIC LETTER HAH */ + {0xFEA7, 0xFEA8, 0xFEA6, 0xFEA5}, /* U+062E ARABIC LETTER KHAH */ + {0x062F, 0x062F, 0xFEAA, 0xFEA9}, /* U+062F ARABIC LETTER DAL */ + {0x0630, 0x0630, 0xFEAC, 0xFEAB}, /* U+0630 ARABIC LETTER THAL */ + {0x0631, 0x0631, 0xFEAE, 0xFEAD}, /* U+0631 ARABIC LETTER REH */ + {0x0632, 0x0632, 0xFEB0, 0xFEAF}, /* U+0632 ARABIC LETTER ZAIN */ + {0xFEB3, 0xFEB4, 0xFEB2, 0xFEB1}, /* U+0633 ARABIC LETTER SEEN */ + {0xFEB7, 0xFEB8, 0xFEB6, 0xFEB5}, /* U+0634 ARABIC LETTER SHEEN */ + {0xFEBB, 0xFEBC, 0xFEBA, 0xFEB9}, /* U+0635 ARABIC LETTER SAD */ + {0xFEBF, 0xFEC0, 0xFEBE, 0xFEBD}, /* U+0636 ARABIC LETTER DAD */ + {0xFEC3, 0xFEC4, 0xFEC2, 0xFEC1}, /* U+0637 ARABIC LETTER TAH */ + {0xFEC7, 0xFEC8, 0xFEC6, 0xFEC5}, /* U+0638 ARABIC LETTER ZAH */ + {0xFECB, 0xFECC, 0xFECA, 0xFEC9}, /* U+0639 ARABIC LETTER AIN */ + {0xFECF, 0xFED0, 0xFECE, 0xFECD}, /* U+063A ARABIC LETTER GHAIN */ + {0x063B, 0x063B, 0x063B, 0x063B}, /* U+063B */ + {0x063C, 0x063C, 0x063C, 0x063C}, /* U+063C */ + {0x063D, 0x063D, 0x063D, 0x063D}, /* U+063D */ + {0x063E, 0x063E, 0x063E, 0x063E}, /* U+063E */ + {0x063F, 0x063F, 0x063F, 0x063F}, /* U+063F */ + {0x0640, 0x0640, 0x0640, 0x0640}, /* U+0640 */ + {0xFED3, 0xFED4, 0xFED2, 0xFED1}, /* U+0641 ARABIC LETTER FEH */ + {0xFED7, 0xFED8, 0xFED6, 0xFED5}, /* U+0642 ARABIC LETTER QAF */ + {0xFEDB, 0xFEDC, 0xFEDA, 0xFED9}, /* U+0643 ARABIC LETTER KAF */ + {0xFEDF, 0xFEE0, 0xFEDE, 0xFEDD}, /* U+0644 ARABIC LETTER LAM */ + {0xFEE3, 0xFEE4, 0xFEE2, 0xFEE1}, /* U+0645 ARABIC LETTER MEEM */ + {0xFEE7, 0xFEE8, 0xFEE6, 0xFEE5}, /* U+0646 ARABIC LETTER NOON */ + {0xFEEB, 0xFEEC, 0xFEEA, 0xFEE9}, /* U+0647 ARABIC LETTER HEH */ + {0x0648, 0x0648, 0xFEEE, 0xFEED}, /* U+0648 ARABIC LETTER WAW */ + {0xFBE8, 0xFBE9, 0xFEF0, 0xFEEF}, /* U+0649 ARABIC LETTER */ + {0xFEF3, 0xFEF4, 0xFEF2, 0xFEF1}, /* U+064A ARABIC LETTER YEH */ + {0x064B, 0x064B, 0x064B, 0x064B}, /* U+064B */ + {0x064C, 0x064C, 0x064C, 0x064C}, /* U+064C */ + {0x064D, 0x064D, 0x064D, 0x064D}, /* U+064D */ + {0x064E, 0x064E, 0x064E, 0x064E}, /* U+064E */ + {0x064F, 0x064F, 0x064F, 0x064F}, /* U+064F */ + {0x0650, 0x0650, 0x0650, 0x0650}, /* U+0650 */ + {0x0651, 0x0651, 0x0651, 0x0651}, /* U+0651 */ + {0x0652, 0x0652, 0x0652, 0x0652}, /* U+0652 */ + {0x0653, 0x0653, 0x0653, 0x0653}, /* U+0653 */ + {0x0654, 0x0654, 0x0654, 0x0654}, /* U+0654 */ + {0x0655, 0x0655, 0x0655, 0x0655}, /* U+0655 */ + {0x0656, 0x0656, 0x0656, 0x0656}, /* U+0656 */ + {0x0657, 0x0657, 0x0657, 0x0657}, /* U+0657 */ + {0x0658, 0x0658, 0x0658, 0x0658}, /* U+0658 */ + {0x0659, 0x0659, 0x0659, 0x0659}, /* U+0659 */ + {0x065A, 0x065A, 0x065A, 0x065A}, /* U+065A */ + {0x065B, 0x065B, 0x065B, 0x065B}, /* U+065B */ + {0x065C, 0x065C, 0x065C, 0x065C}, /* U+065C */ + {0x065D, 0x065D, 0x065D, 0x065D}, /* U+065D */ + {0x065E, 0x065E, 0x065E, 0x065E}, /* U+065E */ + {0x065F, 0x065F, 0x065F, 0x065F}, /* U+065F */ + {0x0660, 0x0660, 0x0660, 0x0660}, /* U+0660 */ + {0x0661, 0x0661, 0x0661, 0x0661}, /* U+0661 */ + {0x0662, 0x0662, 0x0662, 0x0662}, /* U+0662 */ + {0x0663, 0x0663, 0x0663, 0x0663}, /* U+0663 */ + {0x0664, 0x0664, 0x0664, 0x0664}, /* U+0664 */ + {0x0665, 0x0665, 0x0665, 0x0665}, /* U+0665 */ + {0x0666, 0x0666, 0x0666, 0x0666}, /* U+0666 */ + {0x0667, 0x0667, 0x0667, 0x0667}, /* U+0667 */ + {0x0668, 0x0668, 0x0668, 0x0668}, /* U+0668 */ + {0x0669, 0x0669, 0x0669, 0x0669}, /* U+0669 */ + {0x066A, 0x066A, 0x066A, 0x066A}, /* U+066A */ + {0x066B, 0x066B, 0x066B, 0x066B}, /* U+066B */ + {0x066C, 0x066C, 0x066C, 0x066C}, /* U+066C */ + {0x066D, 0x066D, 0x066D, 0x066D}, /* U+066D */ + {0x066E, 0x066E, 0x066E, 0x066E}, /* U+066E */ + {0x066F, 0x066F, 0x066F, 0x066F}, /* U+066F */ + {0x0670, 0x0670, 0x0670, 0x0670}, /* U+0670 */ + {0x0671, 0x0671, 0xFB51, 0xFB50}, /* U+0671 ARABIC LETTER ALEF WASLA */ + {0x0672, 0x0672, 0x0672, 0x0672}, /* U+0672 */ + {0x0673, 0x0673, 0x0673, 0x0673}, /* U+0673 */ + {0x0674, 0x0674, 0x0674, 0x0674}, /* U+0674 */ + {0x0675, 0x0675, 0x0675, 0x0675}, /* U+0675 */ + {0x0676, 0x0676, 0x0676, 0x0676}, /* U+0676 */ + {0x0677, 0x0677, 0x0677, 0xFBDD}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */ + {0x0678, 0x0678, 0x0678, 0x0678}, /* U+0678 */ + {0xFB68, 0xFB69, 0xFB67, 0xFB66}, /* U+0679 ARABIC LETTER TTEH */ + {0xFB60, 0xFB61, 0xFB5F, 0xFB5E}, /* U+067A ARABIC LETTER TTEHEH */ + {0xFB54, 0xFB55, 0xFB53, 0xFB52}, /* U+067B ARABIC LETTER BEEH */ + {0x067C, 0x067C, 0x067C, 0x067C}, /* U+067C */ + {0x067D, 0x067D, 0x067D, 0x067D}, /* U+067D */ + {0xFB58, 0xFB59, 0xFB57, 0xFB56}, /* U+067E ARABIC LETTER PEH */ + {0xFB64, 0xFB65, 0xFB63, 0xFB62}, /* U+067F ARABIC LETTER TEHEH */ + {0xFB5C, 0xFB5D, 0xFB5B, 0xFB5A}, /* U+0680 ARABIC LETTER BEHEH */ + {0x0681, 0x0681, 0x0681, 0x0681}, /* U+0681 */ + {0x0682, 0x0682, 0x0682, 0x0682}, /* U+0682 */ + {0xFB78, 0xFB79, 0xFB77, 0xFB76}, /* U+0683 ARABIC LETTER NYEH */ + {0xFB74, 0xFB75, 0xFB73, 0xFB72}, /* U+0684 ARABIC LETTER DYEH */ + {0x0685, 0x0685, 0x0685, 0x0685}, /* U+0685 */ + {0xFB7C, 0xFB7D, 0xFB7B, 0xFB7A}, /* U+0686 ARABIC LETTER TCHEH */ + {0xFB80, 0xFB81, 0xFB7F, 0xFB7E}, /* U+0687 ARABIC LETTER TCHEHEH */ + {0x0688, 0x0688, 0xFB89, 0xFB88}, /* U+0688 ARABIC LETTER DDAL */ + {0x0689, 0x0689, 0x0689, 0x0689}, /* U+0689 */ + {0x068A, 0x068A, 0x068A, 0x068A}, /* U+068A */ + {0x068B, 0x068B, 0x068B, 0x068B}, /* U+068B */ + {0x068C, 0x068C, 0xFB85, 0xFB84}, /* U+068C ARABIC LETTER DAHAL */ + {0x068D, 0x068D, 0xFB83, 0xFB82}, /* U+068D ARABIC LETTER DDAHAL */ + {0x068E, 0x068E, 0xFB87, 0xFB86}, /* U+068E ARABIC LETTER DUL */ + {0x068F, 0x068F, 0x068F, 0x068F}, /* U+068F */ + {0x0690, 0x0690, 0x0690, 0x0690}, /* U+0690 */ + {0x0691, 0x0691, 0xFB8D, 0xFB8C}, /* U+0691 ARABIC LETTER RREH */ + {0x0692, 0x0692, 0x0692, 0x0692}, /* U+0692 */ + {0x0693, 0x0693, 0x0693, 0x0693}, /* U+0693 */ + {0x0694, 0x0694, 0x0694, 0x0694}, /* U+0694 */ + {0x0695, 0x0695, 0x0695, 0x0695}, /* U+0695 */ + {0x0696, 0x0696, 0x0696, 0x0696}, /* U+0696 */ + {0x0697, 0x0697, 0x0697, 0x0697}, /* U+0697 */ + {0x0698, 0x0698, 0xFB8B, 0xFB8A}, /* U+0698 ARABIC LETTER JEH */ + {0x0699, 0x0699, 0x0699, 0x0699}, /* U+0699 */ + {0x069A, 0x069A, 0x069A, 0x069A}, /* U+069A */ + {0x069B, 0x069B, 0x069B, 0x069B}, /* U+069B */ + {0x069C, 0x069C, 0x069C, 0x069C}, /* U+069C */ + {0x069D, 0x069D, 0x069D, 0x069D}, /* U+069D */ + {0x069E, 0x069E, 0x069E, 0x069E}, /* U+069E */ + {0x069F, 0x069F, 0x069F, 0x069F}, /* U+069F */ + {0x06A0, 0x06A0, 0x06A0, 0x06A0}, /* U+06A0 */ + {0x06A1, 0x06A1, 0x06A1, 0x06A1}, /* U+06A1 */ + {0x06A2, 0x06A2, 0x06A2, 0x06A2}, /* U+06A2 */ + {0x06A3, 0x06A3, 0x06A3, 0x06A3}, /* U+06A3 */ + {0xFB6C, 0xFB6D, 0xFB6B, 0xFB6A}, /* U+06A4 ARABIC LETTER VEH */ + {0x06A5, 0x06A5, 0x06A5, 0x06A5}, /* U+06A5 */ + {0xFB70, 0xFB71, 0xFB6F, 0xFB6E}, /* U+06A6 ARABIC LETTER PEHEH */ + {0x06A7, 0x06A7, 0x06A7, 0x06A7}, /* U+06A7 */ + {0x06A8, 0x06A8, 0x06A8, 0x06A8}, /* U+06A8 */ + {0xFB90, 0xFB91, 0xFB8F, 0xFB8E}, /* U+06A9 ARABIC LETTER KEHEH */ + {0x06AA, 0x06AA, 0x06AA, 0x06AA}, /* U+06AA */ + {0x06AB, 0x06AB, 0x06AB, 0x06AB}, /* U+06AB */ + {0x06AC, 0x06AC, 0x06AC, 0x06AC}, /* U+06AC */ + {0xFBD5, 0xFBD6, 0xFBD4, 0xFBD3}, /* U+06AD ARABIC LETTER NG */ + {0x06AE, 0x06AE, 0x06AE, 0x06AE}, /* U+06AE */ + {0xFB94, 0xFB95, 0xFB93, 0xFB92}, /* U+06AF ARABIC LETTER GAF */ + {0x06B0, 0x06B0, 0x06B0, 0x06B0}, /* U+06B0 */ + {0xFB9C, 0xFB9D, 0xFB9B, 0xFB9A}, /* U+06B1 ARABIC LETTER NGOEH */ + {0x06B2, 0x06B2, 0x06B2, 0x06B2}, /* U+06B2 */ + {0xFB98, 0xFB99, 0xFB97, 0xFB96}, /* U+06B3 ARABIC LETTER GUEH */ + {0x06B4, 0x06B4, 0x06B4, 0x06B4}, /* U+06B4 */ + {0x06B5, 0x06B5, 0x06B5, 0x06B5}, /* U+06B5 */ + {0x06B6, 0x06B6, 0x06B6, 0x06B6}, /* U+06B6 */ + {0x06B7, 0x06B7, 0x06B7, 0x06B7}, /* U+06B7 */ + {0x06B8, 0x06B8, 0x06B8, 0x06B8}, /* U+06B8 */ + {0x06B9, 0x06B9, 0x06B9, 0x06B9}, /* U+06B9 */ + {0x06BA, 0x06BA, 0xFB9F, 0xFB9E}, /* U+06BA ARABIC LETTER NOON GHUNNA */ + {0xFBA2, 0xFBA3, 0xFBA1, 0xFBA0}, /* U+06BB ARABIC LETTER RNOON */ + {0x06BC, 0x06BC, 0x06BC, 0x06BC}, /* U+06BC */ + {0x06BD, 0x06BD, 0x06BD, 0x06BD}, /* U+06BD */ + {0xFBAC, 0xFBAD, 0xFBAB, 0xFBAA}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ + {0x06BF, 0x06BF, 0x06BF, 0x06BF}, /* U+06BF */ + {0x06C0, 0x06C0, 0xFBA5, 0xFBA4}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */ + {0xFBA8, 0xFBA9, 0xFBA7, 0xFBA6}, /* U+06C1 ARABIC LETTER HEH GOAL */ + {0x06C2, 0x06C2, 0x06C2, 0x06C2}, /* U+06C2 */ + {0x06C3, 0x06C3, 0x06C3, 0x06C3}, /* U+06C3 */ + {0x06C4, 0x06C4, 0x06C4, 0x06C4}, /* U+06C4 */ + {0x06C5, 0x06C5, 0xFBE1, 0xFBE0}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */ + {0x06C6, 0x06C6, 0xFBDA, 0xFBD9}, /* U+06C6 ARABIC LETTER OE */ + {0x06C7, 0x06C7, 0xFBD8, 0xFBD7}, /* U+06C7 ARABIC LETTER U */ + {0x06C8, 0x06C8, 0xFBDC, 0xFBDB}, /* U+06C8 ARABIC LETTER YU */ + {0x06C9, 0x06C9, 0xFBE3, 0xFBE2}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */ + {0x06CA, 0x06CA, 0x06CA, 0x06CA}, /* U+06CA */ + {0x06CB, 0x06CB, 0xFBDF, 0xFBDE}, /* U+06CB ARABIC LETTER VE */ + {0xFBFE, 0xFBFF, 0xFBFD, 0xFBFC}, /* U+06CC ARABIC LETTER FARSI YEH */ + {0x06CD, 0x06CD, 0x06CD, 0x06CD}, /* U+06CD */ + {0x06CE, 0x06CE, 0x06CE, 0x06CE}, /* U+06CE */ + {0x06CF, 0x06CF, 0x06CF, 0x06CF}, /* U+06CF */ + {0xFBE6, 0xFBE7, 0xFBE5, 0xFBE4}, /* U+06D0 ARABIC LETTER E */ + {0x06D1, 0x06D1, 0x06D1, 0x06D1}, /* U+06D1 */ + {0x06D2, 0x06D2, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */ + {0x06D3, 0x06D3, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */ +}; + +#define SHAPING_TABLE_FIRST 0x0621 +#define SHAPING_TABLE_LAST 0x06D3 + + +static const struct { + uint16_t first; + struct { + uint16_t second; + uint16_t ligature; + } ligatures[4]; +} ligature_table[] = +{ + { 0xFEDF, { + { 0xFE88, 0xFEF9 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { 0xFE82, 0xFEF5 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ + { 0xFE8E, 0xFEFB }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ + { 0xFE84, 0xFEF7 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + }}, + { 0xFEE0, { + { 0xFE88, 0xFEFA }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { 0xFE82, 0xFEF6 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ + { 0xFE8E, 0xFEFC }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ + { 0xFE84, 0xFEF8 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + }}, +}; + #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */ + +/* == End of generated table == */ diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index 02df533..54460f0 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -25,12 +25,12 @@ */ #include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-private.hh" -HB_BEGIN_DECLS /* buffer var allocations */ -#define arabic_shaping_action() complex_var_temporary_u16() /* arabic shaping action */ +#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */ /* @@ -59,8 +59,6 @@ enum { static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) { - /* TODO Macroize the magic bit operations */ - if (likely (hb_in_range (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) { unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST]; if (likely (j_type != JOINING_TYPE_X)) @@ -83,7 +81,23 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ JOINING_TYPE_T : JOINING_TYPE_U; } +static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape) +{ + if (likely (hb_in_range (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4) + return shaping_table[u - SHAPING_TABLE_FIRST][shape]; + return u; +} +static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second) +{ + if (unlikely (!second)) return 0; + for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++) + if (ligature_table[i].first == first) + for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++) + if (ligature_table[i].ligatures[j].second == second) + return ligature_table[i].ligatures[j].ligature; + return 0; +} static const hb_tag_t arabic_syriac_features[] = { @@ -151,7 +165,8 @@ static const struct arabic_state_table_entry { void -_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb_segment_properties_t *props) +_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, + const hb_segment_properties_t *props) { /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together, * then rlig and calt each in their own stage. This makes IranNastaliq's ALLAH @@ -164,6 +179,7 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb */ map->add_bool_feature (HB_TAG('c','c','m','p')); + map->add_bool_feature (HB_TAG('l','o','c','l')); map->add_gsub_pause (NULL, NULL); @@ -183,14 +199,51 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb map->add_bool_feature (HB_TAG('c','s','w','h')); } -bool -_hb_ot_shape_complex_prefer_decomposed_arabic (void) +hb_ot_shape_normalization_mode_t +_hb_ot_shape_complex_normalization_preference_arabic (void) { - return FALSE; + return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; +} + + +static void +arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_codepoint_t glyph; + + /* Shape to presentation forms */ + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t u = buffer->info[i].codepoint; + hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action()); + if (shaped != u && hb_font_get_glyph (font, shaped, 0, &glyph)) + buffer->info[i].codepoint = shaped; + } + + /* Mandatory ligatures */ + buffer->clear_output (); + for (buffer->idx = 0; buffer->idx + 1 < count;) { + hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint, + buffer->cur(+1).codepoint); + if (likely (!ligature) || !(hb_font_get_glyph (font, ligature, 0, &glyph))) { + buffer->next_glyph (); + continue; + } + + buffer->replace_glyphs (2, 1, &ligature); + + /* Technically speaking we can skip marks and stuff, like the GSUB path does. + * But who cares, we're in fallback! */ + } + for (; buffer->idx < count;) + buffer->next_glyph (); + buffer->swap_buffers (); } void -_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer) +_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, + hb_buffer_t *buffer, + hb_font_t *font) { unsigned int count = buffer->len; unsigned int prev = 0, state = 0; @@ -199,7 +252,7 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer) for (unsigned int i = 0; i < count; i++) { - unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category()); + unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i])); if (unlikely (this_type == JOINING_TYPE_T)) { buffer->info[i].arabic_shaping_action() = NONE; @@ -218,15 +271,29 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer) } hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0}; + hb_mask_t total_masks = 0; unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES; - for (unsigned int i = 0; i < num_masks; i++) + for (unsigned int i = 0; i < num_masks; i++) { mask_array[i] = map->get_1_mask (arabic_syriac_features[i]); + total_masks |= mask_array[i]; + } - for (unsigned int i = 0; i < count; i++) - buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()]; + if (total_masks) { + /* Has OpenType tables */ + for (unsigned int i = 0; i < count; i++) + buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()]; + } else if (buffer->props.script == HB_SCRIPT_ARABIC) { + /* Fallback Arabic shaping to Presentation Forms */ + /* Pitfalls: + * - This path fires if user force-set init/medi/fina/isol off, + * - If font does not declare script 'arab', well, what to do? + * Most probably it's safe to assume that init/medi/fina/isol + * still mean Arabic shaping, although they do not have to. + */ + arabic_fallback_shape (font, buffer); + } HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } -HB_END_DECLS diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh new file mode 100644 index 0000000..db1396b --- /dev/null +++ b/src/hb-ot-shape-complex-indic-machine.hh @@ -0,0 +1,293 @@ + +#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH +#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH + +#include "hb-private.hh" + +HB_BEGIN_DECLS + + +#line 38 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" +static const unsigned char _indic_syllable_machine_trans_keys[] = { + 5u, 5u, 1u, 2u, 1u, 2u, 5u, 5u, 1u, 5u, 1u, 2u, 5u, 5u, 1u, 13u, + 4u, 11u, 4u, 11u, 5u, 11u, 1u, 10u, 1u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, + 5u, 10u, 8u, 10u, 5u, 10u, 6u, 10u, 9u, 10u, 4u, 11u, 1u, 13u, 4u, 10u, + 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, + 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 0 +}; + +static const char _indic_syllable_machine_key_spans[] = { + 1, 2, 2, 1, 5, 2, 1, 13, + 8, 8, 7, 10, 10, 1, 1, 7, + 6, 3, 6, 5, 2, 8, 13, 7, + 7, 6, 7, 6, 3, 1, 1, 7, + 7, 6, 7, 6, 3, 1, 1 +}; + +static const unsigned char _indic_syllable_machine_index_offsets[] = { + 0, 2, 5, 8, 10, 16, 19, 21, + 35, 44, 53, 61, 72, 83, 85, 87, + 95, 102, 106, 113, 119, 122, 131, 145, + 153, 161, 168, 176, 183, 187, 189, 191, + 199, 207, 214, 222, 229, 233, 235 +}; + +static const char _indic_syllable_machine_indicies[] = { + 1, 0, 2, 2, 0, 4, 4, 3, + 5, 3, 4, 4, 3, 3, 5, 3, + 7, 7, 6, 8, 6, 2, 10, 11, + 9, 9, 9, 9, 9, 9, 9, 9, + 12, 12, 9, 14, 15, 16, 16, 17, + 18, 19, 20, 13, 21, 15, 16, 16, + 17, 18, 19, 20, 13, 15, 16, 16, + 17, 18, 19, 20, 13, 2, 2, 13, + 13, 13, 22, 22, 13, 18, 19, 13, + 2, 2, 13, 13, 13, 13, 13, 13, + 18, 19, 13, 19, 13, 23, 13, 24, + 25, 13, 13, 17, 18, 19, 13, 25, + 13, 13, 17, 18, 19, 13, 17, 18, + 19, 13, 26, 13, 13, 17, 18, 19, + 13, 27, 27, 13, 18, 19, 13, 18, + 19, 13, 14, 28, 16, 16, 17, 18, + 19, 20, 13, 2, 2, 11, 13, 13, + 22, 22, 13, 18, 19, 13, 12, 12, + 13, 30, 5, 31, 32, 33, 34, 35, + 29, 4, 5, 31, 32, 33, 34, 35, + 29, 5, 31, 32, 33, 34, 35, 29, + 36, 37, 29, 29, 33, 34, 35, 29, + 37, 29, 29, 33, 34, 35, 29, 33, + 34, 35, 29, 35, 29, 38, 29, 40, + 8, 41, 41, 42, 43, 44, 39, 7, + 8, 41, 41, 42, 43, 44, 39, 8, + 41, 41, 42, 43, 44, 39, 45, 46, + 39, 39, 42, 43, 44, 39, 46, 39, + 39, 42, 43, 44, 39, 42, 43, 44, + 39, 44, 39, 47, 39, 0 +}; + +static const char _indic_syllable_machine_trans_targs[] = { + 7, 1, 8, 7, 25, 2, 7, 33, + 5, 7, 21, 23, 31, 7, 9, 11, + 0, 15, 13, 14, 18, 10, 12, 7, + 16, 17, 19, 20, 22, 7, 24, 3, + 4, 26, 29, 30, 27, 28, 7, 7, + 32, 6, 34, 37, 38, 35, 36, 7 +}; + +static const char _indic_syllable_machine_trans_actions[] = { + 1, 0, 2, 3, 2, 0, 4, 2, + 0, 7, 2, 2, 2, 8, 2, 0, + 0, 0, 0, 0, 0, 2, 0, 9, + 0, 0, 0, 0, 0, 10, 2, 0, + 0, 0, 0, 0, 0, 0, 11, 12, + 2, 0, 0, 0, 0, 0, 0, 13 +}; + +static const char _indic_syllable_machine_to_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +static const char _indic_syllable_machine_from_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char _indic_syllable_machine_eof_trans[] = { + 1, 1, 4, 4, 4, 7, 7, 0, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 30, + 30, 30, 30, 30, 30, 30, 30, 40, + 40, 40, 40, 40, 40, 40, 40 +}; + +static const int indic_syllable_machine_start = 7; +static const int indic_syllable_machine_first_final = 7; +static const int indic_syllable_machine_error = -1; + +static const int indic_syllable_machine_en_main = 7; + + +#line 38 "../../src/hb-ot-shape-complex-indic-machine.rl" + + + +#line 79 "../../src/hb-ot-shape-complex-indic-machine.rl" + + +#define process_syllable(func) \ + HB_STMT_START { \ + /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = syllable_serial; \ + PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (!syllable_serial)) syllable_serial++; \ + } HB_STMT_END + +static void +find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array) +{ + unsigned int p, pe, eof, ts, te, act; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 170 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" + { + cs = indic_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 101 "../../src/hb-ot-shape-complex-indic-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int last = 0; + uint8_t syllable_serial = 1; + +#line 187 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _indic_syllable_machine_from_state_actions[cs] ) { + case 6: +#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" + {ts = p;} + break; +#line 201 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" + } + + _keys = _indic_syllable_machine_trans_keys + (cs<<1); + _inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs]; + + _slen = _indic_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) && + ( info[p].indic_category()) <= _keys[1] ? + ( info[p].indic_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _indic_syllable_machine_trans_targs[_trans]; + + if ( _indic_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _indic_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p+1;} + break; + case 9: +#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ process_syllable (consonant_syllable); }} + break; + case 11: +#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ process_syllable (vowel_syllable); }} + break; + case 13: +#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ process_syllable (standalone_cluster); }} + break; + case 7: +#line 75 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p+1;{ process_syllable (non_indic); }} + break; + case 8: +#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ process_syllable (consonant_syllable); }} + break; + case 10: +#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ process_syllable (vowel_syllable); }} + break; + case 12: +#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" + {te = p;p--;{ process_syllable (standalone_cluster); }} + break; + case 1: +#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ process_syllable (consonant_syllable); }} + break; + case 3: +#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ process_syllable (vowel_syllable); }} + break; + case 4: +#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl" + {{p = ((te))-1;}{ process_syllable (standalone_cluster); }} + break; +#line 263 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" + } + +_again: + switch ( _indic_syllable_machine_to_state_actions[cs] ) { + case 5: +#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl" + {ts = 0;} + break; +#line 272 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _indic_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _indic_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 110 "../../src/hb-ot-shape-complex-indic-machine.rl" + +} + +HB_END_DECLS + +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl index 53dc20d..93ca29a 100644 --- a/src/hb-ot-shape-complex-indic-machine.rl +++ b/src/hb-ot-shape-complex-indic-machine.rl @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -53,62 +53,58 @@ SM = 9; VD = 10; A = 11; NBSP = 12; +DOTTEDCIRCLE = 13; c = C | Ra; +n = N N?; z = ZWJ|ZWNJ; matra_group = M N? H?; syllable_tail = SM? (VD VD?)?; +place_holder = NBSP | DOTTEDCIRCLE; -action found_consonant_syllable { found_consonant_syllable (map, buffer, mask_array, last, p); } -action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, last, p); } -action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); } -action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); } -action next_syllable { set_cluster (buffer, p, last); last = p; } +consonant_syllable = (c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail; +vowel_syllable = (Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail; +standalone_cluster = (Ra H)? place_holder n? (z? H c)* matra_group* syllable_tail; +other = any; -consonant_syllable = (c.N? (z.H|H.z?))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable); -vowel_syllable = (Ra H)? V N? (z.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable); -standalone_cluster = (Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster); -non_indic = X %(found_non_indic); +main := |* + consonant_syllable => { process_syllable (consonant_syllable); }; + vowel_syllable => { process_syllable (vowel_syllable); }; + standalone_cluster => { process_syllable (standalone_cluster); }; + other => { process_syllable (non_indic); }; +*|; -syllable = - consonant_syllable - | vowel_syllable - | standalone_cluster - | non_indic - ; - -main := (syllable %(next_syllable))**; }%% - -static void -set_cluster (hb_buffer_t *buffer, - unsigned int start, unsigned int end) -{ - unsigned int cluster = buffer->info[start].cluster; - - for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, buffer->info[i].cluster); - for (unsigned int i = start; i < end; i++) - buffer->info[i].cluster = cluster; -} +#define process_syllable(func) \ + HB_STMT_START { \ + /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \ + for (unsigned int i = last; i < p+1; i++) \ + info[i].syllable() = syllable_serial; \ + PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \ + last = p+1; \ + syllable_serial++; \ + if (unlikely (!syllable_serial)) syllable_serial++; \ + } HB_STMT_END static void find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array) { - unsigned int p, pe, eof; + unsigned int p, pe, eof, ts, te, act; int cs; + hb_glyph_info_t *info = buffer->info; %%{ write init; - getkey buffer->info[p].indic_category(); + getkey info[p].indic_category(); }%% p = 0; pe = eof = buffer->len; unsigned int last = 0; + uint8_t syllable_serial = 1; %%{ write exec; }%% diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh new file mode 100644 index 0000000..64af0da --- /dev/null +++ b/src/hb-ot-shape-complex-indic-private.hh @@ -0,0 +1,270 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH +#define HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH + +#include "hb-private.hh" + + +#include "hb-ot-shape-complex-private.hh" + + +/* buffer var allocations */ +#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */ +#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */ + +#define INDIC_TABLE_ELEMENT_TYPE uint8_t + +/* Cateories used in the OpenType spec: + * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx + */ +/* Note: This enum is duplicated in the -machine.rl source file. + * Not sure how to avoid duplication. */ +enum indic_category_t { + OT_X = 0, + OT_C, + OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */ + OT_V, + OT_N, + OT_H, + OT_ZWNJ, + OT_ZWJ, + OT_M, + OT_SM, + OT_VD, + OT_A, + OT_NBSP, + OT_DOTTEDCIRCLE /* Not in the spec, but special in Uniscribe. /Very very/ special! */ +}; + +/* Visual positions in a syllable from left to right. */ +enum indic_position_t { + POS_RA_TO_BECOME_REPH, + POS_PRE_M, + POS_PRE_C, + POS_BASE_C, + POS_ABOVE_C, + POS_BELOW_C, + POS_ABOVE_M, + POS_BELOW_M, + POS_POST_C, + POS_POST_M, + POS_SMVD +}; + +/* Categories used in IndicSyllabicCategory.txt from UCD. */ +enum indic_syllabic_category_t { + INDIC_SYLLABIC_CATEGORY_OTHER = OT_X, + + INDIC_SYLLABIC_CATEGORY_AVAGRAHA = OT_X, + INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM, + INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_NBSP, + INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_C, + INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA = OT_C, + INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X, + INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N, + INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_X, + INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X, + INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_X, + INDIC_SYLLABIC_CATEGORY_VIRAMA = OT_H, + INDIC_SYLLABIC_CATEGORY_VISARGA = OT_SM, + INDIC_SYLLABIC_CATEGORY_VOWEL = OT_V, + INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT = OT_M, + INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT = OT_V +}; + +/* Categories used in IndicSMatraCategory.txt from UCD */ +enum indic_matra_category_t { + INDIC_MATRA_CATEGORY_NOT_APPLICABLE = POS_BASE_C, + + INDIC_MATRA_CATEGORY_LEFT = POS_PRE_M, + INDIC_MATRA_CATEGORY_TOP = POS_ABOVE_M, + INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW_M, + INDIC_MATRA_CATEGORY_RIGHT = POS_POST_M, + + /* We don't really care much about these since we decompose them + * in the generic pre-shaping layer. They will only be used if + * the font does not cover the decomposition. In which case, we + * define these as aliases to the place we want the split-matra + * glyph to show up. Quite arbitrary. + * + * TODO: There are some split matras without Unicode decompositions. + * We have to figure out what to do with them. + */ + INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = POS_POST_M, + INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = POS_PRE_M, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = POS_BELOW_M, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = POS_POST_M, + INDIC_MATRA_CATEGORY_TOP_AND_LEFT = POS_PRE_M, + INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = POS_PRE_M, + INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = POS_POST_M, + + INDIC_MATRA_CATEGORY_INVISIBLE = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, + INDIC_MATRA_CATEGORY_OVERSTRUCK = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, + INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = INDIC_MATRA_CATEGORY_NOT_APPLICABLE +}; + +/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation + * because gcc fails to optimize the latter and fills the table in at runtime. */ +#define INDIC_COMBINE_CATEGORIES(S,M) \ + (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \ + ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \ + ((M << 4) | S)) + + +#include "hb-ot-shape-complex-indic-table.hh" + +/* XXX + * This is a hack for now. We should: + * 1. Move this data into the main Indic table, + * and/or + * 2. Probe font lookups to determine consonant positions. + */ +static const struct consonant_position_t { + hb_codepoint_t u; + indic_position_t position; +} consonant_positions[] = { + {0x0930, POS_BELOW_C}, + {0x09AC, POS_BELOW_C}, + {0x09AF, POS_POST_C}, + {0x09B0, POS_BELOW_C}, + {0x09F0, POS_BELOW_C}, + {0x0A2F, POS_POST_C}, + {0x0A30, POS_BELOW_C}, + {0x0A35, POS_BELOW_C}, + {0x0A39, POS_BELOW_C}, + {0x0AB0, POS_BELOW_C}, + {0x0B24, POS_BELOW_C}, + {0x0B28, POS_BELOW_C}, + {0x0B2C, POS_BELOW_C}, + {0x0B2D, POS_BELOW_C}, + {0x0B2E, POS_BELOW_C}, + {0x0B2F, POS_POST_C}, + {0x0B30, POS_BELOW_C}, + {0x0B32, POS_BELOW_C}, + {0x0B33, POS_BELOW_C}, + {0x0B5F, POS_POST_C}, + {0x0B71, POS_BELOW_C}, + {0x0C15, POS_BELOW_C}, + {0x0C16, POS_BELOW_C}, + {0x0C17, POS_BELOW_C}, + {0x0C18, POS_BELOW_C}, + {0x0C19, POS_BELOW_C}, + {0x0C1A, POS_BELOW_C}, + {0x0C1B, POS_BELOW_C}, + {0x0C1C, POS_BELOW_C}, + {0x0C1D, POS_BELOW_C}, + {0x0C1E, POS_BELOW_C}, + {0x0C1F, POS_BELOW_C}, + {0x0C20, POS_BELOW_C}, + {0x0C21, POS_BELOW_C}, + {0x0C22, POS_BELOW_C}, + {0x0C23, POS_BELOW_C}, + {0x0C24, POS_BELOW_C}, + {0x0C25, POS_BELOW_C}, + {0x0C26, POS_BELOW_C}, + {0x0C27, POS_BELOW_C}, + {0x0C28, POS_BELOW_C}, + {0x0C2A, POS_BELOW_C}, + {0x0C2B, POS_BELOW_C}, + {0x0C2C, POS_BELOW_C}, + {0x0C2D, POS_BELOW_C}, + {0x0C2E, POS_BELOW_C}, + {0x0C2F, POS_BELOW_C}, + {0x0C30, POS_BELOW_C}, + {0x0C32, POS_BELOW_C}, + {0x0C33, POS_BELOW_C}, + {0x0C35, POS_BELOW_C}, + {0x0C36, POS_BELOW_C}, + {0x0C37, POS_BELOW_C}, + {0x0C38, POS_BELOW_C}, + {0x0C39, POS_BELOW_C}, + {0x0C95, POS_BELOW_C}, + {0x0C96, POS_BELOW_C}, + {0x0C97, POS_BELOW_C}, + {0x0C98, POS_BELOW_C}, + {0x0C99, POS_BELOW_C}, + {0x0C9A, POS_BELOW_C}, + {0x0C9B, POS_BELOW_C}, + {0x0C9C, POS_BELOW_C}, + {0x0C9D, POS_BELOW_C}, + {0x0C9E, POS_BELOW_C}, + {0x0C9F, POS_BELOW_C}, + {0x0CA0, POS_BELOW_C}, + {0x0CA1, POS_BELOW_C}, + {0x0CA2, POS_BELOW_C}, + {0x0CA3, POS_BELOW_C}, + {0x0CA4, POS_BELOW_C}, + {0x0CA5, POS_BELOW_C}, + {0x0CA6, POS_BELOW_C}, + {0x0CA7, POS_BELOW_C}, + {0x0CA8, POS_BELOW_C}, + {0x0CAA, POS_BELOW_C}, + {0x0CAB, POS_BELOW_C}, + {0x0CAC, POS_BELOW_C}, + {0x0CAD, POS_BELOW_C}, + {0x0CAE, POS_BELOW_C}, + {0x0CAF, POS_BELOW_C}, + {0x0CB0, POS_BELOW_C}, + {0x0CB2, POS_BELOW_C}, + {0x0CB3, POS_BELOW_C}, + {0x0CB5, POS_BELOW_C}, + {0x0CB6, POS_BELOW_C}, + {0x0CB7, POS_BELOW_C}, + {0x0CB8, POS_BELOW_C}, + {0x0CB9, POS_BELOW_C}, + {0x0CDE, POS_BELOW_C}, + {0x0D2F, POS_POST_C}, + {0x0D30, POS_POST_C}, + {0x0D32, POS_BELOW_C}, + {0x0D35, POS_POST_C}, +}; + +/* XXX + * This is a hack for now. We should move this data into the main Indic table. + * Or completely remove it and just check in the tables. + */ +static const hb_codepoint_t ra_chars[] = { + 0x0930, /* Devanagari */ + 0x09B0, /* Bengali */ + 0x09F0, /* Bengali */ + 0x0A30, /* Gurmukhi */ /* No Reph */ + 0x0AB0, /* Gujarati */ + 0x0B30, /* Oriya */ + 0x0BB0, /* Tamil */ /* No Reph */ + 0x0C30, /* Telugu */ /* No Reph */ + 0x0CB0, /* Kannada */ + 0x0D30, /* Malayalam */ /* No Reph */ +}; + + +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */ diff --git a/src/hb-ot-shape-complex-indic-table.hh b/src/hb-ot-shape-complex-indic-table.hh index 2ee27b8..5b4b344 100644 --- a/src/hb-ot-shape-complex-indic-table.hh +++ b/src/hb-ot-shape-complex-indic-table.hh @@ -1,36 +1,3 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH -#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH - -#include "hb-private.hh" - -HB_BEGIN_DECLS - /* == Start of generated table == */ /* * The following table is generated by running: @@ -39,57 +6,60 @@ HB_BEGIN_DECLS * * on files with these headers: * - * # IndicSyllabicCategory-6.0.0.txt - * # Date: 2010-05-25, 11:45:00 PDT [KW] - * # IndicMatraCategory-6.0.0.txt - * # Date: 2010-07-14, 15:03:00 PDT [KW] - * # Blocks-6.0.0.txt - * # Date: 2010-06-04, 11:12:00 PDT [KW] + * # IndicSyllabicCategory-6.1.0.txt + * # Date: 2011-08-31, 23:54:00 GMT [KW] + * # IndicMatraCategory-6.1.0.txt + * # Date: 2011-08-31, 23:50:00 GMT [KW] + * # Blocks-6.1.0.txt + * # Date: 2011-06-14, 18:26:00 GMT [KW, LI] */ +#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH +#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH -#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 9 chars; Avagraha */ -#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 31 chars; Bindu */ -#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 116 chars; Consonant */ + +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 11 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 34 chars; Bindu */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 123 chars; Consonant */ #define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 2 chars; Consonant_Dead */ -#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 16 chars; Consonant_Final */ +#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 17 chars; Consonant_Final */ #define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 1 chars; Consonant_Head_Letter */ #define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 12 chars; Consonant_Medial */ #define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 4 chars; Consonant_Placeholder */ #define ISC_CR INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA /* 5 chars; Consonant_Repha */ -#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 9 chars; Consonant_Subjoined */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 10 chars; Consonant_Subjoined */ #define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ -#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 11 chars; Nukta */ +#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 12 chars; Nukta */ #define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ #define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 1 chars; Register_Shifter */ #define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 3 chars; Tone_Letter */ #define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 16 chars; Tone_Mark */ -#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 29 chars; Virama */ -#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 19 chars; Visarga */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 34 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 25 chars; Visarga */ #define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 5 chars; Vowel */ -#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 161 chars; Vowel_Dependent */ -#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 53 chars; Vowel_Independent */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 165 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 59 chars; Vowel_Independent */ -#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 60 chars; Bottom */ +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 65 chars; Bottom */ #define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ -#define IMC_I INDIC_MATRA_CATEGORY_INVISIBLE /* 4 chars; Invisible */ -#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 25 chars; Left */ +#define IMC_I INDIC_MATRA_CATEGORY_INVISIBLE /* 6 chars; Invisible */ +#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 30 chars; Left */ #define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 8 chars; Left_And_Right */ #define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ #define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 2 chars; Overstruck */ -#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 70 chars; Right */ -#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 74 chars; Top */ -#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 5 chars; Top_And_Bottom */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 75 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 83 chars; Top */ +#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 6 chars; Top_And_Bottom */ #define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ #define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 4 chars; Top_And_Left */ #define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 2 chars; Top_And_Left_And_Right */ -#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 7 chars; Top_And_Right */ +#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 8 chars; Top_And_Right */ #define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 5 chars; Visual_Order_Left */ #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M) -static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { +static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { #define indic_offset_0x0900 0 @@ -535,9 +505,9 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { /* 1B90 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1B98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 1BA0 */ _(C,x), _(CS,x), _(CS,x), _(CS,x), _(M,T), _(M,B), _(M,L), _(M,R), - /* 1BA8 */ _(M,T), _(M,T), _(V,R), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), + /* 1BA8 */ _(M,T), _(M,T), _(V,R), _(V,x), _(CS,x), _(CS,x), _(C,x), _(C,x), /* 1BB0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), - /* 1BB8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1BB8 */ _(x,x), _(x,x), _(A,x), _(C,x), _(C,x), _(C,x), _(CF,x), _(CF,x), /* Batak (1BC0..1BFF) */ @@ -563,7 +533,19 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { /* 1C40 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 1C48 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), -#define indic_offset_0xa800 2976 +#define indic_offset_0x1cd0 2976 + + + /* Vedic Extensions (1CD0..1CFF) */ + + /* 1CD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CE8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0xa800 3024 /* Syloti Nagri (A800..A82F) */ @@ -697,21 +679,28 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { /* AAD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* AAD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0xabc0 3712 + /* Meetei Mayek Extensions (AAE0..AAFF) */ + + /* AAE0 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* AAE8 */ _(C,x), _(C,x), _(C,x), _(M,L), _(M,B), _(M,T), _(M,L), _(M,R), + /* AAF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Vs,x), _(V,I), _(x,x), + /* AAF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0xabc0 3792 /* Meetei Mayek (ABC0..ABFF) */ /* ABC0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* ABC8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), - /* ABD0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* ABC8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), + /* ABD0 */ _(C,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* ABD8 */ _(C,x), _(C,x), _(C,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), _(CF,x), /* ABE0 */ _(CF,x), _(CF,x), _(CF,x), _(M,R), _(M,R), _(M,T), _(M,R), _(M,R), /* ABE8 */ _(M,B), _(M,R), _(M,R), _(x,x), _(TM,x), _(V,B), _(x,x), _(x,x), /* ABF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* ABF8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x10a00 3776 +#define indic_offset_0x10a00 3856 /* Kharoshthi (10A00..10A5F) */ @@ -729,7 +718,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { /* 10A50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 10A58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_0x11000 3872 +#define indic_offset_0x11000 3952 /* Brahmi (11000..1107F) */ @@ -764,9 +753,59 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[4080] = { /* 110C0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 110C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -#define indic_offset_total 4080 +#define indic_offset_0x11100 4160 + + + /* Chakma (11100..1114F) */ + + /* 11100 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), + /* 11108 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11110 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11118 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11120 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,T), + /* 11128 */ _(M,T), _(M,T), _(M,B), _(M,B), _(M,L), _(M,T), _(M,TB), _(M,TB), + /* 11130 */ _(M,T), _(M,B), _(M,B), _(V,I), _(V,T), _(x,x), _(x,x), _(x,x), + /* 11138 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11140 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 11148 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_0x11180 4240 + + + /* Sharada (11180..111DF) */ + + /* 11180 */ _(Bi,x), _(Bi,x), _(Vs,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11188 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11190 */ _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11198 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 111A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 111A8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 111B0 */ _(C,x), _(C,x), _(C,x), _(M,R), _(M,L), _(M,R), _(M,B), _(M,B), + /* 111B8 */ _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), _(M,TR), + /* 111C0 */ _(V,R), _(A,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 111C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 111D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 111D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), -}; +#define indic_offset_0x11680 4336 + + + /* Takri (11680..116CF) */ + + /* 11680 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), + /* 11688 */ _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11690 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 11698 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 116A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), + /* 116A8 */ _(C,x), _(C,x), _(C,x), _(Bi,x), _(Vs,x), _(M,T), _(M,L), _(M,R), + /* 116B0 */ _(M,B), _(M,B), _(M,T), _(M,T), _(M,T), _(M,T), _(V,T), _(N,x), + /* 116B8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 116C0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 116C8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + +#define indic_offset_total 4416 + +}; /* Table occupancy: 60% */ static INDIC_TABLE_ELEMENT_TYPE get_indic_categories (hb_codepoint_t u) @@ -775,10 +814,14 @@ get_indic_categories (hb_codepoint_t u) if (0x1700 <= u && u <= 0x1800) return indic_table[u - 0x1700 + indic_offset_0x1700]; if (0x1900 <= u && u <= 0x1AB0) return indic_table[u - 0x1900 + indic_offset_0x1900]; if (0x1B00 <= u && u <= 0x1C50) return indic_table[u - 0x1B00 + indic_offset_0x1b00]; - if (0xA800 <= u && u <= 0xAAE0) return indic_table[u - 0xA800 + indic_offset_0xa800]; + if (0x1CD0 <= u && u <= 0x1D00) return indic_table[u - 0x1CD0 + indic_offset_0x1cd0]; + if (0xA800 <= u && u <= 0xAB00) return indic_table[u - 0xA800 + indic_offset_0xa800]; if (0xABC0 <= u && u <= 0xAC00) return indic_table[u - 0xABC0 + indic_offset_0xabc0]; if (0x10A00 <= u && u <= 0x10A60) return indic_table[u - 0x10A00 + indic_offset_0x10a00]; if (0x11000 <= u && u <= 0x110D0) return indic_table[u - 0x11000 + indic_offset_0x11000]; + if (0x11100 <= u && u <= 0x11150) return indic_table[u - 0x11100 + indic_offset_0x11100]; + if (0x11180 <= u && u <= 0x111E0) return indic_table[u - 0x11180 + indic_offset_0x11180]; + if (0x11680 <= u && u <= 0x116D0) return indic_table[u - 0x11680 + indic_offset_0x11680]; if (unlikely (u == 0x00A0)) return _(CP,x); if (unlikely (u == 0x25CC)) return _(CP,x); return _(x,x); @@ -824,9 +867,6 @@ get_indic_categories (hb_codepoint_t u) #undef IMC_TR #undef IMC_VOL +#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */ /* == End of generated table == */ - -HB_END_DECLS - -#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */ diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index fceb076..f168fe1 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,232 +24,47 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-complex-indic-private.hh" +#include "hb-ot-shape-private.hh" -HB_BEGIN_DECLS - - -/* buffer var allocations */ -#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */ -#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */ - -#define INDIC_TABLE_ELEMENT_TYPE uint8_t - -/* Cateories used in the OpenType spec: - * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx - */ -/* Note: This enum is duplicated in the -machine.rl source file. - * Not sure how to avoid duplication. */ -enum indic_category_t { - OT_X = 0, - OT_C, - OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */ - OT_V, - OT_N, - OT_H, - OT_ZWNJ, - OT_ZWJ, - OT_M, - OT_SM, - OT_VD, - OT_A, - OT_NBSP +struct indic_options_t +{ + int initialized : 1; + int uniscribe_bug_compatible : 1; }; -/* Visual positions in a syllable from left to right. */ -enum indic_position_t { - POS_PRE, - POS_BASE, - POS_ABOVE, - POS_BELOW, - POS_POST, +union indic_options_union_t { + int i; + indic_options_t opts; }; +ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t)); -/* Categories used in IndicSyllabicCategory.txt from UCD */ -/* The assignments are guesswork */ -enum indic_syllabic_category_t { - INDIC_SYLLABIC_CATEGORY_OTHER = OT_X, - - INDIC_SYLLABIC_CATEGORY_AVAGRAHA = OT_X, - INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM, - INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_NBSP, - INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_C, - INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA = OT_C, - INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X, - INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N, - INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_X, - INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X, - INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_X, - INDIC_SYLLABIC_CATEGORY_VIRAMA = OT_H, - INDIC_SYLLABIC_CATEGORY_VISARGA = OT_SM, - INDIC_SYLLABIC_CATEGORY_VOWEL = OT_V, - INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT = OT_M, - INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT = OT_V -}; +static indic_options_union_t +indic_options_init (void) +{ + indic_options_union_t u; + u.i = 0; + u.opts.initialized = 1; -/* Categories used in IndicSMatraCategory.txt from UCD */ -enum indic_matra_category_t { - INDIC_MATRA_CATEGORY_NOT_APPLICABLE = POS_BASE, - - INDIC_MATRA_CATEGORY_LEFT = POS_PRE, - INDIC_MATRA_CATEGORY_TOP = POS_ABOVE, - INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW, - INDIC_MATRA_CATEGORY_RIGHT = POS_POST, - - /* We don't really care much about these since we decompose them - * in the generic pre-shaping layer. They will only be used if - * the font does not cover the decomposition. In which case, we - * define these as aliases to the place we want the split-matra - * glyph to show up. Quite arbitrary. */ - INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_BOTTOM, - INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_LEFT, - INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM, - INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_BOTTOM, - INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_LEFT, - INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_LEFT, - INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, - - INDIC_MATRA_CATEGORY_INVISIBLE = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, - INDIC_MATRA_CATEGORY_OVERSTRUCK = INDIC_MATRA_CATEGORY_NOT_APPLICABLE, - INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = INDIC_MATRA_CATEGORY_NOT_APPLICABLE -}; + char *c = getenv ("HB_OT_INDIC_OPTIONS"); + u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); + + return u; +} -/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation - * because gcc fails to optimize the latter and fills the table in at runtime. */ -#define INDIC_COMBINE_CATEGORIES(S,M) \ - (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \ - ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \ - ((M << 4) | S)) +inline indic_options_t +indic_options (void) +{ + static indic_options_union_t options; -#include "hb-ot-shape-complex-indic-table.hh" + if (unlikely (!options.i)) { + /* This is idempotent and threadsafe. */ + options = indic_options_init (); + } -/* XXX - * This is a hack for now. We should: - * 1. Move this data into the main Indic table, - * and/or - * 2. Probe font lookups to determine consonant positions. - */ -static const struct consonant_position_t { - hb_codepoint_t u; - indic_position_t position; -} consonant_positions[] = { - {0x0930, POS_BELOW}, - {0x09AC, POS_BELOW}, - {0x09AF, POS_POST}, - {0x09B0, POS_BELOW}, - {0x09F0, POS_BELOW}, - {0x0A2F, POS_POST}, - {0x0A30, POS_BELOW}, - {0x0A35, POS_BELOW}, - {0x0A39, POS_BELOW}, - {0x0AB0, POS_BELOW}, - {0x0B24, POS_BELOW}, - {0x0B28, POS_BELOW}, - {0x0B2C, POS_BELOW}, - {0x0B2D, POS_BELOW}, - {0x0B2E, POS_BELOW}, - {0x0B2F, POS_POST}, - {0x0B30, POS_BELOW}, - {0x0B32, POS_BELOW}, - {0x0B33, POS_BELOW}, - {0x0B5F, POS_POST}, - {0x0B71, POS_BELOW}, - {0x0C15, POS_BELOW}, - {0x0C16, POS_BELOW}, - {0x0C17, POS_BELOW}, - {0x0C18, POS_BELOW}, - {0x0C19, POS_BELOW}, - {0x0C1A, POS_BELOW}, - {0x0C1B, POS_BELOW}, - {0x0C1C, POS_BELOW}, - {0x0C1D, POS_BELOW}, - {0x0C1E, POS_BELOW}, - {0x0C1F, POS_BELOW}, - {0x0C20, POS_BELOW}, - {0x0C21, POS_BELOW}, - {0x0C22, POS_BELOW}, - {0x0C23, POS_BELOW}, - {0x0C24, POS_BELOW}, - {0x0C25, POS_BELOW}, - {0x0C26, POS_BELOW}, - {0x0C27, POS_BELOW}, - {0x0C28, POS_BELOW}, - {0x0C2A, POS_BELOW}, - {0x0C2B, POS_BELOW}, - {0x0C2C, POS_BELOW}, - {0x0C2D, POS_BELOW}, - {0x0C2E, POS_BELOW}, - {0x0C2F, POS_BELOW}, - {0x0C30, POS_BELOW}, - {0x0C32, POS_BELOW}, - {0x0C33, POS_BELOW}, - {0x0C35, POS_BELOW}, - {0x0C36, POS_BELOW}, - {0x0C37, POS_BELOW}, - {0x0C38, POS_BELOW}, - {0x0C39, POS_BELOW}, - {0x0C95, POS_BELOW}, - {0x0C96, POS_BELOW}, - {0x0C97, POS_BELOW}, - {0x0C98, POS_BELOW}, - {0x0C99, POS_BELOW}, - {0x0C9A, POS_BELOW}, - {0x0C9B, POS_BELOW}, - {0x0C9C, POS_BELOW}, - {0x0C9D, POS_BELOW}, - {0x0C9E, POS_BELOW}, - {0x0C9F, POS_BELOW}, - {0x0CA0, POS_BELOW}, - {0x0CA1, POS_BELOW}, - {0x0CA2, POS_BELOW}, - {0x0CA3, POS_BELOW}, - {0x0CA4, POS_BELOW}, - {0x0CA5, POS_BELOW}, - {0x0CA6, POS_BELOW}, - {0x0CA7, POS_BELOW}, - {0x0CA8, POS_BELOW}, - {0x0CAA, POS_BELOW}, - {0x0CAB, POS_BELOW}, - {0x0CAC, POS_BELOW}, - {0x0CAD, POS_BELOW}, - {0x0CAE, POS_BELOW}, - {0x0CAF, POS_BELOW}, - {0x0CB0, POS_BELOW}, - {0x0CB2, POS_BELOW}, - {0x0CB3, POS_BELOW}, - {0x0CB5, POS_BELOW}, - {0x0CB6, POS_BELOW}, - {0x0CB7, POS_BELOW}, - {0x0CB8, POS_BELOW}, - {0x0CB9, POS_BELOW}, - {0x0CDE, POS_BELOW}, - {0x0D2F, POS_POST}, - {0x0D30, POS_POST}, - {0x0D32, POS_BELOW}, - {0x0D35, POS_POST}, -}; + return options.opts; +} -/* XXX - * This is a hack for now. We should move this data into the main Indic table. - */ -static const hb_codepoint_t ra_chars[] = { - 0x0930, /* Devanagari */ - 0x09B0, /* Bengali */ - 0x09F0, /* Bengali */ -//0x09F1, /* Bengali */ -//0x0A30, /* Gurmukhi */ - 0x0AB0, /* Gujarati */ - 0x0B30, /* Oriya */ -//0x0BB0, /* Tamil */ -//0x0C30, /* Telugu */ - 0x0CB0, /* Kannada */ -//0x0D30, /* Malayalam */ -}; static int compare_codepoint (const void *pa, const void *pb) @@ -270,7 +85,7 @@ consonant_position (hb_codepoint_t u) sizeof (consonant_positions[0]), compare_codepoint); - return record ? record->position : POS_BASE; + return record ? record->position : POS_BASE_C; } static bool @@ -291,24 +106,32 @@ is_joiner (const hb_glyph_info_t &info) static bool is_consonant (const hb_glyph_info_t &info) { - return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra))); + /* Note: + * + * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels + * cannot happen in a consonant syllable. The plus side however is, we can call the + * consonant syllable logic from the vowel syllable function and get it all right! */ + return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))); } -static const struct { +struct feature_list_t { hb_tag_t tag; hb_bool_t is_global; -} indic_basic_features[] = +}; + +static const feature_list_t +indic_basic_features[] = { {HB_TAG('n','u','k','t'), true}, {HB_TAG('a','k','h','n'), false}, {HB_TAG('r','p','h','f'), false}, - {HB_TAG('r','k','r','f'), false}, + {HB_TAG('r','k','r','f'), true}, {HB_TAG('p','r','e','f'), false}, {HB_TAG('b','l','w','f'), false}, {HB_TAG('h','a','l','f'), false}, - {HB_TAG('v','a','t','u'), true}, {HB_TAG('p','s','t','f'), false}, {HB_TAG('c','j','c','t'), false}, + {HB_TAG('v','a','t','u'), true}, }; /* Same order as the indic_basic_features array */ @@ -316,26 +139,33 @@ enum { _NUKT, AKHN, RPHF, - RKRF, + _RKRF, PREF, BLWF, HALF, - _VATU, PSTF, CJCT, + VATU }; -static const hb_tag_t indic_other_features[] = +static const feature_list_t +indic_other_features[] = { - HB_TAG('p','r','e','s'), - HB_TAG('a','b','v','s'), - HB_TAG('b','l','w','s'), - HB_TAG('p','s','t','s'), - HB_TAG('h','a','l','n'), - - HB_TAG('d','i','s','t'), - HB_TAG('a','b','v','m'), - HB_TAG('b','l','w','m'), + {HB_TAG('i','n','i','t'), false}, + {HB_TAG('p','r','e','s'), true}, + {HB_TAG('a','b','v','s'), true}, + {HB_TAG('b','l','w','s'), true}, + {HB_TAG('p','s','t','s'), true}, + {HB_TAG('h','a','l','n'), true}, + + {HB_TAG('d','i','s','t'), true}, + {HB_TAG('a','b','v','m'), true}, + {HB_TAG('b','l','w','m'), true}, +}; + +/* Same order as the indic_other_features array */ +enum { + INIT }; @@ -351,7 +181,8 @@ final_reordering (const hb_ot_map_t *map, void *user_data HB_UNUSED); void -_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_segment_properties_t *props) +_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, + const hb_segment_properties_t *props HB_UNUSED) { map->add_bool_feature (HB_TAG('l','o','c','l')); /* The Indic specs do not require ccmp, but we apply it here since if @@ -360,26 +191,32 @@ _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_ map->add_gsub_pause (initial_reordering, NULL); - for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) + for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) { map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global); + map->add_gsub_pause (NULL, NULL); + } map->add_gsub_pause (final_reordering, NULL); - for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) - map->add_bool_feature (indic_other_features[i], true); + for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) { + map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global); + map->add_gsub_pause (NULL, NULL); + } } -bool -_hb_ot_shape_complex_prefer_decomposed_indic (void) +hb_ot_shape_normalization_mode_t +_hb_ot_shape_complex_normalization_preference_indic (void) { /* We want split matras decomposed by the common shaping logic. */ - return TRUE; + return HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; } void -_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer) +_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) { HB_BUFFER_ALLOCATE_VAR (buffer, indic_category); HB_BUFFER_ALLOCATE_VAR (buffer, indic_position); @@ -390,19 +227,36 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer) unsigned int count = buffer->len; for (unsigned int i = 0; i < count; i++) { - unsigned int type = get_indic_categories (buffer->info[i].codepoint); - - buffer->info[i].indic_category() = type & 0x0F; - buffer->info[i].indic_position() = type >> 4; - - if (buffer->info[i].indic_category() == OT_C) { - buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint); - if (is_ra (buffer->info[i].codepoint)) - buffer->info[i].indic_category() = OT_Ra; - } else if (buffer->info[i].codepoint == 0x200C) - buffer->info[i].indic_category() = OT_ZWNJ; - else if (buffer->info[i].codepoint == 0x200D) - buffer->info[i].indic_category() = OT_ZWJ; + hb_glyph_info_t &info = buffer->info[i]; + unsigned int type = get_indic_categories (info.codepoint); + + info.indic_category() = type & 0x0F; + info.indic_position() = type >> 4; + + /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe + * treats U+0951..U+0952 all as OT_VD. + * TESTS: + * U+092E,U+0947,U+0952 + * U+092E,U+0952,U+0947 + * U+092E,U+0947,U+0951 + * U+092E,U+0951,U+0947 + * */ + if (unlikely (hb_in_range (info.codepoint, 0x0951, 0x0954))) + info.indic_category() = OT_VD; + + if (info.indic_category() == OT_C) { + info.indic_position() = consonant_position (info.codepoint); + if (is_ra (info.codepoint)) + info.indic_category() = OT_Ra; + } else if (info.indic_category() == OT_SM || + info.indic_category() == OT_VD) { + info.indic_position() = POS_SMVD; + } else if (unlikely (info.codepoint == 0x200C)) + info.indic_category() = OT_ZWNJ; + else if (unlikely (info.codepoint == 0x200D)) + info.indic_category() = OT_ZWJ; + else if (unlikely (info.codepoint == 0x25CC)) + info.indic_category() = OT_DOTTEDCIRCLE; } } @@ -415,15 +269,15 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) return a < b ? -1 : a == b ? 0 : +1; } +/* Rules from: + * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */ + static void -found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, - unsigned int start, unsigned int end) +initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, + unsigned int start, unsigned int end) { - unsigned int i; hb_glyph_info_t *info = buffer->info; - /* Comments from: - * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */ /* 1. Find base consonant: * @@ -440,52 +294,65 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t */ unsigned int base = end; + bool has_reph = false; - /* -> starting from the end of the syllable, move backwards */ - i = end; - unsigned int limit = start; - if (info[start].indic_category() == OT_Ra && start + 2 <= end) { - limit += 2; - base = start; - }; - do { - i--; - /* -> until a consonant is found */ - if (is_consonant (info[i])) + { + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. */ + unsigned int limit = start; + if (mask_array[RPHF] && + start + 3 <= end && + info[start].indic_category() == OT_Ra && + info[start + 1].indic_category() == OT_H && + !is_joiner (info[start + 2])) { - /* -> that does not have a below-base or post-base form - * (post-base forms have to follow below-base forms), */ - if (info[i].indic_position() != POS_BELOW && - info[i].indic_position() != POS_POST) + limit += 2; + base = start; + has_reph = true; + }; + + /* -> starting from the end of the syllable, move backwards */ + unsigned int i = end; + do { + i--; + /* -> until a consonant is found */ + if (is_consonant (info[i])) { - base = i; - break; - } - - /* -> or that is not a pre-base reordering Ra, - * - * TODO - */ + /* -> that does not have a below-base or post-base form + * (post-base forms have to follow below-base forms), */ + if (info[i].indic_position() != POS_BELOW_C && + info[i].indic_position() != POS_POST_C) + { + base = i; + break; + } - /* -> o If the syllable starts with Ra + Halant (in a script that has Reph) - * and has more than one consonant, Ra is excluded from candidates for - * base consonants. - * - * IMPLEMENTATION NOTES: - * - * We do this by adjusting limit accordingly before entering the loop. - */ + /* -> or that is not a pre-base reordering Ra, + * + * TODO + */ - /* -> or arrive at the first consonant. The consonant stopped at will - * be the base. */ - base = i; + /* -> or arrive at the first consonant. The consonant stopped at will + * be the base. */ + base = i; + } + else + if (is_joiner (info[i])) + break; + } while (i > limit); + if (base < start) + base = start; /* Just in case... */ + + + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. */ + if (has_reph && base == start) { + /* Have no other consonant, so Reph is not formed and Ra becomes base. */ + has_reph = false; } - else - if (is_joiner (info[i])) - break; - } while (i > limit); - if (base < start) - base = start; /* Just in case... */ + } /* 2. Decompose and reorder Matras: @@ -522,30 +389,23 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t /* Reorder characters */ - for (i = start; i < base; i++) - info[i].indic_position() = POS_PRE; - info[base].indic_position() = POS_BASE; - + for (unsigned int i = start; i < base; i++) + info[i].indic_position() = POS_PRE_C; + info[base].indic_position() = POS_BASE_C; /* Handle beginning Ra */ - if (start + 3 <= end && - info[start].indic_category() == OT_Ra && - info[start + 1].indic_category() == OT_H && - !is_joiner (info[start + 2])) - { - info[start].indic_position() = POS_POST; - info[start].mask = mask_array[RPHF]; - } + if (has_reph) + info[start].indic_position() = POS_RA_TO_BECOME_REPH; /* For old-style Indic script tags, move the first post-base Halant after * last consonant. */ if ((map->get_chosen_script (0) & 0x000000FF) != '2') { /* We should only do this for Indic scripts which have a version two I guess. */ - for (i = base + 1; i < end; i++) + for (unsigned int i = base + 1; i < end; i++) if (info[i].indic_category() == OT_H) { unsigned int j; for (j = end - 1; j > i; j--) - if ((FLAG (info[j].indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra)))) + if (is_consonant (info[j])) break; if (j > i) { /* Move Halant to after last consonant. */ @@ -558,38 +418,68 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t } /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */ - for (i = start + 1; i < end; i++) - if ((FLAG (info[i].indic_category()) & - (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) - info[i].indic_position() = info[i - 1].indic_position(); + if (!indic_options ().uniscribe_bug_compatible) + { + /* Please update the Uniscribe branch when touching this! */ + for (unsigned int i = start + 1; i < end; i++) + if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) + info[i].indic_position() = info[i - 1].indic_position(); + } else { + /* + * Uniscribe doesn't move the Halant with Left Matra. + * TEST: U+092B,U+093F,U+094DE + */ + /* Please update the non-Uniscribe branch when touching this! */ + for (unsigned int i = start + 1; i < end; i++) + if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) { + info[i].indic_position() = info[i - 1].indic_position(); + if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M) + for (unsigned int j = i; j > start; j--) + if (info[j - 1].indic_position() != POS_PRE_M) { + info[i].indic_position() = info[j - 1].indic_position(); + break; + } + } + } /* We do bubble-sort, skip malicious clusters attempts */ - if (end - start > 20) - return; - - /* Sit tight, rock 'n roll! */ - hb_bubble_sort (info + start, end - start, compare_indic_order); + if (end - start < 64) + { + /* Sit tight, rock 'n roll! */ + hb_bubble_sort (info + start, end - start, compare_indic_order); + /* Find base again */ + base = end; + for (unsigned int i = start; i < end; i++) + if (info[i].indic_position() == POS_BASE_C) { + base = i; + break; + } + } /* Setup masks now */ { hb_mask_t mask; + /* Reph */ + for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) + info[i].mask |= mask_array[RPHF]; + /* Pre-base */ mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT]; - for (i = start; i < base; i++) + for (unsigned int i = start; i < base; i++) info[i].mask |= mask; /* Base */ mask = mask_array[AKHN] | mask_array[CJCT]; info[base].mask |= mask; /* Post-base */ mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT]; - for (i = base + 1; i < end; i++) + for (unsigned int i = base + 1; i < end; i++) info[i].mask |= mask; } /* Apply ZWJ/ZWNJ effects */ - for (i = start + 1; i < end; i++) + for (unsigned int i = start + 1; i < end; i++) if (is_joiner (info[i])) { bool non_joiner = info[i].indic_category() == OT_ZWNJ; unsigned int j = i; @@ -597,14 +487,9 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t do { j--; - /* Reading the Unicode and OpenType specs, I think the following line - * is correct, but this is not what the test suite expects currently. - * The test suite has been drinking, not me... But disable while - * investigating. - */ - //info[j].mask &= !mask_array[CJCT]; + info[j].mask &= ~mask_array[CJCT]; if (non_joiner) - info[j].mask &= !mask_array[HALF]; + info[j].mask &= ~mask_array[HALF]; } while (j > start && !is_consonant (info[j])); } @@ -612,28 +497,41 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t static void -found_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, - unsigned int start, unsigned int end) +initial_reordering_vowel_syllable (const hb_ot_map_t *map, + hb_buffer_t *buffer, + hb_mask_t *mask_array, + unsigned int start, unsigned int end) { - /* TODO - * Not clear to me how this should work. Do the matras move to before the - * independent vowel? No idea. - */ + /* We made the vowels look like consonants. So let's call the consonant logic! */ + initial_reordering_consonant_syllable (map, buffer, mask_array, start, end); } static void -found_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, - unsigned int start, unsigned int end) +initial_reordering_standalone_cluster (const hb_ot_map_t *map, + hb_buffer_t *buffer, + hb_mask_t *mask_array, + unsigned int start, unsigned int end) { - /* TODO - * Easiest thing to do here is to convert the NBSP to consonant and - * call found_consonant_syllable. - */ + /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain. + * Only if not in compatibility mode that is... */ + + if (indic_options ().uniscribe_bug_compatible) + { + /* For dotted-circle, this is what Uniscribe does: + * If dotted-circle is the last glyph, it just does nothing. + * Ie. It doesn't form Reph. */ + if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE) + return; + } + + initial_reordering_consonant_syllable (map, buffer, mask_array, start, end); } static void -found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array, - unsigned int start, unsigned int end) +initial_reordering_non_indic (const hb_ot_map_t *map HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + hb_mask_t *mask_array HB_UNUSED, + unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) { /* Nothing to do right now. If we ever switch to using the output * buffer in the reordering process, we'd need to next_glyph() here. */ @@ -642,22 +540,8 @@ found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_ar #include "hb-ot-shape-complex-indic-machine.hh" static void -remove_joiners (hb_buffer_t *buffer) -{ - buffer->clear_output (); - unsigned int count = buffer->len; - for (buffer->idx = 0; buffer->idx < count;) - if (unlikely (is_joiner (buffer->info[buffer->idx]))) - buffer->skip_glyph (); - else - buffer->next_glyph (); - - buffer->swap_buffers (); -} - -static void initial_reordering (const hb_ot_map_t *map, - hb_face_t *face, + hb_face_t *face HB_UNUSED, hb_buffer_t *buffer, void *user_data HB_UNUSED) { @@ -667,24 +551,40 @@ initial_reordering (const hb_ot_map_t *map, mask_array[i] = map->get_1_mask (indic_basic_features[i].tag); find_syllables (map, buffer, mask_array); - - remove_joiners (buffer); } static void -final_reordering (const hb_ot_map_t *map, - hb_face_t *face, - hb_buffer_t *buffer, - void *user_data HB_UNUSED) +final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array, + unsigned int start, unsigned int end) { + hb_glyph_info_t *info = buffer->info; + /* 4. Final reordering: * * After the localized forms and basic shaping forms GSUB features have been * applied (see below), the shaping engine performs some final glyph * reordering before applying all the remaining font features to the entire * cluster. - * - * o Reorder matras: + */ + + /* Find base again */ + unsigned int base = end; + for (unsigned int i = start; i < end; i++) + if (info[i].indic_position() == POS_BASE_C) { + base = i; + break; + } + + if (base == start) { + /* There's no Reph, and no left Matra to reposition. Just merge the cluster + * and go home. */ + buffer->merge_clusters (start, end); + return; + } + + unsigned int start_of_last_cluster = base; + + /* o Reorder matras: * * If a pre-base matra character had been reordered before applying basic * features, the glyph can be moved closer to the main consonant based on @@ -692,49 +592,197 @@ final_reordering (const hb_ot_map_t *map, * defined as “after last standalone halant glyph, after initial matra * position and before the main consonant”. If ZWJ or ZWNJ follow this * halant, position is moved after it. - * - * o Reorder reph: + */ + + { + unsigned int new_matra_pos = base - 1; + while (new_matra_pos > start && + !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H)))) + new_matra_pos--; + /* If we found no Halant we are done. Otherwise only proceed if the Halant does + * not belong to the Matra itself! */ + if (info[new_matra_pos].indic_category() == OT_H && + info[new_matra_pos].indic_position() != POS_PRE_M) { + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1])) + new_matra_pos++; + + /* Now go see if there's actually any matras... */ + for (unsigned int i = new_matra_pos; i > start; i--) + if (info[i - 1].indic_position () == POS_PRE_M) + { + unsigned int old_matra_pos = i - 1; + hb_glyph_info_t matra = info[old_matra_pos]; + memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0])); + info[new_matra_pos] = matra; + start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster); + new_matra_pos--; + } + } + } + + + /* o Reorder reph: * * Reph’s original position is always at the beginning of the syllable, * (i.e. it is not reordered at the character reordering stage). However, * it will be reordered according to the basic-forms shaping results. * Possible positions for reph, depending on the script, are; after main, * before post-base consonant forms, and after post-base consonant forms. - * - * 1. If reph should be positioned after post-base consonant forms, - * proceed to step 5. - * - * 2. If the reph repositioning class is not after post-base: target - * position is after the first explicit halant glyph between the - * first post-reph consonant and last main consonant. If ZWJ or ZWNJ - * are following this halant, position is moved after it. If such - * position is found, this is the target position. Otherwise, - * proceed to the next step. - * - * Note: in old-implementation fonts, where classifications were - * fixed in shaping engine, there was no case where reph position - * will be found on this step. - * - * 3. If reph should be repositioned after the main consonant: from the - * first consonant not ligated with main, or find the first - * consonant that is not a potential pre-base reordering Ra. - * - * - * 4. If reph should be positioned before post-base consonant, find - * first post-base classified consonant not ligated with main. If no - * consonant is found, the target position should be before the - * first matra, syllable modifier sign or vedic sign. - * - * 5. If no consonant is found in steps 3 or 4, move reph to a position - * immediately before the first post-base matra, syllable modifier - * sign or vedic sign that has a reordering class after the intended - * reph position. For example, if the reordering position for reph - * is post-main, it will skip above-base matras that also have a - * post-main position. - * - * 6. Otherwise, reorder reph to the end of the syllable. - * - * o Reorder pre-base reordering consonants: + */ + + /* If there's anything after the Ra that has the REPH pos, it ought to be halant. + * Which means that the font has failed to ligate the Reph. In which case, we + * shouldn't move. */ + if (start + 1 < end && + info[start].indic_position() == POS_RA_TO_BECOME_REPH && + info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH) + { + unsigned int new_reph_pos; + + enum reph_position_t { + REPH_AFTER_MAIN, + REPH_BEFORE_SUBSCRIPT, + REPH_AFTER_SUBSCRIPT, + REPH_BEFORE_POSTSCRIPT, + REPH_AFTER_POSTSCRIPT + } reph_pos; + + /* XXX Figure out old behavior too */ + switch ((hb_tag_t) buffer->props.script) + { + case HB_SCRIPT_MALAYALAM: + case HB_SCRIPT_ORIYA: + reph_pos = REPH_AFTER_MAIN; + break; + + case HB_SCRIPT_GURMUKHI: + reph_pos = REPH_BEFORE_SUBSCRIPT; + break; + + case HB_SCRIPT_BENGALI: + reph_pos = REPH_AFTER_SUBSCRIPT; + break; + + default: + case HB_SCRIPT_DEVANAGARI: + case HB_SCRIPT_GUJARATI: + reph_pos = REPH_BEFORE_POSTSCRIPT; + break; + + case HB_SCRIPT_KANNADA: + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_TELUGU: + reph_pos = REPH_AFTER_POSTSCRIPT; + break; + } + + /* 1. If reph should be positioned after post-base consonant forms, + * proceed to step 5. + */ + if (reph_pos == REPH_AFTER_POSTSCRIPT) + { + goto reph_step_5; + } + + /* 2. If the reph repositioning class is not after post-base: target + * position is after the first explicit halant glyph between the + * first post-reph consonant and last main consonant. If ZWJ or ZWNJ + * are following this halant, position is moved after it. If such + * position is found, this is the target position. Otherwise, + * proceed to the next step. + * + * Note: in old-implementation fonts, where classifications were + * fixed in shaping engine, there was no case where reph position + * will be found on this step. + */ + { + new_reph_pos = start + 1; + while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H) + new_reph_pos++; + + if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) { + /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */ + if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) + new_reph_pos++; + goto reph_move; + } + } + + /* 3. If reph should be repositioned after the main consonant: find the + * first consonant not ligated with main, or find the first + * consonant that is not a potential pre-base reordering Ra. + */ + if (reph_pos == REPH_AFTER_MAIN) + { + /* XXX */ + } + + /* 4. If reph should be positioned before post-base consonant, find + * first post-base classified consonant not ligated with main. If no + * consonant is found, the target position should be before the + * first matra, syllable modifier sign or vedic sign. + */ + /* This is our take on what step 4 is trying to say (and failing, BADLY). */ + if (reph_pos == REPH_AFTER_SUBSCRIPT) + { + new_reph_pos = base; + while (new_reph_pos < end && + !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD)))) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 5. If no consonant is found in steps 3 or 4, move reph to a position + * immediately before the first post-base matra, syllable modifier + * sign or vedic sign that has a reordering class after the intended + * reph position. For example, if the reordering position for reph + * is post-main, it will skip above-base matras that also have a + * post-main position. + */ + reph_step_5: + { + /* XXX */ + } + + /* 6. Otherwise, reorder reph to the end of the syllable. + */ + { + new_reph_pos = end - 1; + while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD) + new_reph_pos--; + + /* + * If the Reph is to be ending up after a Matra,Halant sequence, + * position it before that Halant so it can interact with the Matra. + * However, if it's a plain Consonant,Halant we shouldn't do that. + * Uniscribe doesn't do this. + * TEST: U+0930,U+094D,U+0915,U+094B,U+094D + */ + if (!indic_options ().uniscribe_bug_compatible && + unlikely (info[new_reph_pos].indic_category() == OT_H)) { + for (unsigned int i = base + 1; i < new_reph_pos; i++) + if (info[i].indic_category() == OT_M) { + /* Ok, got it. */ + new_reph_pos--; + } + } + goto reph_move; + } + + reph_move: + { + /* Move */ + hb_glyph_info_t reph = info[start]; + memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0])); + info[new_reph_pos] = reph; + start_of_last_cluster = start; /* Yay, one big cluster! */ + } + } + + + /* o Reorder pre-base reordering consonants: * * If a pre-base reordering consonant is found, reorder it according to * the following rules: @@ -754,10 +802,72 @@ final_reordering (const hb_ot_map_t *map, + /* Apply 'init' to the Left Matra if it's a word start. */ + if (info[start].indic_position () == POS_PRE_M && + (!start || + !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) & + (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))) + info[start].mask |= mask_array[INIT]; + + + + /* Finish off the clusters and go home! */ + + if (!indic_options ().uniscribe_bug_compatible) + { + /* This is what Uniscribe does. Ie. add cluster boundaries after Halant,ZWNJ. + * This means, half forms are submerged into the main consonants cluster. + * This is unnecessary, and makes cursor positioning harder, but that's what + * Uniscribe does. */ + unsigned int cluster_start = start; + for (unsigned int i = start + 1; i < start_of_last_cluster; i++) + if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) { + i++; + buffer->merge_clusters (cluster_start, i); + cluster_start = i; + } + start_of_last_cluster = cluster_start; + } + + buffer->merge_clusters (start_of_last_cluster, end); +} + + +static void +final_reordering (const hb_ot_map_t *map, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + void *user_data HB_UNUSED) +{ + unsigned int count = buffer->len; + if (!count) return; + + hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0}; + unsigned int num_masks = ARRAY_LENGTH (indic_other_features); + for (unsigned int i = 0; i < num_masks; i++) + mask_array[i] = map->get_1_mask (indic_other_features[i].tag); + + hb_glyph_info_t *info = buffer->info; + unsigned int last = 0; + unsigned int last_syllable = info[0].syllable(); + for (unsigned int i = 1; i < count; i++) + if (last_syllable != info[i].syllable()) { + final_reordering_syllable (buffer, mask_array, last, i); + last = i; + last_syllable = info[last].syllable(); + } + final_reordering_syllable (buffer, mask_array, last, count); + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); } -HB_END_DECLS diff --git a/src/hb-ot-shape-complex-misc.cc b/src/hb-ot-shape-complex-misc.cc index b2de3ff..d93d4c6 100644 --- a/src/hb-ot-shape-complex-misc.cc +++ b/src/hb-ot-shape-complex-misc.cc @@ -26,9 +26,8 @@ #include "hb-ot-shape-complex-private.hh" -HB_BEGIN_DECLS -/* TODO Add kana, hangul, and other small sahpers here */ +/* TODO Add kana, and other small shapers here */ /* When adding trivial shapers, eg. kana, hangul, etc, we can either * add a full shaper enum value for them, or switch on the script in @@ -38,20 +37,157 @@ HB_BEGIN_DECLS */ void -_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map, const hb_segment_properties_t *props) +_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSED, + const hb_segment_properties_t *props HB_UNUSED) { } -bool -_hb_ot_shape_complex_prefer_decomposed_default (void) +hb_ot_shape_normalization_mode_t +_hb_ot_shape_complex_normalization_preference_default (void) { - return FALSE; + return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; } void -_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map, hb_buffer_t *buffer) +_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + hb_font_t *font HB_UNUSED) { } -HB_END_DECLS + +/* Hangul shaper */ + +static const hb_tag_t hangul_features[] = +{ + HB_TAG('l','j','m','o'), + HB_TAG('v','j','m','o'), + HB_TAG('t','j','m','o'), +}; + +void +_hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map, + const hb_segment_properties_t *props HB_UNUSED) +{ + for (unsigned int i = 0; i < ARRAY_LENGTH (hangul_features); i++) + map->add_bool_feature (hangul_features[i]); +} + +hb_ot_shape_normalization_mode_t +_hb_ot_shape_complex_normalization_preference_hangul (void) +{ + return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; +} + +void +_hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map HB_UNUSED, + hb_buffer_t *buffer HB_UNUSED, + hb_font_t *font HB_UNUSED) +{ +} + + + +/* Thai / Lao shaper */ + +void +_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED, + const hb_segment_properties_t *props HB_UNUSED) +{ +} + +hb_ot_shape_normalization_mode_t +_hb_ot_shape_complex_normalization_preference_thai (void) +{ + return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; +} + +void +_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + /* The following is NOT specified in the MS OT Thai spec, however, it seems + * to be what Uniscribe and other engines implement. According to Eric Muller: + * + * When you have a sara am, decompose it in nikhahit + sara a, *and* mode the + * nihka hit backwards over any *tone* mark (0E48-0E4B). + * + * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32> + * + * This reordering is legit only when the nikhahit comes from a sara am, not + * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably + * not what a u↪ser wanted, but the rendering is nevertheless nikhahit above + * chattawa. + * + * Same for Lao. + */ + + /* + * Here are the characters of significance: + * + * Thai Lao + * SARA AM: U+0E33 U+0EB3 + * SARA AA: U+0E32 U+0EB2 + * Nikhahit: U+0E4D U+0ECD + * + * Tone marks: + * Thai: <0E48..0E4B> CCC=107 + * Lao: <0EC8..0ECB> CCC=122 + * + * Note how the Lao versions are the same as Thai + 0x80. + */ + + /* We only get one script at a time, so a script-agnostic implementation + * is adequate here. */ +#define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33) +#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D) +#define SARA_AA_FROM_SARA_AM(x) ((x) - 1) +#define IS_TONE_MARK(x) (((x) & ~0x0083) == 0x0E48) + + buffer->clear_output (); + unsigned int count = buffer->len; + for (buffer->idx = 0; buffer->idx < count;) + { + hb_codepoint_t u = buffer->cur().codepoint; + if (likely (!IS_SARA_AM (u))) { + buffer->next_glyph (); + continue; + } + + /* Is SARA AM. Decompose and reorder. */ + hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)), + hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))}; + buffer->replace_glyphs (1, 2, decomposed); + if (unlikely (buffer->in_error)) + return; + + /* Ok, let's see... */ + unsigned int end = buffer->out_len; + unsigned int start = end - 2; + while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint)) + start--; + + /* Move Nikhahit (end-2) to the beginning */ + hb_glyph_info_t t = buffer->out_info[end - 2]; + memmove (buffer->out_info + start + 1, + buffer->out_info + start, + sizeof (buffer->out_info[0]) * (end - start - 2)); + buffer->out_info[start] = t; + + /* XXX Make this easier! */ + /* Make cluster */ + for (; start > 0 && buffer->out_info[start - 1].cluster == buffer->out_info[start].cluster; start--) + ; + for (; buffer->idx < count;) + if (buffer->cur().cluster == buffer->prev().cluster) + buffer->next_glyph (); + else + break; + end = buffer->out_len; + + buffer->merge_out_clusters (start, end); + } + buffer->swap_buffers (); +} diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh index 1aca595..e0d93a6 100644 --- a/src/hb-ot-shape-complex-private.hh +++ b/src/hb-ot-shape-complex-private.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2010,2011 Google, Inc. + * Copyright © 2010,2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -30,94 +30,189 @@ #include "hb-private.hh" #include "hb-ot-map-private.hh" +#include "hb-ot-shape-normalize-private.hh" -HB_BEGIN_DECLS /* buffer var allocations, used during the entire shaping process */ -#define general_category() var1.u8[0] /* unicode general_category (hb_unicode_general_category_t) */ -#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */ +#define unicode_props0() var1.u8[0] +#define unicode_props1() var1.u8[1] + +/* buffer var allocations, used during the GSUB/GPOS processing */ +#define props_cache() var1.u16[1] /* GSUB/GPOS glyph_props cache */ +#define syllable() var2.u8[0] /* GSUB/GPOS shaping boundaries */ +#define lig_props() var2.u8[1] /* GSUB/GPOS ligature tracking */ /* buffer var allocations, used by complex shapers */ -#define complex_var_persistent_u8_0() var2.u8[0] -#define complex_var_persistent_u8_1() var2.u8[1] -#define complex_var_persistent_u16() var2.u16[0] -#define complex_var_temporary_u8_0() var2.u8[2] -#define complex_var_temporary_u8_1() var2.u8[3] -#define complex_var_temporary_u16() var2.u16[1] +#define complex_var_persistent_u8_0() var2.u8[2] +#define complex_var_persistent_u8_1() var2.u8[3] +#define complex_var_temporary_u8() var2.u8[0] #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \ HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \ HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \ + HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \ HB_COMPLEX_SHAPER_IMPLEMENT (indic) \ + HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ /* ^--- Add new shapers here */ enum hb_ot_complex_shaper_t { #define HB_COMPLEX_SHAPER_IMPLEMENT(name) hb_ot_complex_shaper_##name, HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS + /* Just here to avoid enum trailing comma: */ + hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default #undef HB_COMPLEX_SHAPER_IMPLEMENT }; static inline hb_ot_complex_shaper_t hb_ot_shape_complex_categorize (const hb_segment_properties_t *props) { - switch ((int) props->script) + switch ((hb_tag_t) props->script) { default: return hb_ot_complex_shaper_default; + + /* Unicode-1.1 additions */ case HB_SCRIPT_ARABIC: - case HB_SCRIPT_MANDAIC: case HB_SCRIPT_MONGOLIAN: - case HB_SCRIPT_NKO: case HB_SCRIPT_SYRIAC: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + return hb_ot_complex_shaper_arabic; - /* TODO: These are all the scripts that the ucd/IndicSyllabicCategory.txt covers. - * Quite possibly many of these need no shaping, and some other are encoded visually. - * Needs to be refined. + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HANGUL: + + return hb_ot_complex_shaper_hangul; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_THAI: + case HB_SCRIPT_LAO: + + return hb_ot_complex_shaper_thai; + + + + /* ^--- Add new shapers here */ + + +#if 0 + /* Note: + * + * These disabled scripts are listed in ucd/IndicSyllabicCategory.txt, but according + * to Martin Hosken and Jonathan Kew do not require complex shaping. + * + * TODO We should automate figuring out which scripts do not need complex shaping + * + * TODO We currently keep data for these scripts in our indic table. Need to fix the + * generator to not do that. */ - case HB_SCRIPT_BALINESE: + + + /* Simple? */ + + /* Unicode-3.2 additions */ + case HB_SCRIPT_BUHID: + case HB_SCRIPT_HANUNOO: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_SAURASHTRA: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_MEETEI_MAYEK: + + /* Unicode-6.0 additions */ case HB_SCRIPT_BATAK: - case HB_SCRIPT_BENGALI: case HB_SCRIPT_BRAHMI: - case HB_SCRIPT_BUGINESE: - case HB_SCRIPT_BUHID: - case HB_SCRIPT_CHAM: + + + /* Simple */ + + /* Unicode-1.1 additions */ + /* These have their own shaper now. */ + case HB_SCRIPT_LAO: + case HB_SCRIPT_THAI: + + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: + + /* Unicode-3.2 additions */ + case HB_SCRIPT_TAGALOG: + case HB_SCRIPT_TAGBANWA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_LIMBU: + case HB_SCRIPT_TAI_LE: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_SYLOTI_NAGRI: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHAGS_PA: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_KAYAH_LI: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_TAI_VIET: + + + /* May need Indic treatment in the future? */ + + /* Unicode-3.0 additions */ + case HB_SCRIPT_MYANMAR: + + +#endif + + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: case HB_SCRIPT_DEVANAGARI: case HB_SCRIPT_GUJARATI: case HB_SCRIPT_GURMUKHI: - case HB_SCRIPT_HANUNOO: - case HB_SCRIPT_JAVANESE: - case HB_SCRIPT_KAITHI: case HB_SCRIPT_KANNADA: - case HB_SCRIPT_KAYAH_LI: - case HB_SCRIPT_KHAROSHTHI: - case HB_SCRIPT_KHMER: - case HB_SCRIPT_LAO: - case HB_SCRIPT_LEPCHA: - case HB_SCRIPT_LIMBU: case HB_SCRIPT_MALAYALAM: - case HB_SCRIPT_MEETEI_MAYEK: - case HB_SCRIPT_MYANMAR: - case HB_SCRIPT_NEW_TAI_LUE: case HB_SCRIPT_ORIYA: - case HB_SCRIPT_PHAGS_PA: - case HB_SCRIPT_REJANG: - case HB_SCRIPT_SAURASHTRA: + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_TELUGU: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_KHMER: case HB_SCRIPT_SINHALA: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_BUGINESE: + case HB_SCRIPT_KHAROSHTHI: + case HB_SCRIPT_NEW_TAI_LUE: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_BALINESE: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_CHAM: + case HB_SCRIPT_LEPCHA: + case HB_SCRIPT_REJANG: case HB_SCRIPT_SUNDANESE: - case HB_SCRIPT_SYLOTI_NAGRI: - case HB_SCRIPT_TAGALOG: - case HB_SCRIPT_TAGBANWA: - case HB_SCRIPT_TAI_LE: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_JAVANESE: + case HB_SCRIPT_KAITHI: case HB_SCRIPT_TAI_THAM: - case HB_SCRIPT_TAI_VIET: - case HB_SCRIPT_TAMIL: - case HB_SCRIPT_TELUGU: - case HB_SCRIPT_THAI: - case HB_SCRIPT_TIBETAN: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_CHAKMA: + case HB_SCRIPT_SHARADA: + case HB_SCRIPT_TAKRI: + return hb_ot_complex_shaper_indic; /* ^--- Add new shapers here */ @@ -156,26 +251,26 @@ hb_ot_shape_complex_collect_features (hb_ot_complex_shaper_t shaper, /* - * prefer_decomposed() + * normalization_preference() * * Called during shape_execute(). * - * Shapers should return TRUE if it prefers decomposed (NFD) input rather than precomposed (NFC). + * Shapers should return true if it prefers decomposed (NFD) input rather than precomposed (NFC). */ -typedef bool hb_ot_shape_complex_prefer_decomposed_func_t (void); +typedef hb_ot_shape_normalization_mode_t hb_ot_shape_complex_normalization_preference_func_t (void); #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - HB_INTERNAL hb_ot_shape_complex_prefer_decomposed_func_t _hb_ot_shape_complex_prefer_decomposed_##name; + HB_INTERNAL hb_ot_shape_complex_normalization_preference_func_t _hb_ot_shape_complex_normalization_preference_##name; HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS #undef HB_COMPLEX_SHAPER_IMPLEMENT -static inline bool -hb_ot_shape_complex_prefer_decomposed (hb_ot_complex_shaper_t shaper) +static inline hb_ot_shape_normalization_mode_t +hb_ot_shape_complex_normalization_preference (hb_ot_complex_shaper_t shaper) { switch (shaper) { default: #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - case hb_ot_complex_shaper_##name: return _hb_ot_shape_complex_prefer_decomposed_##name (); + case hb_ot_complex_shaper_##name: return _hb_ot_shape_complex_normalization_preference_##name (); HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS #undef HB_COMPLEX_SHAPER_IMPLEMENT } @@ -189,7 +284,7 @@ hb_ot_shape_complex_prefer_decomposed (hb_ot_complex_shaper_t shaper) * Shapers should use map to get feature masks and set on buffer. */ -typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer); +typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font); #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ HB_INTERNAL hb_ot_shape_complex_setup_masks_func_t _hb_ot_shape_complex_setup_masks_##name; HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS @@ -198,18 +293,18 @@ typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer static inline void hb_ot_shape_complex_setup_masks (hb_ot_complex_shaper_t shaper, hb_ot_map_t *map, - hb_buffer_t *buffer) + hb_buffer_t *buffer, + hb_font_t *font) { switch (shaper) { default: #define HB_COMPLEX_SHAPER_IMPLEMENT(name) \ - case hb_ot_complex_shaper_##name: _hb_ot_shape_complex_setup_masks_##name (map, buffer); return; + case hb_ot_complex_shaper_##name: _hb_ot_shape_complex_setup_masks_##name (map, buffer, font); return; HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS #undef HB_COMPLEX_SHAPER_IMPLEMENT } } -HB_END_DECLS #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */ diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize-private.hh new file mode 100644 index 0000000..bb81f00 --- /dev/null +++ b/src/hb-ot-shape-normalize-private.hh @@ -0,0 +1,46 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_NORMALIZE_PRIVATE_HH +#define HB_OT_SHAPE_NORMALIZE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-font.h" +#include "hb-buffer.h" + + +enum hb_ot_shape_normalization_mode_t { + HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED, + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL /* including base-to-base composition */ +}; + +HB_INTERNAL void _hb_ot_shape_normalize (hb_font_t *font, + hb_buffer_t *buffer, + hb_ot_shape_normalization_mode_t mode); + +#endif /* HB_OT_SHAPE_NORMALIZE_PRIVATE_HH */ diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index e2fd91c..562ba88 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,10 +24,9 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb-ot-shape-normalize-private.hh" #include "hb-ot-shape-private.hh" -#include "hb-ot-shape-complex-private.hh" -HB_BEGIN_DECLS /* * HIGHLEVEL DESIGN: @@ -35,11 +34,13 @@ HB_BEGIN_DECLS * This file exports one main function: _hb_ot_shape_normalize(). * * This function closely reflects the Unicode Normalization Algorithm, - * yet it's different. The shaper an either prefer decomposed (NFD) or - * composed (NFC). + * yet it's different. + * + * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC). + * The logic however tries to use whatever the font can support. * * In general what happens is that: each grapheme is decomposed in a chain - * of 1:2 decompositions, marks reordered, and then recomposed if desires, + * of 1:2 decompositions, marks reordered, and then recomposed if desired, * so far it's like Unicode Normalization. However, the decomposition and * recomposition only happens if the font supports the resulting characters. * @@ -53,9 +54,13 @@ HB_BEGIN_DECLS * though their NFC may be different. * * - When a font has a precomposed character for a sequence but the 'ccmp' - * feature in the font is not adequate, form use the precomposed character + * feature in the font is not adequate, use the precomposed character * which typically has better mark positioning. * + * - When a font does not support a combining mark, but supports it precomposed + * with previous base, use that. This needs the itemizer to have this + * knowledge too. We need to provide assistance to the itemizer. + * * - When a font does not support a character but supports its decomposition, * well, use the decomposition. * @@ -64,104 +69,104 @@ HB_BEGIN_DECLS */ static void -output_glyph (hb_ot_shape_context_t *c, - hb_codepoint_t glyph) +output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph) { - hb_buffer_t *buffer = c->buffer; - buffer->output_glyph (glyph); - hb_glyph_info_set_unicode_props (&buffer->out_info[buffer->out_len - 1], buffer->unicode); + _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode); } static bool -decompose (hb_ot_shape_context_t *c, +decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t ab) { hb_codepoint_t a, b, glyph; - if (!hb_unicode_decompose (c->buffer->unicode, ab, &a, &b) || - (b && !hb_font_get_glyph (c->font, b, 0, &glyph))) - return FALSE; + if (!hb_unicode_decompose (buffer->unicode, ab, &a, &b) || + (b && !hb_font_get_glyph (font, b, 0, &glyph))) + return false; - bool has_a = hb_font_get_glyph (c->font, a, 0, &glyph); + bool has_a = hb_font_get_glyph (font, a, 0, &glyph); if (shortest && has_a) { /* Output a and b */ - output_glyph (c, a); + output_glyph (buffer, a); if (b) - output_glyph (c, b); - return TRUE; + output_glyph (buffer, b); + return true; } - if (decompose (c, shortest, a)) { + if (decompose (font, buffer, shortest, a)) { if (b) - output_glyph (c, b); - return TRUE; + output_glyph (buffer, b); + return true; } if (has_a) { - output_glyph (c, a); + output_glyph (buffer, a); if (b) - output_glyph (c, b); - return TRUE; + output_glyph (buffer, b); + return true; } - return FALSE; + return false; } static void -decompose_current_glyph (hb_ot_shape_context_t *c, +decompose_current_glyph (hb_font_t *font, hb_buffer_t *buffer, bool shortest) { - if (decompose (c, shortest, c->buffer->info[c->buffer->idx].codepoint)) - c->buffer->skip_glyph (); + if (decompose (font, buffer, shortest, buffer->cur().codepoint)) + buffer->skip_glyph (); else - c->buffer->next_glyph (); + buffer->next_glyph (); } static void -decompose_single_char_cluster (hb_ot_shape_context_t *c, +decompose_single_char_cluster (hb_font_t *font, hb_buffer_t *buffer, bool will_recompose) { hb_codepoint_t glyph; /* If recomposing and font supports this, we're good to go */ - if (will_recompose && hb_font_get_glyph (c->font, c->buffer->info[c->buffer->idx].codepoint, 0, &glyph)) { - c->buffer->next_glyph (); + if (will_recompose && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph)) { + buffer->next_glyph (); return; } - decompose_current_glyph (c, will_recompose); + decompose_current_glyph (font, buffer, will_recompose); } static void -decompose_multi_char_cluster (hb_ot_shape_context_t *c, +decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end) { /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */ - for (unsigned int i = c->buffer->idx; i < end; i++) - if (unlikely (is_variation_selector (c->buffer->info[i].codepoint))) + for (unsigned int i = buffer->idx; i < end; i++) + if (unlikely (_hb_unicode_is_variation_selector (buffer->info[i].codepoint))) { + while (buffer->idx < end) + buffer->next_glyph (); return; + } - while (c->buffer->idx < end) - decompose_current_glyph (c, FALSE); + while (buffer->idx < end) + decompose_current_glyph (font, buffer, false); } static int compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) { - unsigned int a = pa->combining_class(); - unsigned int b = pb->combining_class(); + unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); + unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); return a < b ? -1 : a == b ? 0 : +1; } void -_hb_ot_shape_normalize (hb_ot_shape_context_t *c) +_hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer, + hb_ot_shape_normalization_mode_t mode) { - hb_buffer_t *buffer = c->buffer; - bool recompose = !hb_ot_shape_complex_prefer_decomposed (c->plan->shaper); - bool has_multichar_clusters = FALSE; + bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; + bool has_multichar_clusters = false; unsigned int count; /* We do a fairly straightforward yet custom normalization process in three @@ -179,28 +184,20 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c) { unsigned int end; for (end = buffer->idx + 1; end < count; end++) - if (buffer->info[buffer->idx].cluster != buffer->info[end].cluster) + if (buffer->cur().cluster != buffer->info[end].cluster) break; if (buffer->idx + 1 == end) - decompose_single_char_cluster (c, recompose); + decompose_single_char_cluster (font, buffer, recompose); else { - decompose_multi_char_cluster (c, end); - has_multichar_clusters = TRUE; + decompose_multi_char_cluster (font, buffer, end); + has_multichar_clusters = true; } } buffer->swap_buffers (); - /* Technically speaking, two characters with ccc=0 may combine. But all - * those cases are in languages that the indic module handles (which expects - * decomposed), or in Hangul jamo, which again, we want decomposed anyway. - * So we don't bother combining across cluster boundaries. - * - * TODO: Am I right about Hangul? If I am, we should add a Hangul module - * that requests decomposed. */ - - if (!has_multichar_clusters) + if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !has_multichar_clusters) return; /* Done! */ @@ -209,12 +206,12 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c) count = buffer->len; for (unsigned int i = 0; i < count; i++) { - if (buffer->info[i].combining_class() == 0) + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) continue; unsigned int end; for (end = i + 1; end < count; end++) - if (buffer->info[end].combining_class() == 0) + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) break; /* We are going to do a bubble-sort. Only do this if the @@ -245,34 +242,38 @@ _hb_ot_shape_normalize (hb_ot_shape_context_t *c) buffer->next_glyph (); while (buffer->idx < count) { - if (buffer->info[buffer->idx].combining_class() == 0) { - starter = buffer->out_len; - buffer->next_glyph (); - continue; - } - hb_codepoint_t composed, glyph; - if ((buffer->out_info[buffer->out_len - 1].combining_class() >= - buffer->info[buffer->idx].combining_class()) || - !hb_unicode_compose (c->buffer->unicode, - buffer->out_info[starter].codepoint, - buffer->info[buffer->idx].codepoint, - &composed) || - !hb_font_get_glyph (c->font, composed, 0, &glyph)) + if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to + * compose a CCC=0 character with it's preceding starter. */ + (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL || + _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) && + /* If there's anything between the starter and this char, they should have CCC + * smaller than this character's. */ + (starter == buffer->out_len - 1 || + _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) && + /* And compose. */ + hb_unicode_compose (buffer->unicode, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + hb_font_get_glyph (font, composed, 0, &glyph)) { - /* Blocked, or doesn't compose. */ - buffer->next_glyph (); + /* Composes. Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + /* XXX update cluster */ + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode); + + buffer->skip_glyph (); continue; } - /* Composes. Modify starter and carry on. */ - buffer->out_info[starter].codepoint = composed; - hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode); + /* Blocked, or doesn't compose. */ + buffer->next_glyph (); - buffer->skip_glyph (); + if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) + starter = buffer->out_len - 1; } buffer->swap_buffers (); } - -HB_END_DECLS diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh index 17f8273..df0c705 100644 --- a/src/hb-ot-shape-private.hh +++ b/src/hb-ot-shape-private.hh @@ -29,20 +29,12 @@ #include "hb-private.hh" -#include "hb-ot-shape.h" - #include "hb-ot-map-private.hh" #include "hb-ot-shape-complex-private.hh" -HB_BEGIN_DECLS - - -enum hb_ot_complex_shaper_t; struct hb_ot_shape_plan_t { - friend struct hb_ot_shape_planner_t; - hb_ot_map_t map; hb_ot_complex_shaper_t shaper; @@ -53,63 +45,39 @@ struct hb_ot_shape_plan_t NO_COPY (hb_ot_shape_plan_t); }; -struct hb_ot_shape_planner_t -{ - hb_ot_map_builder_t map; - hb_ot_complex_shaper_t shaper; - hb_ot_shape_planner_t (void) : map () {} - ~hb_ot_shape_planner_t (void) { map.finish (); } - inline void compile (hb_face_t *face, - const hb_segment_properties_t *props, - struct hb_ot_shape_plan_t &plan) - { - plan.shaper = shaper; - map.compile (face, props, plan.map); - } - - private: - NO_COPY (hb_ot_shape_planner_t); -}; +HB_INTERNAL hb_bool_t +_hb_ot_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); -struct hb_ot_shape_context_t +inline void +_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode) { - /* Input to hb_ot_shape_execute() */ - hb_ot_shape_plan_t *plan; - hb_font_t *font; - hb_face_t *face; - hb_buffer_t *buffer; - const hb_feature_t *user_features; - unsigned int num_user_features; - - /* Transient stuff */ - hb_direction_t target_direction; - hb_bool_t applied_substitute_complex; - hb_bool_t applied_position_complex; -}; - + info->unicode_props0() = ((unsigned int) hb_unicode_general_category (unicode, info->codepoint)) | + (_hb_unicode_is_zero_width (info->codepoint) ? 0x80 : 0); + info->unicode_props1() = _hb_unicode_modified_combining_class (unicode, info->codepoint); +} -static inline hb_bool_t -is_variation_selector (hb_codepoint_t unicode) +inline hb_unicode_general_category_t +_hb_glyph_info_get_general_category (const hb_glyph_info_t *info) { - return unlikely ((unicode >= 0x180B && unicode <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */ - (unicode >= 0xFE00 && unicode <= 0xFE0F) || /* VARIATION SELECTOR-1..16 */ - (unicode >= 0xE0100 && unicode <= 0xE01EF)); /* VARIATION SELECTOR-17..256 */ + return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F); } -static inline void -hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode) +inline unsigned int +_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) { - info->general_category() = hb_unicode_general_category (unicode, info->codepoint); - info->combining_class() = hb_unicode_combining_class (unicode, info->codepoint); + return info->unicode_props1(); } -HB_INTERNAL void _hb_set_unicode_props (hb_buffer_t *buffer); - -HB_INTERNAL void _hb_ot_shape_normalize (hb_ot_shape_context_t *c); - -HB_END_DECLS +inline hb_bool_t +_hb_glyph_info_is_zero_width (const hb_glyph_info_t *info) +{ + return !!(info->unicode_props0() & 0x80); +} #endif /* HB_OT_SHAPE_PRIVATE_HH */ diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index e40cf1e..19cf680 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -27,27 +27,28 @@ */ #include "hb-ot-shape-private.hh" -#include "hb-ot-shape-complex-private.hh" +#include "hb-ot-shape-normalize-private.hh" #include "hb-font-private.hh" +#include "hb-set-private.hh" -HB_BEGIN_DECLS hb_tag_t common_features[] = { HB_TAG('c','c','m','p'), + HB_TAG('l','i','g','a'), HB_TAG('l','o','c','l'), HB_TAG('m','a','r','k'), HB_TAG('m','k','m','k'), HB_TAG('r','l','i','g'), }; + hb_tag_t horizontal_features[] = { HB_TAG('c','a','l','t'), HB_TAG('c','l','i','g'), HB_TAG('c','u','r','s'), HB_TAG('k','e','r','n'), - HB_TAG('l','i','g','a'), }; /* Note: @@ -63,6 +64,28 @@ hb_tag_t vertical_features[] = { HB_TAG('v','r','t','2'), }; + + +struct hb_ot_shape_planner_t +{ + hb_ot_map_builder_t map; + hb_ot_complex_shaper_t shaper; + + hb_ot_shape_planner_t (void) : map () {} + ~hb_ot_shape_planner_t (void) { map.finish (); } + + inline void compile (hb_face_t *face, + const hb_segment_properties_t *props, + struct hb_ot_shape_plan_t &plan) + { + plan.shaper = shaper; + map.compile (face, props, plan.map); + } + + private: + NO_COPY (hb_ot_shape_planner_t); +}; + static void hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, const hb_segment_properties_t *props, @@ -109,13 +132,28 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, } +struct hb_ot_shape_context_t +{ + /* Input to hb_ot_shape_execute() */ + hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + const hb_feature_t *user_features; + unsigned int num_user_features; + + /* Transient stuff */ + hb_direction_t target_direction; + hb_bool_t applied_position_complex; +}; + static void hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) { hb_mask_t global_mask = c->plan->map.get_global_mask (); c->buffer->reset_masks (global_mask); - hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer); + hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer, c->font); for (unsigned int i = 0; i < c->num_user_features; i++) { @@ -133,12 +171,12 @@ hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) /* Prepare */ -void -_hb_set_unicode_props (hb_buffer_t *buffer) +static void +hb_set_unicode_props (hb_buffer_t *buffer) { unsigned int count = buffer->len; - for (unsigned int i = 1; i < count; i++) - hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode); + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode); } static void @@ -146,11 +184,11 @@ hb_form_clusters (hb_buffer_t *buffer) { unsigned int count = buffer->len; for (unsigned int i = 1; i < count; i++) - if (FLAG (buffer->info[i].general_category()) & + if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) - buffer->info[i].cluster = buffer->info[i - 1].cluster; + buffer->info[i].cluster = buffer->info[i - 1].cluster; /* XXX do the min() here */ } static void @@ -206,17 +244,16 @@ hb_map_glyphs (hb_font_t *font, unsigned int count = buffer->len - 1; for (buffer->idx = 0; buffer->idx < count;) { - if (unlikely (is_variation_selector (buffer->info[buffer->idx + 1].codepoint))) { - hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, buffer->info[buffer->idx + 1].codepoint, &glyph); - buffer->replace_glyph (glyph); - buffer->skip_glyph (); + if (unlikely (_hb_unicode_is_variation_selector (buffer->cur(+1).codepoint))) { + hb_font_get_glyph (font, buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph); + buffer->replace_glyphs (2, 1, &glyph); } else { - hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph); + hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph); buffer->replace_glyph (glyph); } } if (likely (buffer->idx < buffer->len)) { - hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph); + hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph); buffer->replace_glyph (glyph); } buffer->swap_buffers (); @@ -235,21 +272,15 @@ hb_substitute_default (hb_ot_shape_context_t *c) static void hb_ot_substitute_complex (hb_ot_shape_context_t *c) { - if (hb_ot_layout_has_substitution (c->face)) + if (hb_ot_layout_has_substitution (c->face)) { c->plan->map.substitute (c->face, c->buffer); + } hb_ot_layout_substitute_finish (c->buffer); - c->applied_substitute_complex = TRUE; return; } -static void -hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) -{ - /* TODO Arabic */ -} - /* Position */ @@ -295,11 +326,12 @@ hb_ot_position_complex (hb_ot_shape_context_t *c) &c->buffer->pos[i].x_offset, &c->buffer->pos[i].y_offset); } + + c->applied_position_complex = true; } hb_ot_layout_position_finish (c->buffer); - c->applied_position_complex = TRUE; return; } @@ -341,6 +373,23 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c) hb_truetype_kern (c); } +static void +hb_hide_zerowidth (hb_ot_shape_context_t *c) +{ + /* TODO Save the space character in the font? */ + hb_codepoint_t space; + if (!hb_font_get_glyph (c->font, ' ', 0, &space)) + return; /* No point! */ + + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_is_zero_width (&c->buffer->info[i]))) { + c->buffer->info[i].codepoint = space; + c->buffer->pos[i].x_advance = 0; + c->buffer->pos[i].y_advance = 0; + } +} + /* Do it! */ @@ -352,16 +401,16 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c) /* Save the original direction, we use it later. */ c->target_direction = c->buffer->props.direction; - HB_BUFFER_ALLOCATE_VAR (c->buffer, general_category); - HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class); + HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0); + HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1); - _hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */ + hb_set_unicode_props (c->buffer); hb_form_clusters (c->buffer); hb_ensure_native_direction (c->buffer); - _hb_ot_shape_normalize (c); + _hb_ot_shape_normalize (c->font, c->buffer, hb_ot_shape_complex_normalization_preference (c->plan->shaper)); hb_ot_shape_setup_masks (c); @@ -370,9 +419,6 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c) hb_substitute_default (c); hb_ot_substitute_complex (c); - - if (!c->applied_substitute_complex) - hb_substitute_complex_fallback (c); } /* POSITION */ @@ -392,8 +438,10 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c) hb_position_complex_fallback_visual (c); } - HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class); - HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category); + hb_hide_zerowidth (c); + + HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1); + HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0); c->buffer->props.direction = c->target_direction; @@ -409,6 +457,8 @@ hb_ot_shape_plan_internal (hb_ot_shape_plan_t *plan, { hb_ot_shape_planner_t planner; + assert (HB_DIRECTION_IS_VALID (props->direction)); + planner.shaper = hb_ot_shape_complex_categorize (props); hb_ot_shape_collect_features (&planner, props, user_features, num_user_features); @@ -427,17 +477,52 @@ hb_ot_shape_execute (hb_ot_shape_plan_t *plan, hb_ot_shape_execute_internal (&c); } -void -hb_ot_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) +hb_bool_t +_hb_ot_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) { hb_ot_shape_plan_t plan; + buffer->guess_properties (); + hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); hb_ot_shape_execute (&plan, font, buffer, features, num_features); + + return true; } -HB_END_DECLS +void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs) +{ + hb_ot_shape_plan_t plan; + + buffer->guess_properties (); + + hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); + + /* TODO: normalization? have shapers do closure()? */ + /* TODO: Deal with mirrored chars? */ + hb_map_glyphs (font, buffer); + + /* Seed it. It's user's responsibility to have cleard glyphs + * if that's what they desire. */ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + hb_set_add (glyphs, buffer->info[i].codepoint); + + /* And find transitive closure. */ + hb_set_t copy; + copy.init (); + + do { + copy.set (glyphs); + plan.map.substitute_closure (font->face, glyphs); + } while (!copy.equal (glyphs)); +} diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc index 0ce635a..ac60e96 100644 --- a/src/hb-ot-tag.cc +++ b/src/hb-ot-tag.cc @@ -31,7 +31,6 @@ #include -HB_BEGIN_DECLS /* hb_script_t */ @@ -652,6 +651,14 @@ hb_ot_tag_from_language (hb_language_t language) return HB_TAG('Z','H','S',' '); } + s = strchr (lang_str, '-'); + if (!s) + s = lang_str + strlen (lang_str); + if (s - lang_str == 3) { + /* Assume it's ISO-639-3 and upper-case and use it. */ + return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000; + } + return HB_OT_TAG_DEFAULT_LANGUAGE; } @@ -665,12 +672,12 @@ hb_ot_tag_to_language (hb_tag_t tag) for (i = 0; i < ARRAY_LENGTH (ot_languages); i++) if (ot_languages[i].tag == tag) - return hb_language_from_string (ot_languages[i].language); + return hb_language_from_string (ot_languages[i].language, -1); /* If tag starts with ZH, it's Chinese */ if ((tag & 0xFFFF0000) == 0x5A480000) { switch (tag) { - case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk"); /* Hong Kong */ + case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */ default: { /* Encode the tag... */ unsigned char buf[14] = "zh-x-hbot"; @@ -681,7 +688,7 @@ hb_ot_tag_to_language (hb_tag_t tag) if (buf[12] == 0x20) buf[12] = '\0'; buf[13] = '\0'; - return hb_language_from_string ((char *) buf); + return hb_language_from_string ((char *) buf, -1); } } } @@ -696,9 +703,8 @@ hb_ot_tag_to_language (hb_tag_t tag) if (buf[9] == 0x20) buf[9] = '\0'; buf[10] = '\0'; - return hb_language_from_string ((char *) buf); + return hb_language_from_string ((char *) buf, -1); } } -HB_END_DECLS diff --git a/src/hb-ot-tag.h b/src/hb-ot-tag.h index 427a069..1bf12ab 100644 --- a/src/hb-ot-tag.h +++ b/src/hb-ot-tag.h @@ -24,10 +24,14 @@ * Red Hat Author(s): Behdad Esfahbod */ +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + #ifndef HB_OT_TAG_H #define HB_OT_TAG_H -#include "hb-common.h" +#include "hb.h" HB_BEGIN_DECLS diff --git a/src/hb-ot.h b/src/hb-ot.h index fd6dd58..2d750c3 100644 --- a/src/hb-ot.h +++ b/src/hb-ot.h @@ -26,14 +26,23 @@ #ifndef HB_OT_H #define HB_OT_H +#define HB_OT_H_IN #include "hb.h" #include "hb-ot-layout.h" -#include "hb-ot-shape.h" #include "hb-ot-tag.h" HB_BEGIN_DECLS + +void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs); + HB_END_DECLS +#undef HB_OT_H_IN #endif /* HB_OT_H */ diff --git a/src/hb-private.hh b/src/hb-private.hh index 23fc0af..0cb049e 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -1,6 +1,6 @@ /* * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2011 Google, Inc. + * Copyright © 2011,2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -29,11 +29,16 @@ #ifndef HB_PRIVATE_HH #define HB_PRIVATE_HH -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif -#include "hb-common.h" +#include "hb.h" +#define HB_H_IN +#ifdef HAVE_OT +#include "hb-ot.h" +#define HB_OT_H_IN +#endif #include #include @@ -48,7 +53,6 @@ #include #include -HB_BEGIN_DECLS /* Essentials */ @@ -57,16 +61,9 @@ HB_BEGIN_DECLS # define NULL ((void *) 0) #endif -#undef FALSE -#define FALSE 0 - -#undef TRUE -#define TRUE 1 - /* Basics */ -HB_END_DECLS #undef MIN template static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; } @@ -74,7 +71,6 @@ template static inline Type MIN (const Type &a, const Type &b) { #undef MAX template static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; } -HB_BEGIN_DECLS #undef ARRAY_LENGTH #define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) @@ -82,13 +78,15 @@ HB_BEGIN_DECLS #define HB_STMT_START do #define HB_STMT_END while (0) -#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1] -#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond)) -#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond)) +#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1] +#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond)) +#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond)) -#define ASSERT_STATIC_EXPR(_cond) ((void) sizeof (char[(_cond) ? 1 : -1])) +#define ASSERT_STATIC_EXPR(_cond)((void) sizeof (char[(_cond) ? 1 : -1])) #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1])) +#define _PASTE1(a,b) a##b +#define PASTE(a,b) _PASTE1(a,b) /* Lets assert int types. Saves trouble down the road. */ @@ -106,6 +104,34 @@ ASSERT_STATIC (sizeof (hb_position_t) == 4); ASSERT_STATIC (sizeof (hb_mask_t) == 4); ASSERT_STATIC (sizeof (hb_var_int_t) == 4); + +/* We like our types POD */ + +#define _ASSERT_TYPE_POD1(_line, _type) union _type_##_type##_on_line_##_line##_is_not_POD { _type instance; } +#define _ASSERT_TYPE_POD0(_line, _type) _ASSERT_TYPE_POD1 (_line, _type) +#define ASSERT_TYPE_POD(_type) _ASSERT_TYPE_POD0 (__LINE__, _type) + +#ifdef __GNUC__ +# define _ASSERT_INSTANCE_POD1(_line, _instance) \ + HB_STMT_START { \ + typedef __typeof__(_instance) _type_##_line; \ + _ASSERT_TYPE_POD1 (_line, _type_##_line); \ + } HB_STMT_END +#else +# define _ASSERT_INSTANCE_POD1(_line, _instance) typedef int _assertion_on_line_##_line##_not_tested +#endif +# define _ASSERT_INSTANCE_POD0(_line, _instance) _ASSERT_INSTANCE_POD1 (_line, _instance) +# define ASSERT_INSTANCE_POD(_instance) _ASSERT_INSTANCE_POD0 (__LINE__, _instance) + +/* Check _assertion in a method environment */ +#define _ASSERT_POD1(_line) \ + inline void _static_assertion_on_line_##_line (void) const \ + { _ASSERT_INSTANCE_POD1 (_line, *this); /* Make sure it's POD. */ } +# define _ASSERT_POD0(_line) _ASSERT_POD1 (_line) +# define ASSERT_POD() _ASSERT_POD0 (__LINE__) + + + /* Misc */ @@ -130,7 +156,7 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4); #else #define HB_PURE_FUNC #define HB_CONST_FUNC -#define HB_PRINTF_FUCN(format_idx, arg_idx) +#define HB_PRINTF_FUNC(format_idx, arg_idx) #endif #if __GNUC__ >= 4 #define HB_UNUSED __attribute__((unused)) @@ -139,7 +165,11 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 4); #endif #ifndef HB_INTERNAL -# define HB_INTERNAL __attribute__((__visibility__("hidden"))) +# ifndef __MINGW32__ +# define HB_INTERNAL __attribute__((__visibility__("hidden"))) +# else +# define HB_INTERNAL +# endif #endif @@ -226,20 +256,22 @@ _hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size) typedef int (*hb_compare_func_t) (const void *, const void *); -HB_END_DECLS /* arrays and maps */ +#define HB_PREALLOCED_ARRAY_INIT {0} template -struct hb_prealloced_array_t { - +struct hb_prealloced_array_t +{ unsigned int len; unsigned int allocated; Type *array; Type static_array[StaticSize]; + void init (void) { memset (this, 0, sizeof (*this)); } + inline Type& operator [] (unsigned int i) { return array[i]; } inline const Type& operator [] (unsigned int i) const { return array[i]; } @@ -333,25 +365,31 @@ struct hb_prealloced_array_t { } }; -template -struct hb_array_t : hb_prealloced_array_t {}; - +#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT} template struct hb_lockable_set_t { - hb_array_t items; + hb_prealloced_array_t items; + + inline void init (void) { items.init (); } template - inline item_t *replace_or_insert (T v, lock_t &l) + inline item_t *replace_or_insert (T v, lock_t &l, bool replace) { l.lock (); item_t *item = items.find (v); if (item) { - item_t old = *item; - *item = v; - l.unlock (); - old.finish (); + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.finish (); + } + else { + item = NULL; + l.unlock (); + } } else { item = items.push (); if (likely (item)) @@ -419,7 +457,6 @@ struct hb_lockable_set_t }; -HB_BEGIN_DECLS /* Big-endian handling */ @@ -430,6 +467,14 @@ static inline uint16_t hb_be_uint16 (const uint16_t v) return (uint16_t) (V[0] << 8) + V[1]; } +/* Note, of the following macros, uint16_get is the one called many many times. + * If there is any optimizations to be done, it's in that macro. However, I + * already confirmed that on my T400 ThinkPad at least, using bswap_16(), which + * results in a single ror instruction, does NOT speed this up. In fact, it + * resulted in a minor slowdown. At any rate, note that v may not be correctly + * aligned, so I think the current implementation is optimal. + */ + #define hb_be_uint16_put(v,V) HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END #define hb_be_uint16_get(v) (uint16_t) ((v[0] << 8) + v[1]) #define hb_be_uint16_eq(a,b) (a[0] == b[0] && a[1] == b[1]) @@ -466,7 +511,6 @@ static inline unsigned char TOLOWER (unsigned char c) /* Debug */ -HB_END_DECLS #ifndef HB_DEBUG #define HB_DEBUG 0 @@ -482,60 +526,106 @@ _hb_debug (unsigned int level, #define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0)) -template inline bool /* always returns TRUE */ +template inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) +{ + if (!_hb_debug (level, max_level)) + return; + + fprintf (stderr, "%-10s", what ? what : ""); + + if (obj) + fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj); + else + fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + + if (indented) { + static const char bars[] = "││││││││││││││││││││││││││││││││││││││││"; + fprintf (stderr, "%2d %s├%s", + level, + bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level), + level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴"); + } else + fprintf (stderr, " ├╴"); + + if (func) { + /* If there's a class name, just write that. */ + const char *dotdot = strstr (func, "::"); + const char *space = strchr (func, ' '); + if (space && dotdot && space < dotdot) + func = space + 1; + unsigned int func_len = dotdot ? dotdot - func : strlen (func); + fprintf (stderr, "%.*s: ", func_len, func); + } + + if (message) + vfprintf (stderr, message, ap); + + fprintf (stderr, "\n"); +} +template <> inline void +_hb_debug_msg_va<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + va_list ap HB_UNUSED) {} + +template inline void _hb_debug_msg (const char *what, const void *obj, const char *func, bool indented, - int level, + unsigned int level, + int level_dir, const char *message, - ...) HB_PRINTF_FUNC(6, 7); -template inline bool /* always returns TRUE */ + ...) HB_PRINTF_FUNC(7, 8); +template inline void _hb_debug_msg (const char *what, const void *obj, const char *func, bool indented, - int level, + unsigned int level, + int level_dir, const char *message, ...) { va_list ap; va_start (ap, message); - - (void) (_hb_debug (level, max_level) && - fprintf (stderr, "%s(%p): ", what, obj) && - (func && fprintf (stderr, "%s: ", func), TRUE) && - (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) && - vfprintf (stderr, message, ap) && - fprintf (stderr, "\n")); - + _hb_debug_msg_va (what, obj, func, indented, level, level_dir, message, ap); va_end (ap); - - return TRUE; -} -template <> inline bool /* always returns TRUE */ -_hb_debug_msg<0> (const char *what, - const void *obj, - const char *func, - bool indented, - int level, - const char *message, - ...) HB_PRINTF_FUNC(6, 7); -template <> inline bool /* always returns TRUE */ -_hb_debug_msg<0> (const char *what, - const void *obj, - const char *func, - bool indented, - int level, - const char *message, - ...) -{ - return TRUE; } - -#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg (#WHAT, (OBJ), NULL, FALSE, (LEVEL), __VA_ARGS__) -#define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__) -#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__) +template <> inline void +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) HB_PRINTF_FUNC(7, 8); +template <> inline void +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) {} + +#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg (#WHAT, (OBJ), NULL, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), NULL, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) /* @@ -545,29 +635,64 @@ _hb_debug_msg<0> (const char *what, template struct hb_auto_trace_t { explicit inline hb_auto_trace_t (unsigned int *plevel_, - const char *what, - const void *obj, + const char *what_, + const void *obj_, const char *func, - const char *message) : plevel(plevel_) + const char *message, + ...) : plevel (plevel_), what (what_), obj (obj_), returned (false) { - if (max_level) ++*plevel; - /* TODO support variadic args here */ - _hb_debug_msg (what, obj, func, TRUE, *plevel, "%s", message); + if (plevel) ++*plevel; + + va_list ap; + va_start (ap, message); + _hb_debug_msg_va (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); + va_end (ap); + } + inline ~hb_auto_trace_t (void) + { + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN. This is a bug, please report. Level was %d.\n", plevel ? *plevel : -1); + _hb_debug_msg (what, obj, NULL, true, plevel ? *plevel : 1, -1, " "); + return; + } + + if (plevel) --*plevel; + } + + inline bool ret (bool v) + { + if (unlikely (returned)) { + fprintf (stderr, "OUCH, double calls to TRACE_RETURN. This is a bug, please report.\n"); + return v; + } + + _hb_debug_msg (what, obj, NULL, true, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false"); + if (plevel) --*plevel; + plevel = NULL; + returned = true; + return v; } - ~hb_auto_trace_t (void) { if (max_level) --*plevel; } private: unsigned int *plevel; + bool returned; + const char *what; + const void *obj; }; template <> /* Optimize when tracing is disabled */ struct hb_auto_trace_t<0> { - explicit inline hb_auto_trace_t (unsigned int *plevel_, - const char *what, - const void *obj, - const char *func, - const char *message) {} + explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED, + const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + const char *message HB_UNUSED, + ...) {} + + template + inline T ret (T v) { return v; } }; +#define TRACE_RETURN(RET) trace.ret (RET) /* Misc */ @@ -619,8 +744,6 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) } -HB_BEGIN_DECLS -HB_END_DECLS #endif /* HB_PRIVATE_HH */ diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh new file mode 100644 index 0000000..5cdf8a0 --- /dev/null +++ b/src/hb-set-private.hh @@ -0,0 +1,172 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SET_PRIVATE_HH +#define HB_SET_PRIVATE_HH + +#include "hb-private.hh" +#include "hb-set.h" +#include "hb-object-private.hh" + + +/* TODO Make this faster and memmory efficient. */ + +struct _hb_set_t +{ + hb_object_header_t header; + ASSERT_POD (); + + inline void init (void) { + header.init (); + clear (); + } + inline void fini (void) { + } + inline void clear (void) { + memset (elts, 0, sizeof elts); + } + inline bool empty (void) const { + for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++) + if (elts[i]) + return false; + return true; + } + inline void add (hb_codepoint_t g) + { + if (unlikely (g == SENTINEL)) return; + if (unlikely (g > MAX_G)) return; + elt (g) |= mask (g); + } + inline void del (hb_codepoint_t g) + { + if (unlikely (g > MAX_G)) return; + elt (g) &= ~mask (g); + } + inline bool has (hb_codepoint_t g) const + { + if (unlikely (g > MAX_G)) return false; + return !!(elt (g) & mask (g)); + } + inline bool intersects (hb_codepoint_t first, + hb_codepoint_t last) const + { + if (unlikely (first > MAX_G)) return false; + if (unlikely (last > MAX_G)) last = MAX_G; + unsigned int end = last + 1; + for (hb_codepoint_t i = first; i < end; i++) + if (has (i)) + return true; + return false; + } + inline bool equal (const hb_set_t *other) const + { + for (unsigned int i = 0; i < ELTS; i++) + if (elts[i] != other->elts[i]) + return false; + return true; + } + inline void set (const hb_set_t *other) + { + for (unsigned int i = 0; i < ELTS; i++) + elts[i] = other->elts[i]; + } + inline void union_ (const hb_set_t *other) + { + for (unsigned int i = 0; i < ELTS; i++) + elts[i] |= other->elts[i]; + } + inline void intersect (const hb_set_t *other) + { + for (unsigned int i = 0; i < ELTS; i++) + elts[i] &= other->elts[i]; + } + inline void subtract (const hb_set_t *other) + { + for (unsigned int i = 0; i < ELTS; i++) + elts[i] &= ~other->elts[i]; + } + inline void symmetric_difference (const hb_set_t *other) + { + for (unsigned int i = 0; i < ELTS; i++) + elts[i] ^= other->elts[i]; + } + inline bool next (hb_codepoint_t *codepoint) + { + if (unlikely (*codepoint == SENTINEL)) { + hb_codepoint_t i = get_min (); + if (i != SENTINEL) { + *codepoint = i; + return true; + } else + return false; + } + for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++) + if (has (i)) { + *codepoint = i; + return true; + } + return false; + } + inline hb_codepoint_t get_min (void) const + { + for (unsigned int i = 0; i < ELTS; i++) + if (elts[i]) + for (unsigned int j = 0; i < BITS; j++) + if (elts[i] & (1 << j)) + return i * BITS + j; + return SENTINEL; + } + inline hb_codepoint_t get_max (void) const + { + for (unsigned int i = ELTS; i; i--) + if (elts[i - 1]) + for (unsigned int j = BITS; j; j--) + if (elts[i - 1] & (1 << (j - 1))) + return (i - 1) * BITS + (j - 1); + return SENTINEL; + } + + typedef uint32_t elt_t; + static const unsigned int MAX_G = 65536 - 1; /* XXX Fix this... */ + static const unsigned int SHIFT = 5; + static const unsigned int BITS = (1 << SHIFT); + static const unsigned int MASK = BITS - 1; + static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS; + static const hb_codepoint_t SENTINEL = (hb_codepoint_t) -1; + + elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; } + elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; } + elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); } + + elt_t elts[ELTS]; /* XXX 8kb */ + + ASSERT_STATIC (sizeof (elt_t) * 8 == BITS); + ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G); +}; + + + +#endif /* HB_SET_PRIVATE_HH */ diff --git a/src/hb-set.cc b/src/hb-set.cc new file mode 100644 index 0000000..4225e3c --- /dev/null +++ b/src/hb-set.cc @@ -0,0 +1,191 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-set-private.hh" + + + +/* Public API */ + + +hb_set_t * +hb_set_create () +{ + hb_set_t *set; + + if (!(set = hb_object_create ())) + return hb_set_get_empty (); + + set->clear (); + + return set; +} + +hb_set_t * +hb_set_get_empty (void) +{ + static const hb_set_t _hb_set_nil = { + HB_OBJECT_HEADER_STATIC, + + {0} /* elts */ + }; + + return const_cast (&_hb_set_nil); +} + +hb_set_t * +hb_set_reference (hb_set_t *set) +{ + return hb_object_reference (set); +} + +void +hb_set_destroy (hb_set_t *set) +{ + if (!hb_object_destroy (set)) return; + + set->fini (); + + free (set); +} + +hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (set, key, data, destroy, replace); +} + +void * +hb_set_get_user_data (hb_set_t *set, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (set, key); +} + + +hb_bool_t +hb_set_allocation_successful (hb_set_t *set HB_UNUSED) +{ + return true; +} + +void +hb_set_clear (hb_set_t *set) +{ + set->clear (); +} + +hb_bool_t +hb_set_empty (hb_set_t *set) +{ + return set->empty (); +} + +hb_bool_t +hb_set_has (hb_set_t *set, + hb_codepoint_t codepoint) +{ + return set->has (codepoint); +} + +void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint) +{ + set->add (codepoint); +} + +void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint) +{ + set->del (codepoint); +} + +hb_bool_t +hb_set_equal (hb_set_t *set, + hb_set_t *other) +{ + return set->equal (other); +} + +void +hb_set_set (hb_set_t *set, + hb_set_t *other) +{ + set->set (other); +} + +void +hb_set_union (hb_set_t *set, + hb_set_t *other) +{ + set->union_ (other); +} + +void +hb_set_intersect (hb_set_t *set, + hb_set_t *other) +{ + set->intersect (other); +} + +void +hb_set_subtract (hb_set_t *set, + hb_set_t *other) +{ + set->subtract (other); +} + +void +hb_set_symmetric_difference (hb_set_t *set, + hb_set_t *other) +{ + set->symmetric_difference (other); +} + +hb_codepoint_t +hb_set_min (hb_set_t *set) +{ + return set->get_min (); +} + +hb_codepoint_t +hb_set_max (hb_set_t *set) +{ + return set->get_max (); +} + +hb_bool_t +hb_set_next (hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->next (codepoint); +} diff --git a/src/hb-set.h b/src/hb-set.h new file mode 100644 index 0000000..9f849cf --- /dev/null +++ b/src/hb-set.h @@ -0,0 +1,132 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_SET_H +#define HB_SET_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +typedef struct _hb_set_t hb_set_t; + + +hb_set_t * +hb_set_create (void); + +hb_set_t * +hb_set_get_empty (void); + +hb_set_t * +hb_set_reference (hb_set_t *set); + +void +hb_set_destroy (hb_set_t *set); + +hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +void * +hb_set_get_user_data (hb_set_t *set, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +hb_bool_t +hb_set_allocation_successful (hb_set_t *set); + +void +hb_set_clear (hb_set_t *set); + +hb_bool_t +hb_set_empty (hb_set_t *set); + +hb_bool_t +hb_set_has (hb_set_t *set, + hb_codepoint_t codepoint); + +/* Right now limited to 16-bit integers. Eventually will do full codepoint range, sans -1 + * which we will use as a sentinel. */ +void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint); + +void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint); + +hb_bool_t +hb_set_equal (hb_set_t *set, + hb_set_t *other); + +void +hb_set_set (hb_set_t *set, + hb_set_t *other); + +void +hb_set_union (hb_set_t *set, + hb_set_t *other); + +void +hb_set_intersect (hb_set_t *set, + hb_set_t *other); + +void +hb_set_subtract (hb_set_t *set, + hb_set_t *other); + +void +hb_set_symmetric_difference (hb_set_t *set, + hb_set_t *other); + +/* Returns -1 if set empty. */ +hb_codepoint_t +hb_set_min (hb_set_t *set); + +/* Returns -1 if set empty. */ +hb_codepoint_t +hb_set_max (hb_set_t *set); + +/* Pass -1 in to get started. */ +hb_bool_t +hb_set_next (hb_set_t *set, + hb_codepoint_t *codepoint); + +/* TODO: Add faster iteration API? */ + + +HB_END_DECLS + +#endif /* HB_SET_H */ diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 1ff830a..163a5bf 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -30,64 +30,188 @@ #include "hb-buffer-private.hh" -#include "hb-ot-shape.h" - #ifdef HAVE_GRAPHITE -#include "hb-graphite.h" +#include "hb-graphite2-private.hh" +#endif +#ifdef HAVE_UNISCRIBE +# include "hb-uniscribe-private.hh" +#endif +#ifdef HAVE_OT +# include "hb-ot-shape-private.hh" +#endif +#include "hb-fallback-shape-private.hh" + +typedef hb_bool_t (*hb_shape_func_t) (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape} +static const struct hb_shaper_pair_t { + char name[16]; + hb_shape_func_t func; +} all_shapers[] = { + /* v--- Add new shapers in the right place here */ +#ifdef HAVE_GRAPHITE + HB_SHAPER_IMPLEMENT (graphite2), #endif +#ifdef HAVE_UNISCRIBE + HB_SHAPER_IMPLEMENT (uniscribe), +#endif +#ifdef HAVE_OT + HB_SHAPER_IMPLEMENT (ot), +#endif + HB_SHAPER_IMPLEMENT (fallback) /* should be last */ +}; +#undef HB_SHAPER_IMPLEMENT + -HB_BEGIN_DECLS +/* Thread-safe, lock-free, shapers */ +static hb_shaper_pair_t *static_shapers; -static void -hb_shape_internal (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) +static +void free_static_shapers (void) { - hb_ot_shape (font, buffer, features, num_features); + if (unlikely (static_shapers != all_shapers)) + free (static_shapers); } -void -hb_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) +static const hb_shaper_pair_t * +get_shapers (void) { - hb_segment_properties_t orig_props; - - orig_props = buffer->props; - - /* If script is set to INVALID, guess from buffer contents */ - if (buffer->props.script == HB_SCRIPT_INVALID) { - hb_unicode_funcs_t *unicode = buffer->unicode; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) { - hb_script_t script = hb_unicode_script (unicode, buffer->info[i].codepoint); - if (likely (script != HB_SCRIPT_COMMON && - script != HB_SCRIPT_INHERITED && - script != HB_SCRIPT_UNKNOWN)) { - buffer->props.script = script; - break; - } +retry: + hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers); + + if (unlikely (!shapers)) + { + char *env = getenv ("HB_SHAPER_LIST"); + if (!env || !*env) { + hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair_t *) all_shapers); + return (const hb_shaper_pair_t *) all_shapers; } - } - /* If direction is set to INVALID, guess from script */ - if (buffer->props.direction == HB_DIRECTION_INVALID) { - buffer->props.direction = hb_script_get_horizontal_direction (buffer->props.script); - } + /* Not found; allocate one. */ + shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers)); + if (unlikely (!shapers)) + return (const hb_shaper_pair_t *) all_shapers; + memcpy (shapers, all_shapers, sizeof (all_shapers)); + + /* Reorder shaper list to prefer requested shapers. */ + unsigned int i = 0; + char *end, *p = env; + for (;;) { + end = strchr (p, ','); + if (!end) + end = p + strlen (p); + + for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++) + if (end - p == (int) strlen (shapers[j].name) && + 0 == strncmp (shapers[j].name, p, end - p)) + { + /* Reorder this shaper to position i */ + struct hb_shaper_pair_t t = shapers[j]; + memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); + shapers[i] = t; + i++; + } + + if (!*end) + break; + else + p = end + 1; + } - /* If language is not set, use default language from locale */ - if (buffer->props.language == HB_LANGUAGE_INVALID) { - /* TODO get_default_for_script? using $LANGUAGE */ - buffer->props.language = hb_language_get_default (); + if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) { + free (shapers); + goto retry; + } + +#ifdef HAVE_ATEXIT + atexit (free_static_shapers); /* First person registers atexit() callback. */ +#endif } - hb_shape_internal (font, buffer, features, num_features); + return shapers; +} + + +static const char **static_shaper_list; + +static +void free_static_shaper_list (void) +{ + free (static_shaper_list); +} + +const char ** +hb_shape_list_shapers (void) +{ +retry: + const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list); + + if (unlikely (!shaper_list)) + { + /* Not found; allocate one. */ + shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof (const char *)); + if (unlikely (!shaper_list)) { + static const char *nil_shaper_list[] = {NULL}; + return nil_shaper_list; + } - buffer->props = orig_props; + const hb_shaper_pair_t *shapers = get_shapers (); + unsigned int i; + for (i = 0; i < ARRAY_LENGTH (all_shapers); i++) + shaper_list[i] = shapers[i].name; + shaper_list[i] = NULL; + + if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { + free (shaper_list); + goto retry; + } + +#ifdef HAVE_ATEXIT + atexit (free_static_shaper_list); /* First person registers atexit() callback. */ +#endif + } + + return shaper_list; } -HB_END_DECLS +hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list) +{ + hb_font_make_immutable (font); /* So we can safely cache stuff on it */ + + if (likely (!shaper_list)) { + const hb_shaper_pair_t *shapers = get_shapers (); + for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) + if (likely (shapers[i].func (font, buffer, features, num_features))) + return true; + } else { + while (*shaper_list) { + for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) + if (0 == strcmp (*shaper_list, all_shapers[i].name)) { + if (likely (all_shapers[i].func (font, buffer, features, num_features))) + return true; + break; + } + shaper_list++; + } + } + return false; +} + +void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_shape_full (font, buffer, features, num_features, NULL); +} diff --git a/src/hb-shape.h b/src/hb-shape.h index 39383af..1a0d6cf 100644 --- a/src/hb-shape.h +++ b/src/hb-shape.h @@ -24,6 +24,10 @@ * Red Hat Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_SHAPE_H #define HB_SHAPE_H @@ -41,11 +45,22 @@ typedef struct _hb_feature_t { unsigned int end; } hb_feature_t; + void -hb_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list); + +const char ** +hb_shape_list_shapers (void); HB_END_DECLS diff --git a/src/hb-tt-font.cc b/src/hb-tt-font.cc new file mode 100644 index 0000000..0055ae9 --- /dev/null +++ b/src/hb-tt-font.cc @@ -0,0 +1,243 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-font-private.hh" /* Shall be first since may include windows.h */ + +#include "hb-open-type-private.hh" + +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" + +#include "hb-blob.h" + +#include + + + +#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::sanitize (hb_face_reference_table (font->face, HB_OT_TAG_hhea)); + tt->hhea = Sanitizer::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-unicode-private.hh b/src/hb-unicode-private.hh index 2b0ec99..fd1fd44 100644 --- a/src/hb-unicode-private.hh +++ b/src/hb-unicode-private.hh @@ -36,7 +36,6 @@ #include "hb-unicode.h" #include "hb-object-private.hh" -HB_BEGIN_DECLS /* @@ -64,6 +63,7 @@ HB_BEGIN_DECLS struct _hb_unicode_funcs_t { hb_object_header_t header; + ASSERT_POD (); hb_unicode_funcs_t *parent; @@ -91,19 +91,69 @@ struct _hb_unicode_funcs_t { }; -#if HAVE_GLIB -extern HB_INTERNAL hb_unicode_funcs_t _hb_glib_unicode_funcs; +#ifdef HAVE_GLIB +extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs; #define _hb_unicode_funcs_default _hb_glib_unicode_funcs -#elif HAVE_ICU -extern HB_INTERNAL hb_unicode_funcs_t _hb_icu_unicode_funcs; +#elif defined(HAVE_ICU) +extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs; #define _hb_unicode_funcs_default _hb_icu_unicode_funcs #else -extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_nil; +#define HB_UNICODE_FUNCS_NIL 1 +extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil; #define _hb_unicode_funcs_default _hb_unicode_funcs_nil #endif +HB_INTERNAL unsigned int +_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); -HB_END_DECLS +static inline hb_bool_t +_hb_unicode_is_variation_selector (hb_codepoint_t unicode) +{ + return unlikely ((unicode >= 0x180B && unicode <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */ + (unicode >= 0xFE00 && unicode <= 0xFE0F) || /* VARIATION SELECTOR-1..16 */ + (unicode >= 0xE0100 && unicode <= 0xE01EF)); /* VARIATION SELECTOR-17..256 */ +} + +/* Zero-Width invisible characters: + * + * 00AD SOFT HYPHEN + * 034F COMBINING GRAPHEME JOINER + * + * 200B ZERO WIDTH SPACE + * 200C ZERO WIDTH NON-JOINER + * 200D ZERO WIDTH JOINER + * 200E LEFT-TO-RIGHT MARK + * 200F RIGHT-TO-LEFT MARK + * + * 2028 LINE SEPARATOR + * + * 202A LEFT-TO-RIGHT EMBEDDING + * 202B RIGHT-TO-LEFT EMBEDDING + * 202C POP DIRECTIONAL FORMATTING + * 202D LEFT-TO-RIGHT OVERRIDE + * 202E RIGHT-TO-LEFT OVERRIDE + * + * 2060 WORD JOINER + * 2061 FUNCTION APPLICATION + * 2062 INVISIBLE TIMES + * 2063 INVISIBLE SEPARATOR + * + * FEFF ZERO WIDTH NO-BREAK SPACE + */ +static inline hb_bool_t +_hb_unicode_is_zero_width (hb_codepoint_t ch) +{ + return ((ch & ~0x007F) == 0x2000 && ( + (ch >= 0x200B && ch <= 0x200F) || + (ch >= 0x202A && ch <= 0x202E) || + (ch >= 0x2060 && ch <= 0x2063) || + (ch == 0x2028) + )) || unlikely (ch == 0x0009 + || ch == 0x00AD + || ch == 0x034F + || ch == 0xFEFF); +} #endif /* HB_UNICODE_PRIVATE_HH */ diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc index aba2cd3..6a21ef3 100644 --- a/src/hb-unicode.cc +++ b/src/hb-unicode.cc @@ -32,7 +32,6 @@ #include "hb-unicode-private.hh" -HB_BEGIN_DECLS /* @@ -86,8 +85,7 @@ hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t *ab HB_UNUSED, void *user_data HB_UNUSED) { - /* TODO handle Hangul jamo here? */ - return FALSE; + return false; } static hb_bool_t @@ -97,28 +95,15 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t *b HB_UNUSED, void *user_data HB_UNUSED) { - /* TODO handle Hangul jamo here? */ - return FALSE; + return false; } -hb_unicode_funcs_t _hb_unicode_funcs_nil = { - HB_OBJECT_HEADER_STATIC, - - NULL, /* parent */ - TRUE, /* immutable */ - { -#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil, - HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_UNICODE_FUNC_IMPLEMENT - } -}; - hb_unicode_funcs_t * hb_unicode_funcs_get_default (void) { - return &_hb_unicode_funcs_default; + return const_cast (&_hb_unicode_funcs_default); } hb_unicode_funcs_t * @@ -127,10 +112,10 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent) hb_unicode_funcs_t *ufuncs; if (!(ufuncs = hb_object_create ())) - return &_hb_unicode_funcs_nil; + return hb_unicode_funcs_get_empty (); if (!parent) - parent = &_hb_unicode_funcs_nil; + parent = hb_unicode_funcs_get_empty (); hb_unicode_funcs_make_immutable (parent); ufuncs->parent = hb_unicode_funcs_reference (parent); @@ -145,10 +130,24 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent) return ufuncs; } + +//extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil; +const hb_unicode_funcs_t _hb_unicode_funcs_nil = { + HB_OBJECT_HEADER_STATIC, + + NULL, /* parent */ + true, /* immutable */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } +}; + hb_unicode_funcs_t * hb_unicode_funcs_get_empty (void) { - return &_hb_unicode_funcs_nil; + return const_cast (&_hb_unicode_funcs_nil); } hb_unicode_funcs_t * @@ -176,9 +175,10 @@ hb_bool_t hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy, + hb_bool_t replace) { - return hb_object_set_user_data (ufuncs, key, data, destroy); + return hb_object_set_user_data (ufuncs, key, data, destroy, replace); } void * @@ -195,7 +195,7 @@ hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs) if (hb_object_is_inert (ufuncs)) return; - ufuncs->immutable = TRUE; + ufuncs->immutable = true; } hb_bool_t @@ -207,7 +207,7 @@ hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs) hb_unicode_funcs_t * hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs) { - return ufuncs->parent ? ufuncs->parent : &_hb_unicode_funcs_nil; + return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty (); } @@ -271,4 +271,58 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, return ufuncs->func.decompose (ufuncs, ab, a, b, ufuncs->user_data.decompose); } -HB_END_DECLS + + +unsigned int +_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode) +{ + int c = hb_unicode_combining_class (ufuncs, unicode); + + if (unlikely (hb_in_range (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 (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; +} + diff --git a/src/hb-unicode.h b/src/hb-unicode.h index 9aa97a6..b26168f 100644 --- a/src/hb-unicode.h +++ b/src/hb-unicode.h @@ -28,6 +28,10 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_UNICODE_H #define HB_UNICODE_H @@ -66,8 +70,8 @@ hb_bool_t hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy); - + hb_destroy_func_t destroy, + hb_bool_t replace); void * hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs, diff --git a/src/hb-uniscribe-private.hh b/src/hb-uniscribe-private.hh new file mode 100644 index 0000000..239ab0c --- /dev/null +++ b/src/hb-uniscribe-private.hh @@ -0,0 +1,42 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_UNISCRIBE_PRIVATE_HH +#define HB_UNISCRIBE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-uniscribe.h" + + +HB_INTERNAL hb_bool_t +_hb_uniscribe_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + + +#endif /* HB_UNISCRIBE_PRIVATE_HH */ diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc new file mode 100644 index 0000000..9f84a3c --- /dev/null +++ b/src/hb-uniscribe.cc @@ -0,0 +1,465 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#define _WIN32_WINNT 0x0500 + +#include "hb-private.hh" + +#include +#include + +typedef ULONG WIN_ULONG; + +#include "hb-uniscribe.h" + +#include "hb-ot-name-table.hh" +#include "hb-ot-tag.h" + +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" + + + +#ifndef HB_DEBUG_UNISCRIBE +#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) +#endif + + +/* +DWORD GetFontData( + __in HDC hdc, + __in DWORD dwTable, + __in DWORD dwOffset, + __out LPVOID lpvBuffer, + __in DWORD cbData +); +*/ + +static bool +populate_log_font (LOGFONTW *lf, + HDC hdc, + hb_font_t *font) +{ + memset (lf, 0, sizeof (*lf)); + int dpi = GetDeviceCaps (hdc, LOGPIXELSY); + lf->lfHeight = -font->y_scale; + + hb_blob_t *blob = Sanitizer::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e'))); + const name *name_table = Sanitizer::lock_instance (blob); + unsigned int len = name_table->get_name (3, 1, 0x409, 4, + lf->lfFaceName, + sizeof (lf->lfFaceName[0]) * LF_FACESIZE) + / sizeof (lf->lfFaceName[0]); + hb_blob_destroy (blob); + + if (unlikely (!len)) { + DEBUG_MSG (UNISCRIBE, NULL, "Didn't find English name table entry"); + return false; + } + if (unlikely (len >= LF_FACESIZE)) { + DEBUG_MSG (UNISCRIBE, NULL, "Font name too long"); + return false; + } + + for (unsigned int i = 0; i < len; i++) + lf->lfFaceName[i] = hb_be_uint16 (lf->lfFaceName[i]); + lf->lfFaceName[len] = 0; + + return true; +} + + +static hb_user_data_key_t hb_uniscribe_data_key; + + +static struct hb_uniscribe_face_data_t { + HANDLE fh; +} _hb_uniscribe_face_data_nil = {0}; + +static void +_hb_uniscribe_face_data_destroy (hb_uniscribe_face_data_t *data) +{ + if (data->fh) + RemoveFontMemResourceEx (data->fh); + free (data); +} + +static hb_uniscribe_face_data_t * +_hb_uniscribe_face_get_data (hb_face_t *face) +{ + hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key); + if (likely (data)) return data; + + data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t)); + if (unlikely (!data)) + return &_hb_uniscribe_face_data_nil; + + + hb_blob_t *blob = hb_face_reference_blob (face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (UNISCRIBE, face, "Face has empty blob"); + + DWORD num_fonts_installed; + data->fh = AddFontMemResourceEx ((void *) blob_data, blob_length, 0, &num_fonts_installed); + hb_blob_destroy (blob); + if (unlikely (!data->fh)) + DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed"); + + + if (unlikely (!hb_face_set_user_data (face, &hb_uniscribe_data_key, data, + (hb_destroy_func_t) _hb_uniscribe_face_data_destroy, + false))) + { + _hb_uniscribe_face_data_destroy (data); + data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key); + if (data) + return data; + else + return &_hb_uniscribe_face_data_nil; + } + + return data; +} + + +static struct hb_uniscribe_font_data_t { + HDC hdc; + LOGFONTW log_font; + HFONT hfont; + SCRIPT_CACHE script_cache; +} _hb_uniscribe_font_data_nil = {NULL, NULL, NULL}; + +static void +_hb_uniscribe_font_data_destroy (hb_uniscribe_font_data_t *data) +{ + if (data->hdc) + ReleaseDC (NULL, data->hdc); + if (data->hfont) + DeleteObject (data->hfont); + if (data->script_cache) + ScriptFreeCache (&data->script_cache); + free (data); +} + +static hb_uniscribe_font_data_t * +_hb_uniscribe_font_get_data (hb_font_t *font) +{ + hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key); + if (likely (data)) return data; + + data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t)); + if (unlikely (!data)) + return &_hb_uniscribe_font_data_nil; + + data->hdc = GetDC (NULL); + + if (unlikely (!populate_log_font (&data->log_font, data->hdc, font))) + DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); + else { + data->hfont = CreateFontIndirectW (&data->log_font); + if (unlikely (!data->hfont)) + DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); + if (!SelectObject (data->hdc, data->hfont)) + DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed"); + } + + if (unlikely (!hb_font_set_user_data (font, &hb_uniscribe_data_key, data, + (hb_destroy_func_t) _hb_uniscribe_font_data_destroy, + false))) + { + _hb_uniscribe_font_data_destroy (data); + data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key); + if (data) + return data; + else + return &_hb_uniscribe_font_data_nil; + } + + return data; +} + +LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font) +{ + hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); + if (unlikely (!font_data)) + return NULL; + return &font_data->log_font; +} + +HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font) +{ + hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); + if (unlikely (!font_data)) + return 0; + return font_data->hfont; +} + + +hb_bool_t +_hb_uniscribe_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + buffer->guess_properties (); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \ + return false; \ + } HB_STMT_END; + + hb_uniscribe_face_data_t *face_data = _hb_uniscribe_face_get_data (font->face); + if (unlikely (!face_data->fh)) + FAIL ("Couldn't get face data"); + + hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font); + if (unlikely (!font_data->hfont)) + FAIL ("Couldn't get font font"); + + if (unlikely (!buffer->len)) + return true; + + HRESULT hr; + +retry: + + unsigned int scratch_size; + char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size); + + /* Allocate char buffers; they all fit */ + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + scratch += len * sizeof (name[0]); \ + scratch_size -= len * sizeof (name[0]); + +#define utf16_index() var1.u32 + + WCHAR *pchars = (WCHAR *) scratch; + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index() = chars_len; + if (likely (c < 0x10000)) + pchars[chars_len++] = c; + else if (unlikely (c >= 0x110000)) + pchars[chars_len++] = 0xFFFD; + else { + pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10); + pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WCHAR, wchars, chars_len); + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); + + /* On Windows, we don't care about alignment...*/ + unsigned int glyphs_size = scratch_size / (sizeof (WORD) + + sizeof (SCRIPT_GLYPHPROP) + + sizeof (int) + + sizeof (GOFFSET) + + sizeof (uint32_t)); + + ALLOCATE_ARRAY (WORD, glyphs, glyphs_size); + ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size); + ALLOCATE_ARRAY (int, advances, glyphs_size); + ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + + +#define MAX_ITEMS 10 + + SCRIPT_ITEM items[MAX_ITEMS + 1]; + SCRIPT_CONTROL bidi_control = {0}; + SCRIPT_STATE bidi_state = {0}; + WIN_ULONG script_tags[MAX_ITEMS]; + int item_count; + + /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ + //bidi_control.fMergeNeutralItems = true; + *(uint32_t*)&bidi_control |= 1<<24; + + bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; +// bidi_state.fOverrideDirection = 1; + + hr = ScriptItemizeOpenType (wchars, + chars_len, + MAX_ITEMS, + &bidi_control, + &bidi_state, + items, + script_tags, + &item_count); + if (unlikely (FAILED (hr))) + FAIL ("ScriptItemizeOpenType() failed: 0x%08xL", hr); + +#undef MAX_ITEMS + + int *range_char_counts = NULL; + TEXTRANGE_PROPERTIES **range_properties = NULL; + int range_count = 0; + if (num_features) { + /* TODO setup ranges */ + } + + OPENTYPE_TAG language_tag = hb_ot_tag_from_language (buffer->props.language); + + unsigned int glyphs_offset = 0; + unsigned int glyphs_len; + for (unsigned int i = 0; i < item_count; i++) + { + unsigned int chars_offset = items[i].iCharPos; + unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset; + OPENTYPE_TAG script_tag = script_tags[i]; /* XXX buffer->props.script */ + + hr = ScriptShapeOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tag, + language_tag, + range_char_counts, + range_properties, + range_count, + wchars + chars_offset, + item_chars_len, + glyphs_size - glyphs_offset, + /* out */ + log_clusters + chars_offset, + char_props + chars_offset, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + (int *) &glyphs_len); + + for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) + log_clusters[j] += glyphs_offset; + + if (unlikely (items[i].a.fNoGlyphIndex)) + FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); + if (unlikely (hr == E_OUTOFMEMORY)) + { + buffer->ensure (buffer->allocated * 2); + if (buffer->in_error) + FAIL ("Buffer resize failed"); + goto retry; + } + if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) + FAIL ("ScriptShapeOpenType() failed: Font doesn't support script"); + if (unlikely (FAILED (hr))) + FAIL ("ScriptShapeOpenType() failed: 0x%08xL", hr); + + hr = ScriptPlaceOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tag, + language_tag, + range_char_counts, + range_properties, + range_count, + wchars + chars_offset, + log_clusters + chars_offset, + char_props + chars_offset, + item_chars_len, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + glyphs_len, + /* out */ + advances + glyphs_offset, + offsets + glyphs_offset, + NULL); + if (unlikely (FAILED (hr))) + FAIL ("ScriptPlaceOpenType() failed: 0x%08xL", hr); + + glyphs_offset += glyphs_len; + } + glyphs_len = glyphs_offset; + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphs_len; i++) + vis_clusters[i] = -1; + for (unsigned int i = 0; i < buffer->len; i++) { + uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; + *p = MIN (*p, buffer->info[i].cluster); + } + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + for (unsigned int i = 1; i < glyphs_len; i++) + if (!glyph_props[i].sva.fClusterStart) + vis_clusters[i] = vis_clusters[i - 1]; + } else { + for (int i = glyphs_len - 2; i >= 0; i--) + if (!glyph_props[i].sva.fClusterStart) + vis_clusters[i] = vis_clusters[i + 1]; + } + +#undef utf16_index + + buffer->ensure (glyphs_len); + if (buffer->in_error) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphs_len; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphs[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = advances[i]; + info->var1.u32 = offsets[i].du; + info->var2.u32 = offsets[i].dv; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphs_len; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = info->mask; + pos->x_offset = info->var1.u32; + pos->y_offset = info->var2.u32; + } + + /* Wow, done! */ + return true; +} + + diff --git a/src/hb-ot-shape.h b/src/hb-uniscribe.h similarity index 75% rename from src/hb-ot-shape.h rename to src/hb-uniscribe.h index f9560e5..216610e 100644 --- a/src/hb-ot-shape.h +++ b/src/hb-uniscribe.h @@ -1,5 +1,5 @@ /* - * Copyright © 2010 Red Hat, Inc. + * Copyright © 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -21,26 +21,27 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_SHAPE_H -#define HB_OT_SHAPE_H +#ifndef HB_UNISCRIBE_H +#define HB_UNISCRIBE_H -#include "hb-common.h" -#include "hb-shape.h" +#include "hb.h" +#define _WIN32_WINNT 0x0500 +#include HB_BEGIN_DECLS -void -hb_ot_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); +LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font); + +HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font); HB_END_DECLS -#endif /* HB_OT_SHAPE_H */ +#endif /* HB_UNISCRIBE_H */ diff --git a/src/hb-version.h b/src/hb-version.h new file mode 100644 index 0000000..43ec9cf --- /dev/null +++ b/src/hb-version.h @@ -0,0 +1,66 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_VERSION_H +#define HB_VERSION_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +#define HB_VERSION_MAJOR 0 +#define HB_VERSION_MINOR 9 +#define HB_VERSION_MICRO 0 + +#define HB_VERSION_STRING "0.9.0" + +#define HB_VERSION_CHECK(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) >= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +const char * +hb_version_string (void); + +hb_bool_t +hb_version_check (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/src/hb-version.h.in b/src/hb-version.h.in index 47a9b30..43634f9 100644 --- a/src/hb-version.h.in +++ b/src/hb-version.h.in @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_H_IN +#error "Include instead." +#endif + #ifndef HB_VERSION_H #define HB_VERSION_H diff --git a/src/hb-view.cc b/src/hb-view.cc deleted file mode 100644 index 31b2a24..0000000 --- a/src/hb-view.cc +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright © 2010 Behdad Esfahbod - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -HB_BEGIN_DECLS - - -/* Controlled by cmd-line options */ -static int margin_t = 10; -static int margin_b = 10; -static int margin_l = 10; -static int margin_r = 10; -static int line_space = 0; -static int face_index = 0; -static double font_size = 18; -static const char *fore = "#000000"; -static const char *back = "#ffffff"; -static const char *text = NULL; -static const char *font_file = NULL; -static const char *out_file = "/dev/stdout"; -static const char *direction = NULL; -static const char *script = NULL; -static const char *language = NULL; -static hb_feature_t *features = NULL; -static unsigned int num_features; -static hb_bool_t annotate = FALSE; -static hb_bool_t debug = FALSE; - -/* Ugh, global vars. Ugly, but does the job */ -static int width = 0; -static int height = 0; -static cairo_surface_t *surface = NULL; -static cairo_pattern_t *fore_pattern = NULL; -static cairo_pattern_t *back_pattern = NULL; -static cairo_font_face_t *cairo_face; - - -G_GNUC_NORETURN static void -usage (FILE *f, int status) -{ - fprintf (f, "usage: hb-view [OPTS...] font-file text\n"); - exit (status); -} - -G_GNUC_NORETURN static void -version (void) -{ - printf ("hb-view (harfbuzz) %s\n", HB_VERSION_STRING); - exit (0); -} - -static void parse_features (char *s); - -static void -parse_opts (int argc, char **argv) -{ - argv[0] = (char *) "hb-view"; - while (1) - { - int option_index = 0, c; - static const struct option long_options[] = { - {"annotate", 0, &annotate, TRUE}, - {"background", 1, 0, 'B'}, - {"debug", 0, &debug, TRUE}, - {"direction", 1, 0, 'd'}, - {"features", 1, 0, 'f'}, - {"font-size", 1, 0, 's'}, - {"face-index", 1, 0, 'i'}, - {"foreground", 1, 0, 'F'}, - {"help", 0, 0, 'h'}, - {"language", 1, 0, 'L'}, - {"line-space", 1, 0, 'l'}, - {"margin", 1, 0, 'm'}, - {"output", 1, 0, 'o'}, - {"script", 1, 0, 'S'}, - {"version", 0, 0, 'v'}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "", long_options, &option_index); - if (c == -1) - break; - - switch (c) - { - case 0: - break; - case 'h': - usage (stdout, 0); - break; - case 'v': - version (); - break; - case 'i': - face_index = atoi (optarg); - break; - case 'l': - line_space = atoi (optarg); - break; - case 'm': - switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) { - default: break; - case 1: margin_r = margin_t; - case 2: margin_b = margin_t; - case 3: margin_l = margin_r; - } - break; - case 's': - font_size = strtod (optarg, NULL); - break; - case 'f': - parse_features (optarg); - break; - case 'F': - fore = optarg; - break; - case 'B': - back = optarg; - break; - case 't': - text = optarg; - break; - case 'd': - direction = optarg; - break; - case 'S': - script = optarg; - break; - case 'L': - language = optarg; - break; - case 'o': - out_file = optarg; - break; - case '?': - usage (stdout, 1); - break; - default: - break; - } - } - if (optind + 2 != argc) - usage (stderr, 1); - font_file = argv[optind++]; - text = argv[optind++]; -} - - -static void -parse_space (char **pp) -{ - char c; -#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v') - while (c = **pp, ISSPACE (c)) - (*pp)++; -#undef ISSPACE -} - -static hb_bool_t -parse_char (char **pp, char c) -{ - parse_space (pp); - - if (**pp != c) - return FALSE; - - (*pp)++; - return TRUE; -} - -static hb_bool_t -parse_uint (char **pp, unsigned int *pv) -{ - char *p = *pp; - unsigned int v; - - v = strtol (p, pp, 0); - - if (p == *pp) - return FALSE; - - *pv = v; - return TRUE; -} - - -static hb_bool_t -parse_feature_value_prefix (char **pp, hb_feature_t *feature) -{ - if (parse_char (pp, '-')) - feature->value = 0; - else { - parse_char (pp, '+'); - feature->value = 1; - } - - return TRUE; -} - -static hb_bool_t -parse_feature_tag (char **pp, hb_feature_t *feature) -{ - char *p = *pp, c; - - parse_space (pp); - -#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) - while (c = **pp, ISALNUM(c)) - (*pp)++; -#undef ISALNUM - - if (p == *pp) - return FALSE; - - **pp = '\0'; - feature->tag = hb_tag_from_string (p); - **pp = c; - - return TRUE; -} - -static hb_bool_t -parse_feature_indices (char **pp, hb_feature_t *feature) -{ - hb_bool_t has_start; - - feature->start = 0; - feature->end = (unsigned int) -1; - - if (!parse_char (pp, '[')) - return TRUE; - - has_start = parse_uint (pp, &feature->start); - - if (parse_char (pp, ':')) { - parse_uint (pp, &feature->end); - } else { - if (has_start) - feature->end = feature->start + 1; - } - - return parse_char (pp, ']'); -} - -static hb_bool_t -parse_feature_value_postfix (char **pp, hb_feature_t *feature) -{ - return !parse_char (pp, '=') || parse_uint (pp, &feature->value); -} - - -static hb_bool_t -parse_one_feature (char **pp, hb_feature_t *feature) -{ - return parse_feature_value_prefix (pp, feature) && - parse_feature_tag (pp, feature) && - parse_feature_indices (pp, feature) && - parse_feature_value_postfix (pp, feature) && - (parse_char (pp, ',') || **pp == '\0'); -} - -static void -skip_one_feature (char **pp) -{ - char *e; - e = strchr (*pp, ','); - if (e) - *pp = e + 1; - else - *pp = *pp + strlen (*pp); -} - -static void parse_features (char *s) -{ - char *p; - - num_features = 0; - features = NULL; - - if (!*s) - return; - - /* count the features first, so we can allocate memory */ - p = s; - do { - num_features++; - p = strchr (p, ','); - if (p) - p++; - } while (p); - - features = (hb_feature_t *) calloc (num_features, sizeof (*features)); - - /* now do the actual parsing */ - p = s; - num_features = 0; - while (*p) { - if (parse_one_feature (&p, &features[num_features])) - num_features++; - else - skip_one_feature (&p); - } -} - - -static cairo_glyph_t * -_hb_cr_text_glyphs (cairo_t *cr, - const char *utf8, int len, - unsigned int *pnum_glyphs) -{ - cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr); - FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); - hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL); - hb_buffer_t *hb_buffer; - cairo_glyph_t *cairo_glyphs; - hb_glyph_info_t *hb_glyph; - hb_glyph_position_t *hb_position; - unsigned int num_glyphs, i; - hb_position_t x, y; - - hb_buffer = hb_buffer_create (0); - - if (direction) - hb_buffer_set_direction (hb_buffer, hb_direction_from_string (direction)); - if (script) - hb_buffer_set_script (hb_buffer, hb_script_from_string (script)); - if (language) - hb_buffer_set_language (hb_buffer, hb_language_from_string (language)); - - if (len < 0) - len = strlen (utf8); - hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len); - - hb_shape (hb_font, hb_buffer, features, num_features); - - num_glyphs = hb_buffer_get_length (hb_buffer); - hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL); - hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL); - cairo_glyphs = cairo_glyph_allocate (num_glyphs); - x = 0; - y = 0; - for (i = 0; i < num_glyphs; i++) - { - cairo_glyphs[i].index = hb_glyph->codepoint; - cairo_glyphs[i].x = ( hb_position->x_offset + x) * (1./64); - cairo_glyphs[i].y = (-hb_position->y_offset + y) * (1./64); - x += hb_position->x_advance; - y += -hb_position->y_advance; - - hb_glyph++; - hb_position++; - } - hb_buffer_destroy (hb_buffer); - hb_font_destroy (hb_font); - cairo_ft_scaled_font_unlock_face (scaled_font); - - if (pnum_glyphs) - *pnum_glyphs = num_glyphs; - return cairo_glyphs; -} - -static cairo_t * -create_context (void) -{ - cairo_t *cr; - unsigned int fr, fg, fb, fa, br, bg, bb, ba; - - if (surface) - cairo_surface_destroy (surface); - if (back_pattern) - cairo_pattern_destroy (back_pattern); - if (fore_pattern) - cairo_pattern_destroy (fore_pattern); - - br = bg = bb = ba = 255; - sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); - fr = fg = fb = 0; fa = 255; - sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); - - if (!annotate && ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) { - /* grayscale. use A8 surface */ - surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - cr = cairo_create (surface); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); - cairo_paint (cr); - back_pattern = cairo_pattern_reference (cairo_get_source (cr)); - cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.); - fore_pattern = cairo_pattern_reference (cairo_get_source (cr)); - } else { - /* color. use (A)RGB surface */ - if (ba != 255) - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - else - surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); - cr = cairo_create (surface); - cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); - cairo_paint (cr); - back_pattern = cairo_pattern_reference (cairo_get_source (cr)); - cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); - fore_pattern = cairo_pattern_reference (cairo_get_source (cr)); - } - - cairo_set_font_face (cr, cairo_face); - - return cr; -} - -static void -draw (void) -{ - cairo_t *cr; - cairo_font_extents_t font_extents; - - cairo_glyph_t *glyphs = NULL; - unsigned int num_glyphs = 0; - - const char *end, *p = text; - double x, y; - - cr= create_context (); - - cairo_set_font_size (cr, font_size); - cairo_font_extents (cr, &font_extents); - - height = 0; - width = 0; - - x = margin_l; - y = margin_t; - - do { - cairo_text_extents_t extents; - - end = strchr (p, '\n'); - if (!end) - end = p + strlen (p); - - if (p != text) - y += line_space; - - if (p != end) { - glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs); - - cairo_glyph_extents (cr, glyphs, num_glyphs, &extents); - - y += ceil (font_extents.ascent); - width = MAX (width, extents.x_advance); - cairo_save (cr); - cairo_translate (cr, x, y); - if (annotate) { - unsigned int i; - cairo_save (cr); - - /* Draw actual glyph origins */ - cairo_set_source_rgba (cr, 1., 0., 0., .5); - cairo_set_line_width (cr, 5); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - for (i = 0; i < num_glyphs; i++) { - cairo_move_to (cr, glyphs[i].x, glyphs[i].y); - cairo_rel_line_to (cr, 0, 0); - } - cairo_stroke (cr); - - cairo_restore (cr); - } - cairo_show_glyphs (cr, glyphs, num_glyphs); - cairo_restore (cr); - y += ceil (font_extents.height - ceil (font_extents.ascent)); - - cairo_glyph_free (glyphs); - } - - p = end + 1; - } while (*end); - - height = y + margin_b; - width += margin_l + margin_r; - - cairo_destroy (cr); -} - - - -int -main (int argc, char **argv) -{ - static FT_Library ft_library; - static FT_Face ft_face; - cairo_status_t status; - - setlocale (LC_ALL, ""); - - parse_opts (argc, argv); - - FT_Init_FreeType (&ft_library); - if (FT_New_Face (ft_library, font_file, face_index, &ft_face)) { - fprintf (stderr, "Failed to open font file `%s'\n", font_file); - exit (1); - } - cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); - - draw (); - draw (); - - status = cairo_surface_write_to_png (surface, out_file); - if (status != CAIRO_STATUS_SUCCESS) { - fprintf (stderr, "Failed to write output file `%s': %s\n", - out_file, cairo_status_to_string (status)); - exit (1); - } - - if (debug) { - free (features); - - cairo_pattern_destroy (fore_pattern); - cairo_pattern_destroy (back_pattern); - cairo_surface_destroy (surface); - cairo_font_face_destroy (cairo_face); - cairo_debug_reset_static_data (); - - FT_Done_Face (ft_face); - FT_Done_FreeType (ft_library); - } - - return 0; -} - - -HB_END_DECLS diff --git a/src/hb-warning.cc b/src/hb-warning.cc new file mode 100644 index 0000000..4f1f65f --- /dev/null +++ b/src/hb-warning.cc @@ -0,0 +1,66 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-atomic-private.hh" +#include "hb-mutex-private.hh" + + +#if defined(HB_ATOMIC_INT_NIL) +#ifdef _MSC_VER +#pragma message("Could not find any system to define atomic_int macros, library may NOT be thread-safe") +#else +#warning "Could not find any system to define atomic_int macros, library may NOT be thread-safe" +#endif +#endif + +#if defined(HB_MUTEX_IMPL_NIL) +#ifdef _MSC_VER +#pragma message("Could not find any system to define mutex macros, library may NOT be thread-safe") +#else +#warning "Could not find any system to define mutex macros, library may NOT be thread-safe" +#endif +#endif + +#if defined(HB_ATOMIC_INT_NIL) || defined(HB_MUTEX_IMPL_NIL) +#ifdef _MSC_VER +#pragma message("To suppress these warnings, define HB_NO_MT") +#else +#warning "To suppress these warnings, define HB_NO_MT" +#endif +#endif + + +#include "hb-unicode-private.hh" + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) +#ifdef _MSC_VER +#pragma message("Could not find any Unicode functions implementation, you have to provide your own") +#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS") +#else +#warning "Could not find any Unicode functions implementation, you have to provide your own" +#warning "To suppress this warning, define HB_NO_UNICODE_FUNCS" +#endif +#endif diff --git a/src/hb.h b/src/hb.h index 0a2ebd9..d36040e 100644 --- a/src/hb.h +++ b/src/hb.h @@ -26,11 +26,13 @@ #ifndef HB_H #define HB_H +#define HB_H_IN #include "hb-blob.h" #include "hb-buffer.h" #include "hb-common.h" #include "hb-font.h" +#include "hb-set.h" #include "hb-shape.h" #include "hb-unicode.h" #include "hb-version.h" @@ -38,4 +40,5 @@ HB_BEGIN_DECLS HB_END_DECLS +#undef HB_H_IN #endif /* HB_H */ diff --git a/src/indic.cc b/src/indic.cc new file mode 100644 index 0000000..3b44076 --- /dev/null +++ b/src/indic.cc @@ -0,0 +1,46 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-shape-complex-indic-private.hh" + +int +main (void) +{ + hb_unicode_funcs_t *funcs = hb_unicode_funcs_get_default (); + + printf ("There are split matras without a Unicode decomposition:\n"); + for (hb_codepoint_t u = 0; u < 0x110000; u++) + { + unsigned int type = get_indic_categories (u); + + unsigned int category = type & 0x0F; + unsigned int position = type >> 4; + + hb_codepoint_t a, b; + if (!hb_unicode_decompose (funcs, u, &a, &b)) + printf ("U+%04X %x %x\n", u, category, position); + } +} diff --git a/src/main.cc b/src/main.cc index 4bf809e..07d3d69 100644 --- a/src/main.cc +++ b/src/main.cc @@ -24,9 +24,9 @@ * Red Hat Author(s): Behdad Esfahbod */ -#define HB_OT_LAYOUT_CC +#include "hb-mutex-private.hh" #include "hb-open-file-private.hh" -#include "hb-ot-layout-gdef-private.hh" +#include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsubgpos-private.hh" #ifdef HAVE_GLIB @@ -35,7 +35,6 @@ #include #include -HB_BEGIN_DECLS int @@ -50,7 +49,7 @@ main (int argc, char **argv) int len = 0; #ifdef HAVE_GLIB - GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL); + GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL); font_data = g_mapped_file_get_contents (mf); len = g_mapped_file_get_length (mf); #else @@ -125,10 +124,11 @@ main (int argc, char **argv) const LangSys &langsys = n_langsys == -1 ? script.get_default_lang_sys () : script.get_lang_sys (n_langsys); - printf (n_langsys == -1 - ? " Default Language System\n" - : " Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, - (const char *)script.get_lang_sys_tag (n_langsys)); + if (n_langsys == -1) + printf (" Default Language System\n"); + else + printf (" Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, + (const char *)script.get_lang_sys_tag (n_langsys)); if (langsys.get_required_feature_index () == Index::NOT_FOUND_INDEX) printf (" No required feature\n"); @@ -193,4 +193,3 @@ main (int argc, char **argv) } -HB_END_DECLS diff --git a/src/test.cc b/src/test.cc deleted file mode 100644 index e3747ce..0000000 --- a/src/test.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright © 2010 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include "hb.h" - -#ifdef HAVE_GLIB -#include -#endif -#include -#include - -HB_BEGIN_DECLS - - -int -main (int argc, char **argv) -{ - hb_blob_t *blob = NULL; - hb_face_t *face = NULL; - - if (argc != 2) { - fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); - exit (1); - } - - /* Create the blob */ - { - const char *font_data; - unsigned int len; - hb_destroy_func_t destroy; - void *user_data; - hb_memory_mode_t mm; - -#ifdef HAVE_GLIB - GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL); - font_data = g_mapped_file_get_contents (mf); - len = g_mapped_file_get_length (mf); - destroy = (hb_destroy_func_t) g_mapped_file_unref; - user_data = (void *) mf; - mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE; -#else - FILE *f = fopen (argv[1], "rb"); - fseek (f, 0, SEEK_END); - len = ftell (f); - fseek (f, 0, SEEK_SET); - font_data = (const char *) malloc (len); - if (!font_data) len = 0; - len = fread ((char *) font_data, 1, len, f); - destroy = free; - user_data = (void *) font_data; - fclose (f); - mm = HB_MEMORY_MODE_WRITABLE; -#endif - - blob = hb_blob_create (font_data, len, mm, user_data, destroy); - } - - printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob)); - - /* Create the face */ - face = hb_face_create (blob, 0 /* first face */); - - /* So, what now? */ - - hb_face_destroy (face); - hb_blob_destroy (blob); - - return 0; -} - - -HB_END_DECLS diff --git a/test/.valgrind-suppressions b/test/.valgrind-suppressions deleted file mode 100644 index e69de29..0000000 diff --git a/test/Makefile.am b/test/Makefile.am index bad1118..16a3cd2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,131 +1,5 @@ # Process this file with automake to produce Makefile.in -NULL = -EXTRA_DIST = -CLEANFILES = -DISTCLEANFILES = -MAINTAINERCLEANFILES = - -if HAVE_GLIB -AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) -LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS) $(GTHREAD_LIBS) - -EXTRA_DIST += hb-test.h - -check_PROGRAMS = $(TEST_PROGS) -noinst_PROGRAMS = $(TEST_PROGS) - -TEST_PROGS = \ - test-blob \ - test-buffer \ - test-common \ - test-font \ - test-object \ - test-shape \ - test-unicode \ - test-version \ - $(NULL) - -TEST_PROGS += \ - test-ot-tag \ - $(NULL) - -# Tests for header compilation -TEST_PROGS += \ - test-c \ - test-cplusplus \ - $(NULL) -test_cplusplus_SOURCES = test-cplusplus.cc -test_c_CPPFLAGS = $(AM_CPPFLAGS) -test_cplusplus_CPPFLAGS = $(AM_CPPFLAGS) -if HAVE_ICU -test_c_CPPFLAGS += $(ICU_CFLAGS) -test_cplusplus_CPPFLAGS += $(ICU_CFLAGS) -endif -if HAVE_FREETYPE -test_c_CPPFLAGS += $(FREETYPE_CFLAGS) -test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS) -# TODO replace freetype with other stuff in the following test -test_object_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS) -test_object_LDADD = $(LDADD) $(FREETYPE_LIBS) - -TEST_PROGS += \ - test-shape-complex \ - $(NULL) -test_shape_complex_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS) -test_shape_complex_LDADD = $(LDADD) $(FREETYPE_LIBS) -endif - - -# Default test running environment -TESTS = $(TEST_PROGS) -TESTS_ENVIRONMENT = \ - MALLOC_CHECK_=2 \ - MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \ - G_DEBUG=gc-friendly \ - G_SLICE=always-malloc \ - srcdir=$(srcdir) \ - $(ENV) - - -# check-tool: Run tests under $(TOOL) -check-tool: - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ - TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute \ - env $(TOOL)' -# check-tool-raw: Run tests under $(TOOL), but don't run under libtool -check-tool-raw: - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ - TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) \ - env $(TOOL)' - -# check-gtester: Run tests under gtester -GTESTER = gtester -check-gtester: - $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool-raw TOOL="$(GTESTER) --verbose --keep-going" - - -# Check tests under valgrind. Saves log to log-valgrind.txt -VALGRIND_FLAGS = \ - --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \ - --track-origins=yes \ - --leak-check=yes - $(EXTRA_VALGRIND_FLAGS) -# Can't do for now: --show-reachable=yes -CLEANFILES += log-valgrind.txt -valgrind_verbose = $(valgrind_verbose_$(V)) -valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY)) -valgrind_verbose_0 = | \ - grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0' -# TODO: The following check does not fail if valgrind finds error. It should. -check-valgrind: - $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool TOOL="valgrind $(VALGRIND_FLAGS)" \ - 2>&1 | tee log-valgrind.txt $(valgrind_verbose) - - -# check-symbols: Finds untested API symbols -symbols-tested.txt: $(TEST_PROGS) - $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ - | grep ' U hb_' | sed 's/.* U hb_/hb_/' \ - | sort | uniq > $@.tmp && mv $@.tmp $@ -symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so - $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ - | grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \ - | sort | uniq > $@.tmp && mv $@.tmp $@ -symbols-untested.txt: symbols-tested.txt symbols-exported.txt - $(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@ -CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt -check-symbols: symbols-untested.txt - @! cat $^ | grep . - - - -else -check-am: - @echo "You need to have glib support enabled to run the tests" - @exit 77 -endif - -.PHONY: check-symbols check-tool check-valgrind +SUBDIRS = api shaping -include $(top_srcdir)/git.mk diff --git a/test/api/Makefile.am b/test/api/Makefile.am new file mode 100644 index 0000000..e6c0c01 --- /dev/null +++ b/test/api/Makefile.am @@ -0,0 +1,125 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +if HAVE_GLIB +AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ -I$(top_builddir)/src/ $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) +LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS) $(GTHREAD_LIBS) + +EXTRA_DIST += hb-test.h + +check_PROGRAMS = $(TEST_PROGS) +noinst_PROGRAMS = $(TEST_PROGS) + +TEST_PROGS = \ + test-blob \ + test-buffer \ + test-common \ + test-font \ + test-object \ + test-shape \ + test-unicode \ + test-version \ + $(NULL) + +if HAVE_OT +TEST_PROGS += \ + test-ot-tag \ + $(NULL) +endif + +# Tests for header compilation +TEST_PROGS += \ + test-c \ + test-cplusplus \ + $(NULL) +test_cplusplus_SOURCES = test-cplusplus.cc +test_c_CPPFLAGS = $(AM_CPPFLAGS) +test_cplusplus_CPPFLAGS = $(AM_CPPFLAGS) +if HAVE_ICU +test_c_CPPFLAGS += $(ICU_CFLAGS) +test_cplusplus_CPPFLAGS += $(ICU_CFLAGS) +endif +if HAVE_FREETYPE +test_c_CPPFLAGS += $(FREETYPE_CFLAGS) +test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS) +endif + + +# Default test running environment +TESTS = $(TEST_PROGS) +TESTS_ENVIRONMENT = \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \ + G_DEBUG=gc-friendly \ + G_SLICE=always-malloc \ + srcdir=$(srcdir) \ + $(ENV) + + +# check-tool: Run tests under $(TOOL) +check-tool: + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ + TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute \ + env $(TOOL)' +# check-tool-raw: Run tests under $(TOOL), but don't run under libtool +check-tool-raw: + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ + TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) \ + env $(TOOL)' + +# check-gtester: Run tests under gtester +GTESTER = gtester +check-gtester: + $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool-raw TOOL="$(GTESTER) --verbose --keep-going" + + +# Check tests under valgrind. Saves log to log-valgrind.txt +VALGRIND_FLAGS = \ + --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \ + --track-origins=yes \ + --leak-check=yes + $(EXTRA_VALGRIND_FLAGS) +# Can't do for now: --show-reachable=yes +CLEANFILES += log-valgrind.txt +valgrind_verbose = $(valgrind_verbose_$(V)) +valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY)) +valgrind_verbose_0 = | \ + grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0' +# TODO: The following check does not fail if valgrind finds error. It should. +check-valgrind: + $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool TOOL="valgrind $(VALGRIND_FLAGS)" \ + 2>&1 | tee log-valgrind.txt $(valgrind_verbose) + + +# check-symbols: Finds untested API symbols +symbols-tested.txt: $(TEST_PROGS) + $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ + | grep ' U hb_' | sed 's/.* U hb_/hb_/' \ + | sort | uniq > $@.tmp && mv $@.tmp $@ +symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so + $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ + | grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \ + | sort | uniq > $@.tmp && mv $@.tmp $@ +symbols-untested.txt: symbols-tested.txt symbols-exported.txt + $(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@ +CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt +check-symbols: symbols-untested.txt + @! cat $^ | grep . + + + +else +check-am: err-glib +err-glib: + @echo "You need to have glib support enabled to run the tests" + @exit 77 +endif + +.PHONY: check-symbols check-tool check-valgrind + +-include $(top_srcdir)/git.mk diff --git a/test/hb-test.h b/test/api/hb-test.h similarity index 98% rename from test/hb-test.h rename to test/api/hb-test.h index de9a79a..f36d53b 100644 --- a/test/hb-test.h +++ b/test/api/hb-test.h @@ -152,8 +152,7 @@ typedef GTestFixtureFunc hb_test_fixture_func_t; #else typedef void (*hb_test_func_t) (void); typedef void (*hb_test_data_func_t) (gconstpointer user_data); -typedef void (*hb_test_fixture_func_t) (gpointer fixture, - gconstpointer user_data); +typedef void (*hb_test_fixture_func_t) (void); #endif #if !GLIB_CHECK_VERSION(2,30,0) diff --git a/test/test-blob.c b/test/api/test-blob.c similarity index 100% rename from test/test-blob.c rename to test/api/test-blob.c diff --git a/test/test-buffer.c b/test/api/test-buffer.c similarity index 98% rename from test/test-buffer.c rename to test/api/test-buffer.c index d387284..ab818d0 100644 --- a/test/test-buffer.c +++ b/test/api/test-buffer.c @@ -62,7 +62,7 @@ fixture_init (fixture_t *fixture, gconstpointer user_data) hb_buffer_t *b; unsigned int i; - b = fixture->buffer = hb_buffer_create (0); + b = fixture->buffer = hb_buffer_create (); switch (GPOINTER_TO_INT (user_data)) { @@ -124,8 +124,8 @@ test_buffer_properties (fixture_t *fixture, gconstpointer user_data) hb_buffer_set_script (b, HB_SCRIPT_ARABIC); g_assert (hb_buffer_get_script (b) == HB_SCRIPT_ARABIC); - hb_buffer_set_language (b, hb_language_from_string ("fa")); - g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa")); + hb_buffer_set_language (b, hb_language_from_string ("fa", -1)); + g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa", -1)); /* test reset clears properties */ @@ -344,7 +344,7 @@ test_buffer_utf8_conversion (void) hb_glyph_info_t *glyphs; unsigned int bytes, chars, i, j, len; - b = hb_buffer_create (0); + b = hb_buffer_create (); for (i = 0; i < G_N_ELEMENTS (utf8_conversion_tests); i++) { @@ -627,7 +627,7 @@ test_buffer_utf8_validity (void) hb_buffer_t *b; unsigned int i; - b = hb_buffer_create (0); + b = hb_buffer_create (); for (i = 0; i < G_N_ELEMENTS (utf8_validity_tests); i++) { @@ -684,7 +684,7 @@ test_buffer_utf16_conversion (void) hb_buffer_t *b; unsigned int i; - b = hb_buffer_create (0); + b = hb_buffer_create (); for (i = 0; i < G_N_ELEMENTS (utf16_conversion_tests); i++) { @@ -726,7 +726,6 @@ test_buffer_empty (void) g_assert (hb_buffer_get_empty ()); g_assert (hb_buffer_get_empty () == b); - g_assert (hb_buffer_get_empty () == hb_buffer_create (-1)); g_assert (!hb_buffer_allocation_successful (b)); diff --git a/test/test-c.c b/test/api/test-c.c similarity index 87% rename from test/test-c.c rename to test/api/test-c.c index 543fa7b..25a38e5 100644 --- a/test/test-c.c +++ b/test/api/test-c.c @@ -31,20 +31,28 @@ #include -#if HAVE_GLIB +#ifdef HAVE_GLIB #include #endif -#if HAVE_ICU +#ifdef HAVE_ICU #include #endif -#if HAVE_FREETYPE +#ifdef HAVE_FREETYPE #include #endif +#ifdef HAVE_OT +#include +#endif + +#ifdef HAVE_UNISCRIBE +#include +#endif + int main (int argc, char **argv) { - return 0; + return !*hb_shape_list_shapers (); } diff --git a/test/test-common.c b/test/api/test-common.c similarity index 70% rename from test/test-common.c rename to test/api/test-common.c index d5cbfc7..74b50be 100644 --- a/test/test-common.c +++ b/test/api/test-common.c @@ -51,42 +51,55 @@ test_types_int (void) static void test_types_direction (void) { - g_assert_cmpint ((signed) HB_DIRECTION_INVALID, ==, -1); - g_assert_cmpint (HB_DIRECTION_LTR, ==, 0); + g_assert_cmpint ((signed) HB_DIRECTION_INVALID, ==, 0); + g_assert_cmpint (HB_DIRECTION_LTR, !=, 0); g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_LTR)); g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_RTL)); g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_TTB)); g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_BTT)); + g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_INVALID)); g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_LTR)); g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_RTL)); g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_TTB)); g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_BTT)); + g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_INVALID)); g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_LTR)); g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_TTB)); g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_RTL)); g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_BTT)); + g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_INVALID)); g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_LTR)); g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_TTB)); g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_RTL)); g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_BTT)); + g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_INVALID)); + + g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_LTR)); + g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_TTB)); + g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_RTL)); + g_assert (HB_DIRECTION_IS_VALID (HB_DIRECTION_BTT)); + g_assert (!HB_DIRECTION_IS_VALID (HB_DIRECTION_INVALID)); + g_assert (!HB_DIRECTION_IS_VALID ((hb_direction_t) 0x12345678)); g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_LTR), ==, HB_DIRECTION_RTL); g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_RTL), ==, HB_DIRECTION_LTR); g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_TTB), ==, HB_DIRECTION_BTT); g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_BTT), ==, HB_DIRECTION_TTB); - - g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (NULL)); - g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("")); - g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("x")); - g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("r")); - g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("rtl")); - g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("RtL")); - g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("right-to-left")); - g_assert_cmpint (HB_DIRECTION_TTB, ==, hb_direction_from_string ("ttb")); + //g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_INVALID), ==, HB_DIRECTION_INVALID); + + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (NULL, -1)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("", -1)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("t", 0)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("x", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("r", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("rtl", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("RtL", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("right-to-left", -1)); + g_assert_cmpint (HB_DIRECTION_TTB, ==, hb_direction_from_string ("ttb", -1)); g_assert (0 == strcmp ("ltr", hb_direction_to_string (HB_DIRECTION_LTR))); g_assert (0 == strcmp ("rtl", hb_direction_to_string (HB_DIRECTION_RTL))); @@ -102,14 +115,20 @@ test_types_tag (void) g_assert_cmphex (HB_TAG ('a','B','c','D'), ==, 0x61426344); - g_assert_cmphex (hb_tag_from_string ("aBcDe"), ==, 0x61426344); - g_assert_cmphex (hb_tag_from_string ("aBcD"), ==, 0x61426344); - g_assert_cmphex (hb_tag_from_string ("aBc"), ==, 0x61426320); - g_assert_cmphex (hb_tag_from_string ("aB"), ==, 0x61422020); - g_assert_cmphex (hb_tag_from_string ("a"), ==, 0x61202020); - - g_assert_cmphex (hb_tag_from_string (""), ==, HB_TAG_NONE); - g_assert_cmphex (hb_tag_from_string (NULL), ==, HB_TAG_NONE); + g_assert_cmphex (hb_tag_from_string ("aBcDe", -1), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBcD", -1), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBc", -1), ==, 0x61426320); + g_assert_cmphex (hb_tag_from_string ("aB", -1), ==, 0x61422020); + g_assert_cmphex (hb_tag_from_string ("a", -1), ==, 0x61202020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 1), ==, 0x61202020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 2), ==, 0x61422020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 3), ==, 0x61426320); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 4), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 4), ==, 0x61426344); + + g_assert_cmphex (hb_tag_from_string ("", -1), ==, HB_TAG_NONE); + g_assert_cmphex (hb_tag_from_string ("x", 0), ==, HB_TAG_NONE); + g_assert_cmphex (hb_tag_from_string (NULL, -1), ==, HB_TAG_NONE); } static void @@ -127,23 +146,26 @@ test_types_script (void) g_assert_cmpint (HB_SCRIPT_INVALID, ==, (hb_script_t) HB_TAG_NONE); g_assert_cmphex (HB_SCRIPT_ARABIC, !=, HB_SCRIPT_LATIN); - g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (NULL)); - g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("")); - g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x")); + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (NULL, -1)); + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("", -1)); + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("x", 0)); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x", -1)); - g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("arab")); - g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arab")); - g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("ARAB")); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("arab", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arab", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("ARAB", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arabic", 6)); + g_assert_cmphex (HB_SCRIPT_ARABIC, !=, hb_script_from_string ("Arabic", 3)); g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (arab)); g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (Arab)); g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (ARAB)); /* Arbitrary tags that look like may be valid ISO 15924 should be preserved. */ - g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_string ("wWyZ")); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_string ("wWyZ", -1)); g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_iso15924_tag (wWyZ)); /* Otherwise, UNKNOWN should be returned. */ - g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x123")); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x123", -1)); g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_iso15924_tag (x123)); g_assert_cmphex (hb_script_to_iso15924_tag (HB_SCRIPT_ARABIC), ==, Arab); @@ -157,10 +179,10 @@ test_types_script (void) static void test_types_language (void) { - hb_language_t fa = hb_language_from_string ("fa"); - hb_language_t fa_IR = hb_language_from_string ("fa_IR"); - hb_language_t fa_ir = hb_language_from_string ("fa-ir"); - hb_language_t en = hb_language_from_string ("en"); + hb_language_t fa = hb_language_from_string ("fa", -1); + hb_language_t fa_IR = hb_language_from_string ("fa_IR", -1); + hb_language_t fa_ir = hb_language_from_string ("fa-ir", -1); + hb_language_t en = hb_language_from_string ("en", -1); g_assert (HB_LANGUAGE_INVALID == NULL); @@ -172,11 +194,14 @@ test_types_language (void) g_assert (en != fa); /* Test recall */ - g_assert (en == hb_language_from_string ("en")); - g_assert (en == hb_language_from_string ("eN")); - - g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (NULL)); - g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("")); + g_assert (en == hb_language_from_string ("en", -1)); + g_assert (en == hb_language_from_string ("eN", -1)); + g_assert (en == hb_language_from_string ("Enx", 2)); + + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (NULL, -1)); + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("", -1)); + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("en", 0)); + g_assert (HB_LANGUAGE_INVALID != hb_language_from_string ("en", 1)); g_assert (NULL == hb_language_to_string (HB_LANGUAGE_INVALID)); /* Not sure how to test this better. Setting env vars diff --git a/test/test-cplusplus.cc b/test/api/test-cplusplus.cc similarity index 100% rename from test/test-cplusplus.cc rename to test/api/test-cplusplus.cc diff --git a/test/test-font.c b/test/api/test-font.c similarity index 100% rename from test/test-font.c rename to test/api/test-font.c diff --git a/test/test-object.c b/test/api/test-object.c similarity index 93% rename from test/test-object.c rename to test/api/test-object.c index d0145c5..66e8d33 100644 --- a/test/test-object.c +++ b/test/api/test-object.c @@ -29,11 +29,6 @@ /* Unit tests for hb-object-private.h */ -#ifdef HAVE_FREETYPE -#include -#endif - - static void * create_blob (void) { @@ -49,12 +44,12 @@ create_blob_inert (void) static void * create_buffer (void) { - return hb_buffer_create (0); + return hb_buffer_create (); } static void * create_buffer_inert (void) { - return hb_buffer_create (-1); + return NULL; } static void * @@ -93,11 +88,7 @@ create_font_funcs (void) static void * create_font_funcs_inert (void) { -#ifdef HAVE_FREETYPE - return hb_ft_get_font_funcs (); -#else return NULL; -#endif } static void * @@ -116,7 +107,7 @@ create_unicode_funcs_inert (void) typedef void *(*create_func_t) (void); typedef void *(*reference_func_t) (void *obj); typedef void (*destroy_func_t) (void *obj); -typedef hb_bool_t (*set_user_data_func_t) (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy); +typedef hb_bool_t (*set_user_data_func_t) (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy, hb_bool_t replace); typedef void * (*get_user_data_func_t) (void *obj, hb_user_data_key_t *key); typedef void (*make_immutable_func_t) (void *obj); typedef hb_bool_t (*is_immutable_func_t) (void *obj); @@ -247,7 +238,7 @@ test_object (void) if (o->is_immutable) g_assert (!o->is_immutable (obj)); - g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0)); + g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); g_assert (o->get_user_data (obj, &key[0]) == &data[0]); if (o->is_immutable) { @@ -256,38 +247,39 @@ test_object (void) } /* Should still work even if object is made immutable */ - g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1)); + g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1, TRUE)); g_assert (o->get_user_data (obj, &key[1]) == &data[1]); - g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0)); + g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE)); g_assert (o->get_user_data (obj, &key[0]) == &data[0]); - g_assert (o->set_user_data (obj, &key[0], &data[1], NULL)); + g_assert (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE)); g_assert (data[0].freed); g_assert (o->get_user_data (obj, &key[0]) == &data[1]); g_assert (!data[1].freed); data[0].freed = FALSE; - g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0)); + g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); g_assert (!data[0].freed); - g_assert (o->set_user_data (obj, &key[0], NULL, NULL)); + g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); g_assert (data[0].freed); data[0].freed = FALSE; global_data = 0; - g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0)); + g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); + g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE)); g_assert_cmpuint (global_data, ==, 0); - g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up)); + g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE)); g_assert_cmpuint (global_data, ==, 0); - g_assert (o->set_user_data (obj, &key[0], NULL, NULL)); + g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); g_assert_cmpuint (global_data, ==, 1); global_data = 0; for (j = 2; j < 1000; j++) - g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up)); + g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE)); for (j = 2; j < 1000; j++) g_assert (o->get_user_data (obj, &key[j]) == &data[j]); for (j = 100; j < 1000; j++) - g_assert (o->set_user_data (obj, &key[j], NULL, NULL)); + g_assert (o->set_user_data (obj, &key[j], NULL, NULL, TRUE)); for (j = 2; j < 100; j++) g_assert (o->get_user_data (obj, &key[j]) == &data[j]); for (j = 100; j < 1000; j++) @@ -298,8 +290,8 @@ test_object (void) * Make sure it doesn't deadlock or corrupt memory. */ deadlock_test.klass = o; deadlock_test.object = obj; - g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test)); - g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL)); + g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test, TRUE)); + g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL, TRUE)); g_assert (!data[1].freed); o->destroy (obj); @@ -321,7 +313,7 @@ test_object (void) if (o->is_immutable) g_assert (o->is_immutable (obj)); - g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0)); + g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); g_assert (!o->get_user_data (obj, &key[0])); o->destroy (obj); @@ -349,7 +341,7 @@ test_object (void) if (o->is_immutable) g_assert (o->is_immutable (obj)); - g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0)); + g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); g_assert (!o->get_user_data (obj, &key[0])); o->destroy (obj); diff --git a/test/test-ot-tag.c b/test/api/test-ot-tag.c similarity index 89% rename from test/test-ot-tag.c rename to test/api/test-ot-tag.c index d440dbd..81b6678 100644 --- a/test/test-ot-tag.c +++ b/test/api/test-ot-tag.c @@ -40,7 +40,7 @@ test_simple_tags (const char *s, hb_script_t script) hb_script_t t1, t2; g_test_message ("Testing script %c%c%c%c: tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s); - tag = hb_tag_from_string (s); + tag = hb_tag_from_string (s, -1); hb_ot_tags_from_script (script, &t1, &t2); @@ -57,8 +57,8 @@ test_indic_tags (const char *s1, const char *s2, hb_script_t script) hb_script_t t1, t2; g_test_message ("Testing script %c%c%c%c: new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2); - tag1 = hb_tag_from_string (s1); - tag2 = hb_tag_from_string (s2); + tag1 = hb_tag_from_string (s1, -1); + tag2 = hb_tag_from_string (s2, -1); hb_ot_tags_from_script (script, &t1, &t2); @@ -85,14 +85,14 @@ test_ot_tag_script_degenerate (void) test_simple_tags ("DFLT", HB_SCRIPT_INVALID); /* Spaces are replaced */ - g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be ")), ==, hb_script_from_string ("Beee")); + g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be ")), ==, hb_script_from_string ("Beee", -1)); } static void test_ot_tag_script_simple (void) { /* Arbitrary non-existent script */ - test_simple_tags ("wwyz", hb_script_from_string ("wWyZ")); + test_simple_tags ("wwyz", hb_script_from_string ("wWyZ", -1)); /* These we don't really care about */ test_simple_tags ("zyyy", HB_SCRIPT_COMMON); @@ -141,8 +141,8 @@ test_ot_tag_script_indic (void) static void test_language_two_way (const char *tag_s, const char *lang_s) { - hb_language_t lang = hb_language_from_string (lang_s); - hb_tag_t tag = hb_tag_from_string (tag_s); + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s); @@ -153,8 +153,8 @@ test_language_two_way (const char *tag_s, const char *lang_s) static void test_tag_from_language (const char *tag_s, const char *lang_s) { - hb_language_t lang = hb_language_from_string (lang_s); - hb_tag_t tag = hb_tag_from_string (tag_s); + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); g_test_message ("Testing language %s -> tag %s", lang_s, tag_s); @@ -164,8 +164,8 @@ test_tag_from_language (const char *tag_s, const char *lang_s) static void test_tag_to_language (const char *tag_s, const char *lang_s) { - hb_language_t lang = hb_language_from_string (lang_s); - hb_tag_t tag = hb_tag_from_string (tag_s); + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); g_test_message ("Testing tag %s -> language %s", tag_s, lang_s); @@ -213,12 +213,17 @@ test_ot_tag_language (void) test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-zxc"); + test_tag_from_language ("dflt", "xy"); + test_tag_from_language ("XYZ", "xyz"); /* Unknown ISO 639-3 */ + test_tag_from_language ("XYZ", "xyz-qw"); /* Unknown ISO 639-3 */ + /* Test that x-hbot overrides the base language */ test_tag_from_language ("ABC", "fa-x-hbotabc-zxc"); test_tag_from_language ("ABC", "fa-ir-x-hbotabc-zxc"); test_tag_from_language ("ABC", "zh-x-hbotabc-zxc"); test_tag_from_language ("ABC", "zh-cn-x-hbotabc-zxc"); test_tag_from_language ("ABC", "zh-xy-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "xyz-xy-x-hbotabc-zxc"); } int diff --git a/test/test-shape-complex.c b/test/api/test-shape-complex.c similarity index 100% rename from test/test-shape-complex.c rename to test/api/test-shape-complex.c diff --git a/test/test-shape.c b/test/api/test-shape.c similarity index 92% rename from test/test-shape.c rename to test/api/test-shape.c index 5a41f0c..ccf6eed 100644 --- a/test/test-shape.c +++ b/test/api/test-shape.c @@ -106,7 +106,7 @@ test_shape (void) hb_font_set_funcs (font, ffuncs, NULL, NULL); hb_font_funcs_destroy (ffuncs); - buffer = hb_buffer_create (0); + buffer = hb_buffer_create (); hb_buffer_set_direction (buffer, HB_DIRECTION_LTR); hb_buffer_add_utf8 (buffer, TesT, 4, 0, 4); @@ -138,6 +138,18 @@ test_shape (void) hb_font_destroy (font); } +static void +test_shape_list (void) +{ + const char **shapers = hb_shape_list_shapers (); + + unsigned int i; + for (i = 0; shapers[i]; i++) + ; + + g_assert_cmpint (i, >, 1); + g_assert (!strcmp (shapers[i - 1], "fallback")); +} int main (int argc, char **argv) @@ -145,6 +157,9 @@ main (int argc, char **argv) hb_test_init (&argc, &argv); hb_test_add (test_shape); + /* TODO test fallback shaper */ + /* TODO test shaper_full */ + hb_test_add (test_shape_list); return hb_test_run(); } diff --git a/test/test-unicode.c b/test/api/test-unicode.c similarity index 99% rename from test/test-unicode.c rename to test/api/test-unicode.c index 9f526d7..a420bf3 100644 --- a/test/test-unicode.c +++ b/test/api/test-unicode.c @@ -142,13 +142,15 @@ static const test_pair_t combining_class_tests[] = { 0x302C, 232 }, { 0x0362, 233 }, { 0x0360, 234 }, - { 0x1DCD, 234 }, { 0x0345, 240 }, { 0x111111, 0 } }; static const test_pair_t combining_class_tests_more[] = { + /* Unicode-5.1 character additions */ + { 0x1DCD, 234 }, + /* Unicode-5.2 character additions */ { 0xA8E0, 230 }, @@ -779,7 +781,6 @@ test_unicode_script_roundtrip (gconstpointer user_data) } -/* TODO test compose() and decompose() */ static void test_unicode_normalization (gconstpointer user_data) { @@ -824,6 +825,7 @@ test_unicode_normalization (gconstpointer user_data) /* Not decomposable */ g_assert (!hb_unicode_decompose (uf, 0x0041, &a, &b) && a == 0x0041 && b == 0); g_assert (!hb_unicode_decompose (uf, 0xFB01, &a, &b) && a == 0xFB01 && b == 0); + g_assert (!hb_unicode_decompose (uf, 0x1F1EF, &a, &b) && a == 0x1F1EF && b == 0); /* Singletons */ g_assert (hb_unicode_decompose (uf, 0x212B, &a, &b) && a == 0x00C5 && b == 0); diff --git a/test/test-version.c b/test/api/test-version.c similarity index 100% rename from test/test-version.c rename to test/api/test-version.c diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am new file mode 100644 index 0000000..4fb762c --- /dev/null +++ b/test/shaping/Makefile.am @@ -0,0 +1,35 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +manifests: + @$(srcdir)/hb-manifest-update "$(srcdir)/texts" "$(srcdir)/fonts" + +EXTRA_DIST += \ + hb-diff \ + hb-diff-colorize \ + hb-diff-filter-failures \ + hb-diff-ngrams \ + hb-diff-stat \ + hb-manifest-read \ + hb-manifest-update \ + hb-unicode-decode \ + hb-unicode-encode \ + hb-unicode-prettyname \ + $(NULL) + +# TODO Figure out Python stuff +EXTRA_DIST += \ + hb_test_tools.py \ + $(NULL) +CLEANFILES += \ + hb_test_tools.py[co] \ + $(NULL) + +.PHONY: manifests + +-include $(top_srcdir)/git.mk diff --git a/test/shaping/Makefile.in b/test/shaping/Makefile.in new file mode 100644 index 0000000..c1969c4 --- /dev/null +++ b/test/shaping/Makefile.in @@ -0,0 +1,406 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to produce Makefile.in +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = test/shaping +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_FT_CFLAGS = @CAIRO_FT_CFLAGS@ +CAIRO_FT_LIBS = @CAIRO_FT_LIBS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ +FREETYPE_LIBS = @FREETYPE_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_CFLAGS = @GOBJECT_CFLAGS@ +GOBJECT_LIBS = @GOBJECT_LIBS@ +GRAPHITE2_CFLAGS = @GRAPHITE2_CFLAGS@ +GRAPHITE2_LIBS = @GRAPHITE2_LIBS@ +GREP = @GREP@ +GTHREAD_CFLAGS = @GTHREAD_CFLAGS@ +GTHREAD_LIBS = @GTHREAD_LIBS@ +HB_LIBTOOL_VERSION_INFO = @HB_LIBTOOL_VERSION_INFO@ +HB_VERSION = @HB_VERSION@ +HB_VERSION_MAJOR = @HB_VERSION_MAJOR@ +HB_VERSION_MICRO = @HB_VERSION_MICRO@ +HB_VERSION_MINOR = @HB_VERSION_MINOR@ +ICU_CFLAGS = @ICU_CFLAGS@ +ICU_LIBS = @ICU_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +UNISCRIBE_CFLAGS = @UNISCRIBE_CFLAGS@ +UNISCRIBE_LIBS = @UNISCRIBE_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +NULL = + +# TODO Figure out Python stuff +EXTRA_DIST = hb-diff hb-diff-colorize hb-diff-filter-failures \ + hb-diff-ngrams hb-diff-stat hb-manifest-read \ + hb-manifest-update hb-unicode-decode hb-unicode-encode \ + hb-unicode-prettyname $(NULL) hb_test_tools.py $(NULL) +CLEANFILES = hb_test_tools.py[co] $(NULL) +DISTCLEANFILES = +MAINTAINERCLEANFILES = +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu test/shaping/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu test/shaping/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am + + +manifests: + @$(srcdir)/hb-manifest-update "$(srcdir)/texts" "$(srcdir)/fonts" + +.PHONY: manifests + +-include $(top_srcdir)/git.mk + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/test/shaping/hb-diff b/test/shaping/hb-diff new file mode 100755 index 0000000..6a13fa2 --- /dev/null +++ b/test/shaping/hb-diff @@ -0,0 +1,10 @@ +#!/usr/bin/python + +from hb_test_tools import * +import sys, os + +if len (sys.argv) < 2: + print "usage: %s FILES..." % sys.argv[0] + sys.exit (1) + +ZipDiffer.diff_files (FileHelpers.open_file_or_stdin (f) for f in sys.argv[1:]) diff --git a/test/shaping/hb-diff-colorize b/test/shaping/hb-diff-colorize new file mode 100755 index 0000000..4e045d2 --- /dev/null +++ b/test/shaping/hb-diff-colorize @@ -0,0 +1,7 @@ +#!/usr/bin/python + +from hb_test_tools import * + +formatter = ColorFormatter.Auto (sys.argv) +colorizer = DiffColorizer (formatter=formatter) +UtilMains.process_multiple_files (FilterHelpers.filter_printer_function_no_newline (colorizer.colorize_diff)) diff --git a/test/shaping/hb-diff-filter-failures b/test/shaping/hb-diff-filter-failures new file mode 100755 index 0000000..4fe218a --- /dev/null +++ b/test/shaping/hb-diff-filter-failures @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.process_multiple_files (FilterHelpers.filter_printer_function_no_newline (DiffFilters.filter_failures)) diff --git a/test/shaping/hb-diff-ngrams b/test/shaping/hb-diff-ngrams new file mode 100755 index 0000000..a496447 --- /dev/null +++ b/test/shaping/hb-diff-ngrams @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.process_multiple_files (DiffSinks.print_ngrams) diff --git a/test/shaping/hb-diff-stat b/test/shaping/hb-diff-stat new file mode 100755 index 0000000..81626e1 --- /dev/null +++ b/test/shaping/hb-diff-stat @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.process_multiple_files (DiffSinks.print_stat) diff --git a/test/shaping/hb-manifest-read b/test/shaping/hb-manifest-read new file mode 100755 index 0000000..f486bcc --- /dev/null +++ b/test/shaping/hb-manifest-read @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.process_multiple_args (FilterHelpers.filter_printer_function (Manifest.read), mnemonic="DIR") diff --git a/test/shaping/hb-manifest-update b/test/shaping/hb-manifest-update new file mode 100755 index 0000000..b963f22 --- /dev/null +++ b/test/shaping/hb-manifest-update @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.process_multiple_args (Manifest.update_recursive, mnemonic="DIR") diff --git a/test/shaping/hb-unicode-decode b/test/shaping/hb-unicode-decode new file mode 100755 index 0000000..5b00eae --- /dev/null +++ b/test/shaping/hb-unicode-decode @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.filter_multiple_strings_or_stdin (Unicode.decode, "UNICODE_STRING") diff --git a/test/shaping/hb-unicode-encode b/test/shaping/hb-unicode-encode new file mode 100755 index 0000000..11bf365 --- /dev/null +++ b/test/shaping/hb-unicode-encode @@ -0,0 +1,5 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.filter_multiple_strings_or_stdin (Unicode.encode, "UNICODE_STRING", '') diff --git a/test/shaping/hb-unicode-prettyname b/test/shaping/hb-unicode-prettyname new file mode 100755 index 0000000..ecc26cc --- /dev/null +++ b/test/shaping/hb-unicode-prettyname @@ -0,0 +1,6 @@ +#!/usr/bin/python + +from hb_test_tools import * + +UtilMains.filter_multiple_strings_or_stdin (Unicode.pretty_names, "UNICODE_CODEPOINTS", \ + concat_separator = ' ') diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py new file mode 100644 index 0000000..47fa6eb --- /dev/null +++ b/test/shaping/hb_test_tools.py @@ -0,0 +1,511 @@ +#!/usr/bin/python + +import sys, os, re, difflib, unicodedata, errno, cgi +from itertools import * + +diff_symbols = "-+=*&^%$#@!~/" +diff_colors = ['red', 'green', 'blue'] + +class ColorFormatter: + + class Null: + @staticmethod + def start_color (c): return '' + @staticmethod + def end_color (): return '' + @staticmethod + def escape (s): return s + @staticmethod + def newline (): return '\n' + + class ANSI: + @staticmethod + def start_color (c): + return { + 'red': '\033[41;37;1m', + 'green': '\033[42;37;1m', + 'blue': '\033[44;37;1m', + }[c] + @staticmethod + def end_color (): + return '\033[m' + @staticmethod + def escape (s): return s + @staticmethod + def newline (): return '\n' + + class HTML: + @staticmethod + def start_color (c): + return '' % c + @staticmethod + def end_color (): + return '' + @staticmethod + def escape (s): return cgi.escape (s) + @staticmethod + def newline (): return '
\n' + + @staticmethod + def Auto (argv = [], out = sys.stdout): + format = ColorFormatter.ANSI + if "--format" in argv: + argv.remove ("--format") + format = ColorFormatter.ANSI + if "--format=ansi" in argv: + argv.remove ("--format=ansi") + format = ColorFormatter.ANSI + if "--format=html" in argv: + argv.remove ("--format=html") + format = ColorFormatter.HTML + if "--no-format" in argv: + argv.remove ("--no-format") + format = ColorFormatter.Null + return format + + +class DiffColorizer: + + diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)') + + def __init__ (self, formatter, colors=diff_colors, symbols=diff_symbols): + self.formatter = formatter + self.colors = colors + self.symbols = symbols + + def colorize_lines (self, lines): + lines = (l if l else '' for l in lines) + ss = [self.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in lines] + oo = ["",""] + st = [False, False] + for l in difflib.Differ().compare (*ss): + if l[0] == '?': + continue + if l[0] == ' ': + for i in range(2): + if st[i]: + oo[i] += self.formatter.end_color () + st[i] = False + oo = [o + self.formatter.escape (l[2:]) for o in oo] + continue + if l[0] in self.symbols: + i = self.symbols.index (l[0]) + if not st[i]: + oo[i] += self.formatter.start_color (self.colors[i]) + st[i] = True + oo[i] += self.formatter.escape (l[2:]) + continue + for i in range(2): + if st[i]: + oo[i] += self.formatter.end_color () + st[i] = False + oo = [o.replace ('\n', '') for o in oo] + return [s1+s2+self.formatter.newline () for (s1,s2) in zip (self.symbols, oo) if s2] + + def colorize_diff (self, f): + lines = [None, None] + for l in f: + if l[0] not in self.symbols: + yield self.formatter.escape (l).replace ('\n', self.formatter.newline ()) + continue + i = self.symbols.index (l[0]) + if lines[i]: + # Flush + for line in self.colorize_lines (lines): + yield line + lines = [None, None] + lines[i] = l[1:] + if (all (lines)): + # Flush + for line in self.colorize_lines (lines): + yield line + lines = [None, None] + if (any (lines)): + # Flush + for line in self.colorize_lines (lines): + yield line + + +class ZipDiffer: + + @staticmethod + def diff_files (files, symbols=diff_symbols): + files = tuple (files) # in case it's a generator, copy it + try: + for lines in izip_longest (*files): + if all (lines[0] == line for line in lines[1:]): + sys.stdout.writelines ([" ", lines[0]]) + continue + + for i, l in enumerate (lines): + if l: + sys.stdout.writelines ([symbols[i], l]) + except IOError as e: + if e.errno != errno.EPIPE: + print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror) + sys.exit (1) + + +class DiffFilters: + + @staticmethod + def filter_failures (f): + for key, lines in DiffHelpers.separate_test_cases (f): + lines = list (lines) + if not DiffHelpers.test_passed (lines): + for l in lines: yield l + +class Stat: + + def __init__ (self): + self.count = 0 + self.freq = 0 + + def add (self, test): + self.count += 1 + self.freq += test.freq + +class Stats: + + def __init__ (self): + self.passed = Stat () + self.failed = Stat () + self.total = Stat () + + def add (self, test): + self.total.add (test) + if test.passed: + self.passed.add (test) + else: + self.failed.add (test) + + def mean (self): + return float (self.passed.count) / self.total.count + + def variance (self): + return (float (self.passed.count) / self.total.count) * \ + (float (self.failed.count) / self.total.count) + + def stddev (self): + return self.variance () ** .5 + + def zscore (self, population): + """Calculate the standard score. + Population is the Stats for population. + Self is Stats for sample. + Returns larger absolute value if sample is highly unlikely to be random. + Anything outside of -3..+3 is very unlikely to be random. + See: http://en.wikipedia.org/wiki/Standard_score""" + + return (self.mean () - population.mean ()) / population.stddev () + + + + +class DiffSinks: + + @staticmethod + def print_stat (f): + passed = 0 + failed = 0 + # XXX port to Stats, but that would really slow us down here + for key, lines in DiffHelpers.separate_test_cases (f): + if DiffHelpers.test_passed (lines): + passed += 1 + else: + failed += 1 + total = passed + failed + print "%d out of %d tests passed. %d failed (%g%%)" % (passed, total, failed, 100. * failed / total) + + @staticmethod + def print_ngrams (f, ns=(1,2,3)): + gens = tuple (Ngram.generator (n) for n in ns) + allstats = Stats () + allgrams = {} + for key, lines in DiffHelpers.separate_test_cases (f): + test = Test (lines) + allstats.add (test) + + for gen in gens: + for ngram in gen (test.unicodes): + if ngram not in allgrams: + allgrams[ngram] = Stats () + allgrams[ngram].add (test) + + importantgrams = {} + for ngram, stats in allgrams.iteritems (): + if stats.failed.count >= 30: # for statistical reasons + importantgrams[ngram] = stats + allgrams = importantgrams + del importantgrams + + for ngram, stats in allgrams.iteritems (): + print "zscore: %9f failed: %6d passed: %6d ngram: <%s>" % (stats.zscore (allstats), stats.failed.count, stats.passed.count, ','.join ("U+%04X" % u for u in ngram)) + + + +class Test: + + def __init__ (self, lines): + self.freq = 1 + self.passed = True + self.identifier = None + self.text = None + self.unicodes = None + self.glyphs = None + for l in lines: + symbol = l[0] + if symbol != ' ': + self.passed = False + i = 1 + if ':' in l: + i = l.index (':') + if not self.identifier: + self.identifier = l[1:i] + i = i + 2 # Skip colon and space + j = -1 + if l[j] == '\n': + j -= 1 + brackets = l[i] + l[j] + l = l[i+1:-2] + if brackets == '()': + self.text = l + elif brackets == '<>': + self.unicodes = Unicode.parse (l) + elif brackets == '[]': + # XXX we don't handle failed tests here + self.glyphs = l + + +class DiffHelpers: + + @staticmethod + def separate_test_cases (f): + '''Reads lines from f, and if the lines have identifiers, ie. + have a colon character, groups them by identifier, + yielding lists of all lines with the same identifier.''' + + def identifier (l): + if ':' in l[1:]: + return l[1:l.index (':')] + return l + return groupby (f, key=identifier) + + @staticmethod + def test_passed (lines): + return all (l[0] == ' ' for l in lines) + + +class FilterHelpers: + + @staticmethod + def filter_printer_function (filter_callback): + def printer (f): + for line in filter_callback (f): + print line + return printer + + @staticmethod + def filter_printer_function_no_newline (filter_callback): + def printer (f): + for line in filter_callback (f): + sys.stdout.writelines ([line]) + return printer + + +class Ngram: + + @staticmethod + def generator (n): + + def gen (f): + l = [] + for x in f: + l.append (x) + if len (l) == n: + yield tuple (l) + l[:1] = [] + + gen.n = n + return gen + + +class UtilMains: + + @staticmethod + def process_multiple_files (callback, mnemonic = "FILE"): + + if "--help" in sys.argv: + print "Usage: %s %s..." % (sys.argv[0], mnemonic) + sys.exit (1) + + try: + files = sys.argv[1:] if len (sys.argv) > 1 else ['-'] + for s in files: + callback (FileHelpers.open_file_or_stdin (s)) + except IOError as e: + if e.errno != errno.EPIPE: + print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror) + sys.exit (1) + + @staticmethod + def process_multiple_args (callback, mnemonic): + + if len (sys.argv) == 1 or "--help" in sys.argv: + print "Usage: %s %s..." % (sys.argv[0], mnemonic) + sys.exit (1) + + try: + for s in sys.argv[1:]: + callback (s) + except IOError as e: + if e.errno != errno.EPIPE: + print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror) + sys.exit (1) + + @staticmethod + def filter_multiple_strings_or_stdin (callback, mnemonic, \ + separator = " ", \ + concat_separator = False): + + if "--help" in sys.argv: + print "Usage:\n %s %s...\nor:\n %s\n\nWhen called with no arguments, input is read from standard input." \ + % (sys.argv[0], mnemonic, sys.argv[0]) + sys.exit (1) + + try: + if len (sys.argv) == 1: + while (1): + line = sys.stdin.readline () + if not len (line): + break + if line[-1] == '\n': + line = line[:-1] + print callback (line) + else: + args = sys.argv[1:] + if concat_separator != False: + args = [concat_separator.join (args)] + print separator.join (callback (x) for x in (args)) + except IOError as e: + if e.errno != errno.EPIPE: + print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror) + sys.exit (1) + + +class Unicode: + + @staticmethod + def decode (s): + return '<' + u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8') + '>' + + @staticmethod + def parse (s): + s = re.sub (r"[<+>,\\uU\n ]", " ", s) + s = re.sub (r"0[xX]", " ", s) + return [int (x, 16) for x in s.split (' ') if len (x)] + + @staticmethod + def encode (s): + return u''.join (unichr (x) for x in Unicode.parse (s)).encode ('utf-8') + + shorthands = { + "ZERO WIDTH NON-JOINER": "ZWNJ", + "ZERO WIDTH JOINER": "ZWJ", + "NARROW NO-BREAK SPACE": "NNBSP", + "COMBINING GRAPHEME JOINER": "CGJ", + "LEFT-TO-RIGHT MARK": "LRM", + "RIGHT-TO-LEFT MARK": "RLM", + "LEFT-TO-RIGHT EMBEDDING": "LRE", + "RIGHT-TO-LEFT EMBEDDING": "RLE", + "POP DIRECTIONAL FORMATTING": "PDF", + "LEFT-TO-RIGHT OVERRIDE": "LRO", + "RIGHT-TO-LEFT OVERRIDE": "RLO", + } + + @staticmethod + def pretty_name (u): + try: + s = unicodedata.name (u) + except ValueError: + return "XXX" + s = re.sub (".* LETTER ", "", s) + s = re.sub (".* VOWEL SIGN (.*)", r"\1-MATRA", s) + s = re.sub (".* SIGN ", "", s) + s = re.sub (".* COMBINING ", "", s) + if re.match (".* VIRAMA", s): + s = "HALANT" + if s in Unicode.shorthands: + s = Unicode.shorthands[s] + return s + + @staticmethod + def pretty_names (s): + s = re.sub (r"[<+>\\uU]", " ", s) + s = re.sub (r"0[xX]", " ", s) + s = [unichr (int (x, 16)) for x in re.split ('[, \n]', s) if len (x)] + return u' + '.join (Unicode.pretty_name (x) for x in s).encode ('utf-8') + + +class FileHelpers: + + @staticmethod + def open_file_or_stdin (f): + if f == '-': + return sys.stdin + return file (f) + + +class Manifest: + + @staticmethod + def read (s, strict = True): + + if not os.path.exists (s): + if strict: + print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], s) + sys.exit (1) + return + + s = os.path.normpath (s) + + if os.path.isdir (s): + + try: + m = file (os.path.join (s, "MANIFEST")) + items = [x.strip () for x in m.readlines ()] + for f in items: + for p in Manifest.read (os.path.join (s, f)): + yield p + except IOError: + if strict: + print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], os.path.join (s, "MANIFEST")) + sys.exit (1) + return + else: + yield s + + @staticmethod + def update_recursive (s): + + for dirpath, dirnames, filenames in os.walk (s, followlinks=True): + + for f in ["MANIFEST", "README", "LICENSE", "COPYING", "AUTHORS", "SOURCES", "ChangeLog"]: + if f in dirnames: + dirnames.remove (f) + if f in filenames: + filenames.remove (f) + dirnames.sort () + filenames.sort () + ms = os.path.join (dirpath, "MANIFEST") + print " GEN %s" % ms + m = open (ms, "w") + for f in filenames: + print >> m, f + for f in dirnames: + print >> m, f + for f in dirnames: + Manifest.update_recursive (os.path.join (dirpath, f)) + +if __name__ == '__main__': + pass diff --git a/util/Makefile.am b/util/Makefile.am new file mode 100644 index 0000000..944b1aa --- /dev/null +++ b/util/Makefile.am @@ -0,0 +1,74 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +bin_PROGRAMS = + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/ \ + -I$(top_builddir)/src/ \ + $(GLIB_CFLAGS) \ + $(FREETYPE_CFLAGS) \ + $(CAIRO_FT_CFLAGS) \ + $(NULL) +LDADD = \ + $(top_builddir)/src/libharfbuzz.la \ + -lm \ + $(GLIB_LIBS) \ + $(FREETYPE_LIBS) \ + $(NULL) + +if HAVE_GLIB +if HAVE_FREETYPE + +if HAVE_CAIRO_FT +hb_view_SOURCES = \ + hb-view.cc \ + options.cc \ + options.hh \ + main-font-text.hh \ + shape-consumer.hh \ + ansi-print.cc \ + ansi-print.hh \ + helper-cairo.cc \ + helper-cairo.hh \ + helper-cairo-ansi.cc \ + helper-cairo-ansi.hh \ + view-cairo.cc \ + view-cairo.hh \ + $(NULL) +hb_view_LDADD = \ + $(LDADD) \ + $(CAIRO_LIBS) \ + $(CAIRO_FT_LIBS) \ + $(NULL) +bin_PROGRAMS += hb-view +endif # HAVE_CAIRO_FT + +hb_shape_SOURCES = \ + hb-shape.cc \ + options.cc \ + options.hh \ + main-font-text.hh \ + shape-consumer.hh \ + $(NULL) +bin_PROGRAMS += hb-shape + +if HAVE_OT +hb_ot_shape_closure_SOURCES = \ + hb-ot-shape-closure.cc \ + options.cc \ + options.hh \ + main-font-text.hh \ + $(NULL) +bin_PROGRAMS += hb-ot-shape-closure +endif # HAVE_OT + +endif # HAVE_FREETYPE +endif # HAVE_GLIB + +-include $(top_srcdir)/git.mk diff --git a/util/ansi-print.cc b/util/ansi-print.cc new file mode 100644 index 0000000..873bee8 --- /dev/null +++ b/util/ansi-print.cc @@ -0,0 +1,413 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ansi-print.hh" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include /* for isatty() */ +#endif + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define CELL_W 8 +#define CELL_H (2 * CELL_W) + +struct color_diff_t +{ + int dot (const color_diff_t &o) + { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; } + + int v[4]; +}; + +struct color_t +{ + static color_t from_ansi (unsigned int x) + { + color_t c = {(0xFF<<24) | ((0xFF*(x&1))<<16) | ((0xFF*((x >> 1)&1))<<8) | (0xFF*((x >> 2)&1))}; + return c; + } + unsigned int to_ansi (void) + { + return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4); + } + + color_diff_t diff (const color_t &o) + { + color_diff_t d; + for (unsigned int i = 0; i < 4; i++) + d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF); + return d; + } + + uint32_t v; +}; + +struct image_t +{ + public: + + image_t (unsigned int width_, + unsigned int height_, + const uint32_t *data_, + unsigned int stride_) : + width (width_), + height (height_), + own_data (false), + data ((color_t *) data_), + stride (stride_) {} + image_t (unsigned int width_, + unsigned int height_) : + width (width_), + height (height_), + own_data (true), + data ((color_t *) malloc (sizeof (data[0]) * width * height)), + stride (width) {} + ~image_t (void) + { if (own_data) free (data); } + + color_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * stride]; } + + color_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * stride]; } + + void + copy_sub_image (const image_t &s, + unsigned int x, unsigned int y, + unsigned int w, unsigned int h) + { + assert (x < width); + assert (y < height); + for (unsigned int row = 0; row < h; row++) { + color_t *p = data + x + MIN (y + row, height - 1) * stride; + color_t *q = s.data + row * s.stride; + if (x + w <= width) + for (unsigned int col = 0; col < w; col++) + *q++ = *p++; + else { + unsigned int limit = width - x; + for (unsigned int col = 0; col < limit; col++) + *q++ = *p++; + p--; + for (unsigned int col = limit; col < w; col++) + *q++ = *p; + } + } + } + + const unsigned int width; + const unsigned int height; + + private: + bool own_data; + color_t * const data; + const unsigned int stride; +}; + +struct biimage_t +{ + public: + + biimage_t (unsigned int width, unsigned int height) : + width (width), + height (height), + data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {} + ~biimage_t (void) + { free (data); } + + void set (const image_t &image) + { + assert (image.width == width); + assert (image.height == height); + int freq[8] = {0}; + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + color_t c = image (x, y); + freq[c.to_ansi ()]++; + } + bg = 0; + for (unsigned int i = 1; i < 8; i++) + if (freq[bg] < freq[i]) + bg = i; + fg = 0; + for (unsigned int i = 1; i < 8; i++) + if (i != bg && freq[fg] < freq[i]) + fg = i; + if (fg == bg || freq[fg] == 0) { + fg = bg; + unicolor = true; + } + else + unicolor = false; + + /* Set the data... */ + + if (unicolor) { + memset (data, 0, sizeof (data[0]) * width * height); + return; + } + + color_t bgc = color_t::from_ansi (bg); + color_t fgc = color_t::from_ansi (fg); + color_diff_t diff = fgc.diff (bgc); + int dd = diff.dot (diff); + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + int d = diff.dot (image (x, y).diff (bgc)); + (*this)(x, y) = d < 0 ? 0 : d > dd ? 255 : lround (d * 255. / dd); + } + } + + uint8_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * width]; } + + uint8_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * width]; } + + const unsigned int width; + const unsigned int height; + unsigned int bg; + unsigned int fg; + bool unicolor; + + private: + uint8_t * const data; +}; + +const char * +block_best (const biimage_t &bi, unsigned int *score, bool *inverse) +{ + assert (bi.width <= CELL_W); + assert (bi.height <= CELL_H); + + unsigned int row_sum[CELL_H] = {0}; + unsigned int col_sum[CELL_W] = {0}; + unsigned int row_sum_i[CELL_H] = {0}; + unsigned int col_sum_i[CELL_W] = {0}; + unsigned int quad[2][2] = {{0}}; + unsigned int quad_i[2][2] = {{0}}; + unsigned int total = 0; + unsigned int total_i = 0; + for (unsigned int y = 0; y < bi.height; y++) + for (unsigned int x = 0; x < bi.width; x++) { + unsigned int c = bi (x, y); + unsigned int c_i = 255 - c; + row_sum[y] += c; + row_sum_i[y] += c_i; + col_sum[x] += c; + col_sum_i[x] += c_i; + quad[2 * y / bi.height][2 * x / bi.width] += c; + quad_i[2 * y / bi.height][2 * x / bi.width] += c_i; + total += c; + total_i += c_i; + } + + /* Make the sums cummulative */ + for (unsigned int i = 1; i < bi.height; i++) { + row_sum[i] += row_sum[i - 1]; + row_sum_i[i] += row_sum_i[i - 1]; + } + for (unsigned int i = 1; i < bi.width; i++) { + col_sum[i] += col_sum[i - 1]; + col_sum_i[i] += col_sum_i[i - 1]; + } + + const char *best_c = " "; + + /* Maybe empty is better! */ + if (total < *score) { + *score = total; + *inverse = false; + best_c = " "; + } + /* Maybe full is better! */ + if (total_i < *score) { + *score = total_i; + *inverse = true; + best_c = " "; + } + + /* Find best lower line */ + if (1) { + unsigned int best_s = (unsigned int) -1; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.height - 1; i++) + { + unsigned int s; + s = row_sum[i] + total_i - row_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + s = row_sum_i[i] + total - row_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + } + if (best_s < *score) { + static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"}; + unsigned int which = lround (((best_i + 1) * 8) / bi.height); + if (1 <= which && which <= 7) { + *score = best_s; + *inverse = best_inv; + best_c = lower[7 - which]; + } + } + } + + /* Find best left line */ + if (1) { + unsigned int best_s = (unsigned int) -1; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.width - 1; i++) + { + unsigned int s; + s = col_sum[i] + total_i - col_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + s = col_sum_i[i] + total - col_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + } + if (best_s < *score) { + static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + unsigned int which = lround (((best_i + 1) * 8) / bi.width); + if (1 <= which && which <= 7) { + *score = best_s; + *inverse = best_inv; + best_c = left[which - 1]; + } + } + } + + /* Find best quadrant */ + if (1) { + unsigned int q = 0; + unsigned int qs = 0; + for (unsigned int i = 0; i < 2; i++) + for (unsigned int j = 0; j < 2; j++) + if (quad[i][j] > quad_i[i][j]) { + q += 1 << (2 * i + j); + qs += quad_i[i][j]; + } else + qs += quad[i][j]; + if (qs < *score) { + const char *c = NULL; + bool inv = false; + switch (q) { + case 1: c = "▟"; inv = true; break; + case 2: c = "▙"; inv = true; break; + case 4: c = "▖"; inv = false; break; + case 8: c = "▗"; inv = false; break; + case 9: c = "▚"; inv = false; break; + case 6: c = "▞"; inv = false; break; + case 7: c = "▜"; inv = true; break; + case 11: c = "▜"; inv = true; break; + case 13: c = "▙"; inv = true; break; + case 14: c = "▟"; inv = true; break; + } + if (c) { + *score = qs; + *inverse = inv; + best_c = c; + } + } + } + + return best_c; +} + +void +ansi_print_image_rgb24 (const uint32_t *data, + unsigned int width, + unsigned int height, + unsigned int stride) +{ + image_t image (width, height, data, stride); + + unsigned int rows = (height + CELL_H - 1) / CELL_H; + unsigned int cols = (width + CELL_W - 1) / CELL_W; + image_t cell (CELL_W, CELL_H); + biimage_t bi (CELL_W, CELL_H); + unsigned int last_bg = -1, last_fg = -1; + for (unsigned int row = 0; row < rows; row++) { + for (unsigned int col = 0; col < cols; col++) { + image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H); + bi.set (cell); + if (bi.unicolor) { + if (last_bg != bi.bg) { + printf ("\e[%dm", 40 + bi.bg); + last_bg = bi.bg; + } + printf (" "); + } else { + /* Figure out the closest character to the biimage */ + unsigned int score = (unsigned int) -1; + bool inverse = false; + const char *c = block_best (bi, &score, &inverse); + if (inverse) { + if (last_bg != bi.fg || last_fg != bi.bg) { + printf ("\e[%d;%dm", 30 + bi.bg, 40 + bi.fg); + last_bg = bi.fg; + last_fg = bi.bg; + } + } else { + if (last_bg != bi.bg || last_fg != bi.fg) { + printf ("\e[%d;%dm", 40 + bi.bg, 30 + bi.fg); + last_bg = bi.bg; + last_fg = bi.fg; + } + } + printf ("%s", c); + } + } + printf ("\e[0m\n"); /* Reset */ + last_bg = last_fg = -1; + } +} diff --git a/util/ansi-print.hh b/util/ansi-print.hh new file mode 100644 index 0000000..dad4d4c --- /dev/null +++ b/util/ansi-print.hh @@ -0,0 +1,39 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef ANSI_PRINT_HH +#define ANSI_PRINT_HH + +#include /* for int types */ + +void +ansi_print_image_rgb24 (const uint32_t *data, + unsigned int width, + unsigned int height, + unsigned int stride); + + +#endif diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc new file mode 100644 index 0000000..6dce7a1 --- /dev/null +++ b/util/hb-ot-shape-closure.cc @@ -0,0 +1,112 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "main-font-text.hh" + +#ifdef HAVE_FREETYPE +#include +#endif + +struct shape_closure_consumer_t : option_group_t +{ + shape_closure_consumer_t (option_parser_t *parser) : + shaper (parser), + show_glyph_names (true) + { + add_options (parser); + } + + void add_options (struct option_parser_t *parser) + { + GOptionEntry entries[] = + { + {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", NULL}, + {NULL} + }; + parser->add_group (entries, + "format", + "Format options:", + "Options controlling output formatting", + this); + } + + void init (const font_options_t *font_opts) + { + glyphs = hb_set_create (); + font = hb_font_reference (font_opts->get_font ()); + failed = false; + } + void consume_line (hb_buffer_t *buffer, + const char *text, + unsigned int text_len) + { + hb_set_clear (glyphs); + shaper.shape_closure (text, text_len, font, buffer, glyphs); + + if (hb_set_empty (glyphs)) + return; + + /* Print it out! */ + bool first = true; + for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);) + if (hb_set_has (glyphs, i)) { + if (first) + first = false; + else + printf (" "); + char glyph_name[32]; + if (show_glyph_names) { + hb_font_get_glyph_name (font, i, glyph_name, sizeof (glyph_name)); + printf ("%s", glyph_name); + } else + printf ("%u", i); + } + } + void finish (const font_options_t *font_opts) + { + printf ("\n"); + hb_font_destroy (font); + font = NULL; + hb_set_destroy (glyphs); + glyphs = NULL; + } + + bool failed; + + protected: + shape_options_t shaper; + hb_bool_t show_glyph_names; + + hb_set_t *glyphs; + hb_font_t *font; +}; + +int +main (int argc, char **argv) +{ + main_font_text_t driver; + return driver.main (argc, argv); +} diff --git a/util/hb-shape.cc b/util/hb-shape.cc new file mode 100644 index 0000000..b23519b --- /dev/null +++ b/util/hb-shape.cc @@ -0,0 +1,97 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "main-font-text.hh" +#include "shape-consumer.hh" + +struct output_buffer_t +{ + output_buffer_t (option_parser_t *parser) + : options (parser), + format (parser) {} + + void init (const font_options_t *font_opts) + { + options.get_file_handle (); + gs = g_string_new (NULL); + line_no = 0; + font = hb_font_reference (font_opts->get_font ()); + } + void new_line (void) + { + line_no++; + } + void consume_text (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + g_string_set_size (gs, 0); + format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, utf8_clusters, gs); + fprintf (options.fp, "%s", gs->str); + } + void shape_failed (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + g_string_set_size (gs, 0); + format.serialize_message (line_no, "msg: all shapers failed", gs); + fprintf (options.fp, "%s", gs->str); + } + void consume_glyphs (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + g_string_set_size (gs, 0); + format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, utf8_clusters, gs); + fprintf (options.fp, "%s", gs->str); + } + void finish (const font_options_t *font_opts) + { + hb_font_destroy (font); + g_string_free (gs, true); + gs = NULL; + font = NULL; + } + + protected: + output_options_t options; + format_options_t format; + + GString *gs; + unsigned int line_no; + hb_font_t *font; +}; + +int +main (int argc, char **argv) +{ + main_font_text_t > driver; + return driver.main (argc, argv); +} diff --git a/util/hb-view.cc b/util/hb-view.cc new file mode 100644 index 0000000..26fad66 --- /dev/null +++ b/util/hb-view.cc @@ -0,0 +1,37 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "main-font-text.hh" +#include "shape-consumer.hh" +#include "view-cairo.hh" + +int +main (int argc, char **argv) +{ + main_font_text_t > driver; + return driver.main (argc, argv); +} diff --git a/util/helper-cairo-ansi.cc b/util/helper-cairo-ansi.cc new file mode 100644 index 0000000..f7c0660 --- /dev/null +++ b/util/helper-cairo-ansi.cc @@ -0,0 +1,102 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "helper-cairo-ansi.hh" +#include "options.hh" + +#include "ansi-print.hh" + + +cairo_status_t +helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure) +{ + unsigned int width = cairo_image_surface_get_width (surface); + unsigned int height = cairo_image_surface_get_height (surface); + if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) { + cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + cairo_t *cr = cairo_create (new_surface); + if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) { + cairo_set_source_rgb (cr, 0., 0., 0.); + cairo_paint (cr); + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_mask_surface (cr, surface, 0, 0); + } else { + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + } + cairo_destroy (cr); + surface = new_surface; + } else + cairo_surface_reference (surface); + + unsigned int stride = cairo_image_surface_get_stride (surface); + const uint32_t *data = (uint32_t *) cairo_image_surface_get_data (surface); + + /* We don't have rows to spare on the terminal window... + * Find the tight image top/bottom and only print in between. */ + + /* Use corner color as background color. */ + uint32_t bg_color = * (uint32_t *) data; + + /* Drop first row while empty */ + while (height) + { + unsigned int i; + for (i = 0; i < width; i++) + if (data[i] != bg_color) + break; + if (i < width) + break; + data += stride / 4; + height--; + } + + /* Drop last row while empty */ + unsigned int orig_height = height; + while (height) + { + const uint32_t *row = data + (height - 1) * stride / 4; + unsigned int i; + for (i = 0; i < width; i++) + if (row[i] != bg_color) + break; + if (i < width) + break; + height--; + } + if (height < orig_height) + height++; /* Add one last blank row for padding. */ + + if (width && height) + ansi_print_image_rgb24 (data, width, height, stride / 4); + + cairo_surface_destroy (surface); + return CAIRO_STATUS_SUCCESS; +} diff --git a/util/helper-cairo-ansi.hh b/util/helper-cairo-ansi.hh new file mode 100644 index 0000000..eeaaa50 --- /dev/null +++ b/util/helper-cairo-ansi.hh @@ -0,0 +1,39 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include + +#ifndef HELPER_CAIRO_ANSI_HH +#define HELPER_CAIRO_ANSI_HH + + +cairo_status_t +helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure); + + +#endif diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc new file mode 100644 index 0000000..0cdfd63 --- /dev/null +++ b/util/helper-cairo.cc @@ -0,0 +1,453 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "helper-cairo.hh" + +#include +#include + +#include "helper-cairo-ansi.hh" +#ifdef CAIRO_HAS_SVG_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PDF_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PS_SURFACE +# include +# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) +# define HAS_EPS 1 + +static cairo_surface_t * +_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_surface_t *surface; + + surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); + cairo_ps_surface_set_eps (surface, true); + + return surface; +} + +# else +# undef HAS_EPS +# endif +#endif + +cairo_scaled_font_t * +helper_cairo_create_scaled_font (const font_options_t *font_opts, + double font_size) +{ + hb_font_t *font = hb_font_reference (font_opts->get_font ()); + + cairo_font_face_t *cairo_face; + FT_Face ft_face = hb_ft_font_get_face (font); + if (!ft_face) + /* This allows us to get some boxes at least... */ + cairo_face = cairo_toy_font_face_create ("@cairo:sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + else + cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); + cairo_matrix_t ctm, font_matrix; + cairo_font_options_t *font_options; + + cairo_matrix_init_identity (&ctm); + cairo_matrix_init_scale (&font_matrix, + font_size, font_size); + font_options = cairo_font_options_create (); + cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); + + cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, + &font_matrix, + &ctm, + font_options); + + cairo_font_options_destroy (font_options); + cairo_font_face_destroy (cairo_face); + + static cairo_user_data_key_t key; + if (cairo_scaled_font_set_user_data (scaled_font, + &key, + (void *) font, + (cairo_destroy_func_t) hb_font_destroy)) + hb_font_destroy (font); + + return scaled_font; +} + + +struct finalize_closure_t { + void (*callback)(finalize_closure_t *); + cairo_surface_t *surface; + cairo_write_func_t write_func; + void *closure; +}; +static cairo_user_data_key_t finalize_closure_key; + + +static void +finalize_ansi (finalize_closure_t *closure) +{ + cairo_status_t status; + status = helper_cairo_surface_write_to_ansi_stream (closure->surface, + closure->write_func, + closure->closure); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); +} + +static cairo_surface_t * +_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); + ansi_closure->callback = finalize_ansi; + ansi_closure->surface = surface; + ansi_closure->write_func = write_func; + ansi_closure->closure = closure; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) ansi_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + +static void +finalize_png (finalize_closure_t *closure) +{ + cairo_status_t status; + status = cairo_surface_write_to_png_stream (closure->surface, + closure->write_func, + closure->closure); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); +} + +static cairo_surface_t * +_cairo_png_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); + png_closure->callback = finalize_png; + png_closure->surface = surface; + png_closure->write_func = write_func; + png_closure->closure = closure; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) png_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + +#endif + +static cairo_status_t +stdio_write_func (void *closure, + const unsigned char *data, + unsigned int size) +{ + FILE *fp = (FILE *) closure; + + while (size) { + size_t ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) + fail (false, "Failed to write output: %s", strerror (errno)); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_t * +helper_cairo_create_context (double w, double h, + view_options_t *view_opts, + output_options_t *out_opts) +{ + cairo_surface_t *(*constructor) (cairo_write_func_t write_func, + void *closure, + double width, + double height) = NULL; + cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content) = NULL; + + const char *extension = out_opts->output_format; + if (!extension) { +#if HAVE_ISATTY + if (isatty (fileno (out_opts->get_file_handle ()))) + extension = "ansi"; + else +#endif + extension = "png"; + } + if (0) + ; + else if (0 == strcasecmp (extension, "ansi")) + constructor2 = _cairo_ansi_surface_create_for_stream; + #ifdef CAIRO_HAS_PNG_FUNCTIONS + else if (0 == strcasecmp (extension, "png")) + constructor2 = _cairo_png_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_SVG_SURFACE + else if (0 == strcasecmp (extension, "svg")) + constructor = cairo_svg_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PDF_SURFACE + else if (0 == strcasecmp (extension, "pdf")) + constructor = cairo_pdf_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PS_SURFACE + else if (0 == strcasecmp (extension, "ps")) + constructor = cairo_ps_surface_create_for_stream; + #ifdef HAS_EPS + else if (0 == strcasecmp (extension, "eps")) + constructor = _cairo_eps_surface_create_for_stream; + #endif + #endif + + + unsigned int fr, fg, fb, fa, br, bg, bb, ba; + br = bg = bb = 0; ba = 255; + sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); + fr = fg = fb = 0; fa = 255; + sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); + + cairo_content_t content; + if (!view_opts->annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb) + content = CAIRO_CONTENT_ALPHA; + else if (ba == 255) + content = CAIRO_CONTENT_COLOR; + else + content = CAIRO_CONTENT_COLOR_ALPHA; + + cairo_surface_t *surface; + FILE *f = out_opts->get_file_handle (); + if (constructor) + surface = constructor (stdio_write_func, f, w, h); + else if (constructor2) + surface = constructor2 (stdio_write_func, f, w, h, content); + else + fail (false, "Unknown output format `%s'", extension); + + cairo_t *cr = cairo_create (surface); + content = cairo_surface_get_content (surface); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); + cairo_paint (cr); + cairo_set_source_rgba (cr, 1., 1., 1., + (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); + break; + default: + case CAIRO_CONTENT_COLOR: + case CAIRO_CONTENT_COLOR_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); + break; + } + + cairo_surface_destroy (surface); + return cr; +} + +void +helper_cairo_destroy_context (cairo_t *cr) +{ + finalize_closure_t *closure = (finalize_closure_t *) + cairo_surface_get_user_data (cairo_get_target (cr), + &finalize_closure_key); + if (closure) + closure->callback (closure); + + cairo_status_t status = cairo_status (cr); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed: %s", + cairo_status_to_string (status)); + cairo_destroy (cr); +} + + +void +helper_cairo_line_from_buffer (helper_cairo_line_t *l, + hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + double scale, + hb_bool_t utf8_clusters) +{ + memset (l, 0, sizeof (*l)); + + l->num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL); + l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); + + if (text) { + l->utf8 = g_strndup (text, text_len); + l->utf8_len = text_len; + l->num_clusters = l->num_glyphs ? 1 : 0; + for (unsigned int i = 1; i < l->num_glyphs; i++) + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + l->num_clusters++; + l->clusters = cairo_text_cluster_allocate (l->num_clusters); + } + + if ((l->num_glyphs && !l->glyphs) || + (l->utf8_len && !l->utf8) || + (l->num_clusters && !l->clusters)) + { + l->finish (); + return; + } + + hb_position_t x = 0, y = 0; + int i; + for (i = 0; i < (int) l->num_glyphs; i++) + { + l->glyphs[i].index = hb_glyph[i].codepoint; + l->glyphs[i].x = ( hb_position->x_offset + x) * scale; + l->glyphs[i].y = (-hb_position->y_offset + y) * scale; + x += hb_position->x_advance; + y += -hb_position->y_advance; + + hb_position++; + } + l->glyphs[i].index = -1; + l->glyphs[i].x = x * scale; + l->glyphs[i].y = y * scale; + + if (l->num_clusters) { + memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); + hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); + l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; + unsigned int cluster = 0; + const char *start = l->utf8, *end = start; + l->clusters[cluster].num_glyphs++; + if (backward) { + for (i = l->num_glyphs - 2; i >= 0; i--) { + if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } else { + for (i = 1; i < (int) l->num_glyphs; i++) { + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } + } +} diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh new file mode 100644 index 0000000..2f2c9d4 --- /dev/null +++ b/util/helper-cairo.hh @@ -0,0 +1,81 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "options.hh" + +#include + +#ifndef HELPER_CAIRO_HH +#define HELPER_CAIRO_HH + + +cairo_scaled_font_t * +helper_cairo_create_scaled_font (const font_options_t *font_opts, + double font_size); + + +cairo_t * +helper_cairo_create_context (double w, double h, + view_options_t *view_opts, + output_options_t *out_opts); + +void +helper_cairo_destroy_context (cairo_t *cr); + + +struct helper_cairo_line_t { + cairo_glyph_t *glyphs; + unsigned int num_glyphs; + char *utf8; + unsigned int utf8_len; + cairo_text_cluster_t *clusters; + unsigned int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + + void finish (void) { + if (glyphs) + cairo_glyph_free (glyphs); + if (clusters) + cairo_text_cluster_free (clusters); + if (utf8) + g_free (utf8); + } + + void get_advance (double *x_advance, double *y_advance) { + *x_advance = glyphs[num_glyphs].x; + *y_advance = glyphs[num_glyphs].y; + } +}; + +void +helper_cairo_line_from_buffer (helper_cairo_line_t *l, + hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + double scale, + hb_bool_t utf8_clusters); + +#endif diff --git a/util/main-font-text.hh b/util/main-font-text.hh new file mode 100644 index 0000000..44e3bfb --- /dev/null +++ b/util/main-font-text.hh @@ -0,0 +1,80 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "options.hh" + +#ifndef HB_MAIN_FONT_TEXT_HH +#define HB_MAIN_FONT_TEXT_HH + +/* main() body for utilities taking font and processing text.*/ + +template +struct main_font_text_t +{ + main_font_text_t (void) + : options ("[FONT-FILE] [TEXT]"), + font_opts (&options), + input (&options), + consumer (&options) {} + + int + main (int argc, char **argv) + { + options.parse (&argc, &argv); + + argc--, argv++; + if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++; + if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++; + if (argc) + fail (true, "Too many arguments on the command line"); + if (!font_opts.font_file) + options.usage (); + if (!input.text && !input.text_file) + input.text_file = "-"; + + consumer.init (&font_opts); + + hb_buffer_t *buffer = hb_buffer_create (); + unsigned int text_len; + const char *text; + while ((text = input.get_line (&text_len))) + consumer.consume_line (buffer, text, text_len); + hb_buffer_destroy (buffer); + + consumer.finish (&font_opts); + + return consumer.failed ? 1 : 0; + } + + protected: + option_parser_t options; + font_options_t font_opts; + text_options_t input; + consumer_t consumer; +}; + +#endif + diff --git a/util/options.cc b/util/options.cc new file mode 100644 index 0000000..db1b244 --- /dev/null +++ b/util/options.cc @@ -0,0 +1,829 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "options.hh" + +#ifdef HAVE_FREETYPE +#include +#endif + + +void +fail (hb_bool_t suggest_help, const char *format, ...) +{ + const char *msg; + + va_list vap; + va_start (vap, format); + msg = g_strdup_vprintf (format, vap); + const char *prgname = g_get_prgname (); + g_printerr ("%s: %s\n", prgname, msg); + if (suggest_help) + g_printerr ("Try `%s --help' for more information.\n", prgname); + + exit (1); +} + + +hb_bool_t debug = false; + +static gchar * +shapers_to_string (void) +{ + GString *shapers = g_string_new (NULL); + const char **shaper_list = hb_shape_list_shapers (); + + for (; *shaper_list; shaper_list++) { + g_string_append (shapers, *shaper_list); + g_string_append_c (shapers, ','); + } + g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1)); + + return g_string_free (shapers, false); +} + +static G_GNUC_NORETURN gboolean +show_version (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); + + char *shapers = shapers_to_string (); + g_printf ("Available shapers: %s\n", shapers); + g_free (shapers); + if (strcmp (HB_VERSION_STRING, hb_version_string ())) + g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ()); + + exit(0); +} + + +void +option_parser_t::add_main_options (void) +{ + GOptionEntry entries[] = + { + {"version", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", NULL}, + {"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", NULL}, + {NULL} + }; + g_option_context_add_main_entries (context, entries, NULL); +} + +static gboolean +pre_parse (GOptionContext *context G_GNUC_UNUSED, + GOptionGroup *group G_GNUC_UNUSED, + gpointer data, + GError **error) +{ + option_group_t *option_group = (option_group_t *) data; + option_group->pre_parse (error); + return *error == NULL; +} + +static gboolean +post_parse (GOptionContext *context G_GNUC_UNUSED, + GOptionGroup *group G_GNUC_UNUSED, + gpointer data, + GError **error) +{ + option_group_t *option_group = static_cast(data); + option_group->post_parse (error); + return *error == NULL; +} + +void +option_parser_t::add_group (GOptionEntry *entries, + const gchar *name, + const gchar *description, + const gchar *help_description, + option_group_t *option_group) +{ + GOptionGroup *group = g_option_group_new (name, description, help_description, + static_cast(option_group), NULL); + g_option_group_add_entries (group, entries); + g_option_group_set_parse_hooks (group, pre_parse, post_parse); + g_option_context_add_group (context, group); +} + +void +option_parser_t::parse (int *argc, char ***argv) +{ + setlocale (LC_ALL, ""); + + GError *parse_error = NULL; + if (!g_option_context_parse (context, argc, argv, &parse_error)) + { + if (parse_error != NULL) { + fail (true, "%s", parse_error->message); + //g_error_free (parse_error); + } else + fail (true, "Option parse error"); + } +} + + +static gboolean +parse_margin (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + view_options_t *view_opts = (view_options_t *) data; + view_options_t::margin_t &m = view_opts->margin; + switch (sscanf (arg, "%lf %lf %lf %lf", &m.t, &m.r, &m.b, &m.l)) { + case 1: m.r = m.t; + case 2: m.b = m.t; + case 3: m.l = m.r; + case 4: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one to four space-separated numbers", + name); + return false; + } +} + + +static gboolean +parse_shapers (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + g_free (shape_opts->shapers); + shape_opts->shapers = g_strsplit (arg, ",", 0); + return true; +} + +static G_GNUC_NORETURN gboolean +list_shapers (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++) + g_printf ("%s\n", *shaper); + + exit(0); +} + + + +static void +parse_space (char **pp) +{ + char c; +#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v') + while (c = **pp, ISSPACE (c)) + (*pp)++; +#undef ISSPACE +} + +static hb_bool_t +parse_char (char **pp, char c) +{ + parse_space (pp); + + if (**pp != c) + return false; + + (*pp)++; + return true; +} + +static hb_bool_t +parse_uint (char **pp, unsigned int *pv) +{ + char *p = *pp; + unsigned int v; + + v = strtol (p, pp, 0); + + if (p == *pp) + return false; + + *pv = v; + return true; +} + + +static hb_bool_t +parse_feature_value_prefix (char **pp, hb_feature_t *feature) +{ + if (parse_char (pp, '-')) + feature->value = 0; + else { + parse_char (pp, '+'); + feature->value = 1; + } + + return true; +} + +static hb_bool_t +parse_feature_tag (char **pp, hb_feature_t *feature) +{ + char *p = *pp, c; + + parse_space (pp); + +#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) + while (c = **pp, ISALNUM(c)) + (*pp)++; +#undef ISALNUM + + if (p == *pp) + return false; + + feature->tag = hb_tag_from_string (p, *pp - p); + return true; +} + +static hb_bool_t +parse_feature_indices (char **pp, hb_feature_t *feature) +{ + parse_space (pp); + + hb_bool_t has_start; + + feature->start = 0; + feature->end = (unsigned int) -1; + + if (!parse_char (pp, '[')) + return true; + + has_start = parse_uint (pp, &feature->start); + + if (parse_char (pp, ':')) { + parse_uint (pp, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; + } + + return parse_char (pp, ']'); +} + +static hb_bool_t +parse_feature_value_postfix (char **pp, hb_feature_t *feature) +{ + return !parse_char (pp, '=') || parse_uint (pp, &feature->value); +} + + +static hb_bool_t +parse_one_feature (char **pp, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, feature) && + parse_feature_tag (pp, feature) && + parse_feature_indices (pp, feature) && + parse_feature_value_postfix (pp, feature) && + (parse_char (pp, ',') || **pp == '\0'); +} + +static void +skip_one_feature (char **pp) +{ + char *e; + e = strchr (*pp, ','); + if (e) + *pp = e + 1; + else + *pp = *pp + strlen (*pp); +} + +static gboolean +parse_features (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + char *s = (char *) arg; + char *p; + + shape_opts->num_features = 0; + g_free (shape_opts->features); + shape_opts->features = NULL; + + if (!*s) + return true; + + /* count the features first, so we can allocate memory */ + p = s; + do { + shape_opts->num_features++; + p = strchr (p, ','); + if (p) + p++; + } while (p); + + shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features)); + + /* now do the actual parsing */ + p = s; + shape_opts->num_features = 0; + while (*p) { + if (parse_one_feature (&p, &shape_opts->features[shape_opts->num_features])) + shape_opts->num_features++; + else + skip_one_feature (&p); + } + + return true; +} + + +void +view_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", NULL}, + {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: "DEFAULT_BACK")", "red/#rrggbb/#rrggbbaa"}, + {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: "DEFAULT_FORE")", "red/#rrggbb/#rrggbbaa"}, + {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"}, + {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"}, + {"font-size", 0, 0, G_OPTION_ARG_DOUBLE, &this->font_size, "Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"}, + {NULL} + }; + parser->add_group (entries, + "view", + "View options:", + "Options controlling output rendering", + this); +} + +void +shape_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"list-shapers", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", NULL}, + {"shaper", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", NULL}, + {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Comma-separated list of shapers to try","list"}, + {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"}, + {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"}, + {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, + {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", NULL}, + {NULL} + }; + parser->add_group (entries, + "shape", + "Shape options:", + "Options controlling the shaping process", + this); + + const gchar *features_help = "Comma-separated list of font features\n" + "\n" + " Features can be enabled or disabled, either globally or limited to\n" + " specific character ranges.\n" + "\n" + " The range indices refer to the positions between Unicode characters,\n" + " unless the --utf8-clusters is provided, in which case range indices\n" + " refer to UTF-8 byte indices. The position before the first character\n" + " is always 0.\n" + "\n" + " The format is Python-esque. Here is how it all works:\n" + "\n" + " Syntax: Value: Start: End:\n" + "\n" + " Setting value:\n" + " \"kern\" 1 0 ∞ # Turn feature on\n" + " \"+kern\" 1 0 ∞ # Turn feature on\n" + " \"-kern\" 0 0 ∞ # Turn feature off\n" + " \"kern=0\" 0 0 ∞ # Turn feature off\n" + " \"kern=1\" 1 0 ∞ # Turn feature on\n" + " \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n" + "\n" + " Setting index:\n" + " \"kern[]\" 1 0 ∞ # Turn feature on\n" + " \"kern[:]\" 1 0 ∞ # Turn feature on\n" + " \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n" + " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n" + " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n" + " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n" + "\n" + " Mixing it all:\n" + "\n" + " \"kern[3:5]=0\" 1 3 5 # Turn feature off for range"; + + GOptionEntry entries2[] = + { + {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"}, + {NULL} + }; + parser->add_group (entries2, + "features", + "Features options:", + "Options controlling font features used", + this); +} + +void +font_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Font file-name", "filename"}, + {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Face index (default: 0)", "index"}, + {NULL} + }; + parser->add_group (entries, + "font", + "Font options:", + "Options controlling the font", + this); +} + +void +text_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"text", 0, 0, G_OPTION_ARG_STRING, &this->text, "Set input text", "string"}, + {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name\n\n If no text is provided, standard input is used for input.", "filename"}, + {NULL} + }; + parser->add_group (entries, + "text", + "Text options:", + "Options controlling the input text", + this); +} + +void +output_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"output-file", 0, 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, + {"output-format", 0, 0, G_OPTION_ARG_STRING, &this->output_format, "Set output format", "format"}, + {NULL} + }; + parser->add_group (entries, + "output", + "Output options:", + "Options controlling the output", + this); +} + + + +hb_font_t * +font_options_t::get_font (void) const +{ + if (font) + return font; + + hb_blob_t *blob = NULL; + + /* Create the blob */ + { + char *font_data; + unsigned int len = 0; + hb_destroy_func_t destroy; + void *user_data; + hb_memory_mode_t mm; + + /* This is a hell of a lot of code for just reading a file! */ + if (!font_file) + fail (true, "No font file set"); + + if (0 == strcmp (font_file, "-")) { + /* read it */ + GString *gs = g_string_new (NULL); + char buf[BUFSIZ]; +#ifdef HAVE__SETMODE + _setmode (fileno (stdin), _O_BINARY); +#endif + while (!feof (stdin)) { + size_t ret = fread (buf, 1, sizeof (buf), stdin); + if (ferror (stdin)) + fail (false, "Failed reading font from standard input: %s", + strerror (errno)); + g_string_append_len (gs, buf, ret); + } + len = gs->len; + font_data = g_string_free (gs, false); + user_data = font_data; + destroy = (hb_destroy_func_t) g_free; + mm = HB_MEMORY_MODE_WRITABLE; + } else { + GError *error = NULL; + GMappedFile *mf = g_mapped_file_new (font_file, false, &error); + if (mf) { + font_data = g_mapped_file_get_contents (mf); + len = g_mapped_file_get_length (mf); + if (len) { + destroy = (hb_destroy_func_t) g_mapped_file_unref; + user_data = (void *) mf; + mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE; + } else + g_mapped_file_unref (mf); + } else { + fail (false, "%s", error->message); + //g_error_free (error); + } + if (!len) { + /* GMappedFile is buggy, it doesn't fail if file isn't regular. + * Try reading. + * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */ + GError *error = NULL; + gsize l; + if (g_file_get_contents (font_file, &font_data, &l, &error)) { + len = l; + destroy = (hb_destroy_func_t) g_free; + user_data = (void *) font_data; + mm = HB_MEMORY_MODE_WRITABLE; + } else { + fail (false, "%s", error->message); + //g_error_free (error); + } + } + } + + blob = hb_blob_create (font_data, len, mm, user_data, destroy); + } + + /* Create the face */ + hb_face_t *face = hb_face_create (blob, face_index); + hb_blob_destroy (blob); + + + font = hb_font_create (face); + + unsigned int upem = hb_face_get_upem (face); + hb_font_set_scale (font, upem, upem); + hb_face_destroy (face); + +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + return font; +} + + +const char * +text_options_t::get_line (unsigned int *len) +{ + if (text) { + if (text_len == (unsigned int) -1) + text_len = strlen (text); + + if (!text_len) { + *len = 0; + return NULL; + } + + const char *ret = text; + const char *p = (const char *) memchr (text, '\n', text_len); + unsigned int ret_len; + if (!p) { + ret_len = text_len; + text += ret_len; + text_len = 0; + } else { + ret_len = p - ret; + text += ret_len + 1; + text_len -= ret_len + 1; + } + + *len = ret_len; + return ret; + } + + if (!fp) { + if (!text_file) + fail (true, "At least one of text or text-file must be set"); + + if (0 != strcmp (text_file, "-")) + fp = fopen (text_file, "r"); + else + fp = stdin; + + if (!fp) + fail (false, "Failed opening text file `%s': %s", + text_file, strerror (errno)); + + gs = g_string_new (NULL); + } + + g_string_set_size (gs, 0); + char buf[BUFSIZ]; + while (fgets (buf, sizeof (buf), fp)) { + unsigned int bytes = strlen (buf); + if (bytes && buf[bytes - 1] == '\n') { + bytes--; + g_string_append_len (gs, buf, bytes); + break; + } + g_string_append_len (gs, buf, bytes); + } + if (ferror (fp)) + fail (false, "Failed reading text: %s", + strerror (errno)); + *len = gs->len; + return !*len && feof (fp) ? NULL : gs->str; +} + + +FILE * +output_options_t::get_file_handle (void) +{ + if (fp) + return fp; + + if (output_file) + fp = fopen (output_file, "wb"); + else { +#ifdef HAVE__SETMODE + _setmode (fileno (stdout), _O_BINARY); +#endif + fp = stdout; + } + if (!fp) + fail (false, "Cannot open output file `%s': %s", + g_filename_display_name (output_file), strerror (errno)); + + return fp; +} + +static gboolean +parse_verbose (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + format_options_t *format_opts = (format_options_t *) data; + format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true; + return true; +} + +void +format_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", NULL}, + {"no-positions", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_positions, "Do not show glyph positions", NULL}, + {"no-clusters", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_clusters, "Do not show cluster mapping", NULL}, + {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Show input text", NULL}, + {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Show input Unicode codepoints", NULL}, + {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Show line numbers", NULL}, + {"verbose", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,(gpointer) &parse_verbose, "Show everything", NULL}, + {NULL} + }; + parser->add_group (entries, + "format", + "Format options:", + "Options controlling the formatting of buffer contents", + this); +} + +void +format_options_t::serialize_unicode (hb_buffer_t *buffer, + GString *gs) +{ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + + g_string_append_c (gs, '<'); + for (unsigned int i = 0; i < num_glyphs; i++) + { + if (i) + g_string_append_c (gs, ','); + g_string_append_printf (gs, "U+%04X", info->codepoint); + info++; + } + g_string_append_c (gs, '>'); +} + +void +format_options_t::serialize_glyphs (hb_buffer_t *buffer, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs) +{ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL); + + g_string_append_c (gs, '['); + for (unsigned int i = 0; i < num_glyphs; i++) + { + if (i) + g_string_append_c (gs, '|'); + + char glyph_name[32]; + if (show_glyph_names) { + hb_font_get_glyph_name (font, info->codepoint, glyph_name, sizeof (glyph_name)); + g_string_append_printf (gs, "%s", glyph_name); + } else + g_string_append_printf (gs, "%u", info->codepoint); + + if (show_clusters) { + g_string_append_printf (gs, "=%u", info->cluster); + if (utf8_clusters) + g_string_append (gs, "u8"); + } + + if (show_positions && (pos->x_offset || pos->y_offset)) { + g_string_append_c (gs, '@'); + if (pos->x_offset) g_string_append_printf (gs, "%d", pos->x_offset); + if (pos->y_offset) g_string_append_printf (gs, ",%d", pos->y_offset); + } + if (show_positions && (pos->x_advance || pos->y_advance)) { + g_string_append_c (gs, '+'); + if (pos->x_advance) g_string_append_printf (gs, "%d", pos->x_advance); + if (pos->y_advance) g_string_append_printf (gs, ",%d", pos->y_advance); + } + + info++; + pos++; + } + g_string_append_c (gs, ']'); +} +void +format_options_t::serialize_line_no (unsigned int line_no, + GString *gs) +{ + if (show_line_num) + g_string_append_printf (gs, "%d: ", line_no); +} +void +format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs) +{ + if (show_text) { + serialize_line_no (line_no, gs); + g_string_append_c (gs, '('); + g_string_append_len (gs, text, text_len); + g_string_append_c (gs, ')'); + g_string_append_c (gs, '\n'); + } + + if (show_unicode) { + serialize_line_no (line_no, gs); + serialize_unicode (buffer, gs); + g_string_append_c (gs, '\n'); + } +} +void +format_options_t::serialize_message (unsigned int line_no, + const char *msg, + GString *gs) +{ + serialize_line_no (line_no, gs); + g_string_append_printf (gs, "%s", msg); + g_string_append_c (gs, '\n'); +} +void +format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs) +{ + serialize_line_no (line_no, gs); + serialize_glyphs (buffer, font, utf8_clusters, gs); + g_string_append_c (gs, '\n'); +} diff --git a/util/options.hh b/util/options.hh new file mode 100644 index 0000000..9b7baa7 --- /dev/null +++ b/util/options.hh @@ -0,0 +1,368 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OPTIONS_HH +#define OPTIONS_HH + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include /* for isatty() */ +#endif +#ifdef HAVE_IO_H +#include /* for _setmode() under Windows */ +#endif + +#include +#ifdef HAVE_OT +#include +#endif +#include +#include + +#undef MIN +template static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; } + +#undef MAX +template static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; } + + +void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN; + + +extern hb_bool_t debug; + +struct option_group_t +{ + virtual void add_options (struct option_parser_t *parser) = 0; + + virtual void pre_parse (GError **error G_GNUC_UNUSED) {}; + virtual void post_parse (GError **error G_GNUC_UNUSED) {}; +}; + + +struct option_parser_t +{ + option_parser_t (const char *usage) { + memset (this, 0, sizeof (*this)); + usage_str = usage; + context = g_option_context_new (usage); + + add_main_options (); + } + ~option_parser_t (void) { + g_option_context_free (context); + } + + void add_main_options (void); + + void add_group (GOptionEntry *entries, + const gchar *name, + const gchar *description, + const gchar *help_description, + option_group_t *option_group); + + void parse (int *argc, char ***argv); + + G_GNUC_NORETURN void usage (void) { + g_printerr ("Usage: %s [OPTION...] %s\n", g_get_prgname (), usage_str); + exit (1); + } + + const char *usage_str; + GOptionContext *context; +}; + + +#define DEFAULT_MARGIN 16 +#define DEFAULT_FORE "#000000" +#define DEFAULT_BACK "#FFFFFF" +#define DEFAULT_FONT_SIZE 256 + +struct view_options_t : option_group_t +{ + view_options_t (option_parser_t *parser) { + annotate = false; + fore = DEFAULT_FORE; + back = DEFAULT_BACK; + line_space = 0; + margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN; + font_size = DEFAULT_FONT_SIZE; + + add_options (parser); + } + + void add_options (option_parser_t *parser); + + hb_bool_t annotate; + const char *fore; + const char *back; + double line_space; + struct margin_t { + double t, r, b, l; + } margin; + double font_size; +}; + + +struct shape_options_t : option_group_t +{ + shape_options_t (option_parser_t *parser) + { + direction = language = script = NULL; + features = NULL; + num_features = 0; + shapers = NULL; + utf8_clusters = false; + + add_options (parser); + } + ~shape_options_t (void) + { + free (features); + g_free (shapers); + } + + void add_options (option_parser_t *parser); + + void setup_buffer (hb_buffer_t *buffer) + { + hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1)); + hb_buffer_set_script (buffer, hb_script_from_string (script, -1)); + hb_buffer_set_language (buffer, hb_language_from_string (language, -1)); + } + + void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len) + { + hb_buffer_reset (buffer); + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + + if (!utf8_clusters) { + /* Reset cluster values to refer to Unicode character index + * instead of UTF-8 index. */ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + for (unsigned int i = 0; i < num_glyphs; i++) + { + info->cluster = i; + info++; + } + } + + setup_buffer (buffer); + } + + hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer) + { + return hb_shape_full (font, buffer, features, num_features, shapers); + } + + void shape_closure (const char *text, int text_len, + hb_font_t *font, hb_buffer_t *buffer, + hb_set_t *glyphs) + { + hb_buffer_reset (buffer); + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + setup_buffer (buffer); + hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs); + } + + const char *direction; + const char *language; + const char *script; + hb_feature_t *features; + unsigned int num_features; + char **shapers; + hb_bool_t utf8_clusters; +}; + + +struct font_options_t : option_group_t +{ + font_options_t (option_parser_t *parser) { + font_file = NULL; + face_index = 0; + + font = NULL; + + add_options (parser); + } + ~font_options_t (void) { + hb_font_destroy (font); + } + + void add_options (option_parser_t *parser); + + hb_font_t *get_font (void) const; + + const char *font_file; + int face_index; + + private: + mutable hb_font_t *font; +}; + + +struct text_options_t : option_group_t +{ + text_options_t (option_parser_t *parser) { + text = NULL; + text_file = NULL; + + fp = NULL; + gs = NULL; + text_len = (unsigned int) -1; + + add_options (parser); + } + ~text_options_t (void) { + if (gs) + g_string_free (gs, true); + if (fp) + fclose (fp); + } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error G_GNUC_UNUSED) { + if (text && text_file) + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Only one of text and text-file can be set"); + + }; + + const char *get_line (unsigned int *len); + + const char *text; + const char *text_file; + + private: + FILE *fp; + GString *gs; + unsigned int text_len; +}; + +struct output_options_t : option_group_t +{ + output_options_t (option_parser_t *parser) { + output_file = NULL; + output_format = NULL; + + fp = NULL; + + add_options (parser); + } + ~output_options_t (void) { + if (fp) + fclose (fp); + } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error G_GNUC_UNUSED) + { + if (output_file && !output_format) { + output_format = strrchr (output_file, '.'); + if (output_format) + output_format++; /* skip the dot */ + } + + if (output_file && 0 == strcmp (output_file, "-")) + output_file = NULL; /* STDOUT */ + } + + FILE *get_file_handle (void); + + const char *output_file; + const char *output_format; + + mutable FILE *fp; +}; + +struct format_options_t : option_group_t +{ + format_options_t (option_parser_t *parser) { + show_glyph_names = true; + show_positions = true; + show_clusters = true; + show_text = false; + show_unicode = false; + show_line_num = false; + + add_options (parser); + } + + void add_options (option_parser_t *parser); + + void serialize_unicode (hb_buffer_t *buffer, + GString *gs); + void serialize_glyphs (hb_buffer_t *buffer, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs); + void serialize_line_no (unsigned int line_no, + GString *gs); + void serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs); + void serialize_message (unsigned int line_no, + const char *msg, + GString *gs); + void serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_bool_t utf8_clusters, + GString *gs); + + + hb_bool_t show_glyph_names; + hb_bool_t show_positions; + hb_bool_t show_clusters; + hb_bool_t show_text; + hb_bool_t show_unicode; + hb_bool_t show_line_num; +}; + + +#endif diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh new file mode 100644 index 0000000..220daa4 --- /dev/null +++ b/util/shape-consumer.hh @@ -0,0 +1,82 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "options.hh" + +#ifndef HB_SHAPE_CONSUMER_HH +#define HB_SHAPE_CONSUMER_HH + + +template +struct shape_consumer_t +{ + shape_consumer_t (option_parser_t *parser) + : shaper (parser), + output (parser) {} + + void init (const font_options_t *font_opts) + { + font = hb_font_reference (font_opts->get_font ()); + output.init (font_opts); + failed = false; + } + void consume_line (hb_buffer_t *buffer, + const char *text, + unsigned int text_len) + { + output.new_line (); + + shaper.populate_buffer (buffer, text, text_len); + output.consume_text (buffer, text, text_len, shaper.utf8_clusters); + + if (!shaper.shape (font, buffer)) { + failed = true; + hb_buffer_set_length (buffer, 0); + output.shape_failed (buffer, text, text_len, shaper.utf8_clusters); + return; + } + + output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters); + } + void finish (const font_options_t *font_opts) + { + output.finish (font_opts); + hb_font_destroy (font); + font = NULL; + } + + public: + bool failed; + + protected: + shape_options_t shaper; + output_t output; + + hb_font_t *font; +}; + + +#endif diff --git a/util/view-cairo.cc b/util/view-cairo.cc new file mode 100644 index 0000000..666013e --- /dev/null +++ b/util/view-cairo.cc @@ -0,0 +1,126 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "view-cairo.hh" + +void +view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font, + double *w, double *h) +{ + cairo_font_extents_t font_extents; + + cairo_scaled_font_extents (scaled_font, &font_extents); + + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + (vertical ? *w : *h) = (int) lines->len * (font_extents.height + view_options.line_space) - view_options.line_space; + (vertical ? *h : *w) = 0; + for (unsigned int i = 0; i < lines->len; i++) { + helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); + double x_advance, y_advance; + line.get_advance (&x_advance, &y_advance); + if (vertical) + *h = MAX (*h, y_advance); + else + *w = MAX (*w, x_advance); + } + + *w += view_options.margin.l + view_options.margin.r; + *h += view_options.margin.t + view_options.margin.b; +} + +void +view_cairo_t::render (const font_options_t *font_opts) +{ + cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts, view_options.font_size); + double w, h; + get_surface_size (scaled_font, &w, &h); + cairo_t *cr = helper_cairo_create_context (w, h, &view_options, &output_options); + cairo_set_scaled_font (cr, scaled_font); + cairo_scaled_font_destroy (scaled_font); + + draw (cr); + + helper_cairo_destroy_context (cr); +} + +void +view_cairo_t::draw (cairo_t *cr) +{ + cairo_save (cr); + + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + int v = vertical ? 1 : 0; + int h = vertical ? 0 : 1; + cairo_font_extents_t font_extents; + cairo_font_extents (cr, &font_extents); + cairo_translate (cr, view_options.margin.l, view_options.margin.t); + double descent; + if (vertical) + descent = font_extents.height * (lines->len + .5); + else + descent = font_extents.height - font_extents.ascent; + cairo_translate (cr, v * descent, h * -descent); + for (unsigned int i = 0; i < lines->len; i++) + { + helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i); + + if (i) + cairo_translate (cr, v * -view_options.line_space, h * view_options.line_space); + + cairo_translate (cr, v * -font_extents.height, h * font_extents.height); + + if (view_options.annotate) { + cairo_save (cr); + + /* Draw actual glyph origins */ + cairo_set_source_rgba (cr, 1., 0., 0., .5); + cairo_set_line_width (cr, 5); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + for (unsigned i = 0; i < l.num_glyphs; i++) { + cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y); + cairo_rel_line_to (cr, 0, 0); + } + cairo_stroke (cr); + + cairo_restore (cr); + } + + if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) { + /* cairo_show_glyphs() doesn't support subpixel positioning */ + cairo_glyph_path (cr, l.glyphs, l.num_glyphs); + cairo_fill (cr); + } else if (l.num_clusters) + cairo_show_text_glyphs (cr, + l.utf8, l.utf8_len, + l.glyphs, l.num_glyphs, + l.clusters, l.num_clusters, + l.cluster_flags); + else + cairo_show_glyphs (cr, l.glyphs, l.num_glyphs); + } + + cairo_restore (cr); +} diff --git a/util/view-cairo.hh b/util/view-cairo.hh new file mode 100644 index 0000000..c621984 --- /dev/null +++ b/util/view-cairo.hh @@ -0,0 +1,99 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "options.hh" +#include "helper-cairo.hh" + +#ifndef VIEW_CAIRO_HH +#define VIEW_CAIRO_HH + + +struct view_cairo_t { + view_cairo_t (option_parser_t *parser) + : output_options (parser), + view_options (parser) {} + ~view_cairo_t (void) { + if (debug) + cairo_debug_reset_static_data (); + } + + void init (const font_options_t *font_opts) + { + lines = g_array_new (false, false, sizeof (helper_cairo_line_t)); + scale = double (view_options.font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ())); + } + void new_line (void) + { + } + void consume_text (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + } + void shape_failed (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + fail (false, "all shapers failed"); + } + void consume_glyphs (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + direction = hb_buffer_get_direction (buffer); + helper_cairo_line_t l; + helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale, utf8_clusters); + g_array_append_val (lines, l); + } + void finish (const font_options_t *font_opts) + { + render (font_opts); + + for (unsigned int i = 0; i < lines->len; i++) { + helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); + line.finish (); + } + g_array_unref (lines); + } + + protected: + + output_options_t output_options; + view_options_t view_options; + + void render (const font_options_t *font_opts); + void get_surface_size (cairo_scaled_font_t *scaled_font, double *w, double *h); + void draw (cairo_t *cr); + + hb_direction_t direction; // Remove this, make segment_properties accessible + GArray *lines; + double scale; +}; + +#endif -- 2.7.4