NULL =
-SUBDIRS = src test
+SUBDIRS = src util test
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = harfbuzz.pc
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?)
- 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:
- 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.
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 $@"
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/])
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))
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
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)
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])
dnl ==========================================================================
PKG_CHECK_MODULES(ICU, icu, have_icu=true, [
- AC_CHECK_PROG([have_icu], [icu-config], [true], [false])
+ have_icu=true
+ AC_CHECK_HEADERS(unicode/uchar.h,, have_icu=false)
+ AC_MSG_CHECKING([for libicuuc])
+ LIBS_old=$LIBS
+ LIBS="$LIBS -licuuc"
+ AC_TRY_LINK([#include <unicode/uchar.h>],
+ [u_getIntPropertyValue (0, (UProperty)0);],
+ AC_MSG_RESULT(yes),
+ AC_MSG_RESULT(no);have_icu=false)
+ LIBS=$LIBS_old
if $have_icu; then
- icu_cflags=`icu-config --cppflags`
- icu_libs=`icu-config --ldflags-libsonly`
- AC_SUBST(ICU_CFLAGS, [$icu_cflags])
- AC_SUBST(ICU_LIBS, [$icu_libs])
+ ICU_CFLAGS=-D_REENTRANT
+ ICU_LIBS="-licuuc"
+ AC_SUBST(ICU_CFLAGS)
+ AC_SUBST(ICU_LIBS)
fi
])
if $have_icu; then
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"
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
+harfbuzz (0.9.0-1slp2+1) unstable; urgency=low
+
+ * Upgrade to latest harfbuzz
+ * Git: 165.213.180.234:slp/pkgs/h/harfbuzz
+ * Tag: harfbuzz_0.9.1-1slp2+1
+
+ -- Ankush Dua <ankush.d@samsung.com> Wed, 27 June 2012 15:00:08 +0900
+
harfbuzz (0.7.0-1slp2+1) unstable; urgency=low
* Upgrade to latest harfbuzz
Source: harfbuzz
Section: libs
Priority: optional
-Maintainer: janani <janani.k@samsung.com>, Kesavardhana Gandla <kesav.gandla@samsung.com>
+Maintainer: janani <janani.k@samsung.com>, Ankush Dua <ankush.d@samsung.com>
Build-Depends: debhelper (>= 5),libfreetype6-dev, pkg-config, ragel
Standards-Version: 3.7.2
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
# Process this file with automake to produce Makefile.in
NULL =
+BUILT_SOURCES =
EXTRA_DIST =
CLEANFILES =
DISTCLEANFILES =
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 \
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 \
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)
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 \
HBHEADERS="$(HBHEADERS)" \
$(NULL)
+scan:
+ g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library libharfbuzz.la
+
+
-include $(top_srcdir)/git.mk
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
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
:
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
:
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
#!/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)
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]
j = line.find ('#')
if j >= 0:
line = line[:j]
-
+
fields = [x.strip () for x in line.split (';')]
if len (fields) == 1:
continue
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',
print
print
+total = 0
+used = 0
def print_block (block, start, end, data):
print
print
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 ()
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)"
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):
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)
--- /dev/null
+/*
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2009,2010 Red Hat, Inc.
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ATOMIC_PRIVATE_HH
+#define HB_ATOMIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* atomic_int */
+
+/* We need external help for these */
+
+#if 0
+
+
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
+
+#include <intrin.h>
+#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchangePointer)
+
+typedef long hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V) _InterlockedExchangeAdd (&(AI), (V))
+
+#define hb_atomic_ptr_get(P) (MemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N) (_InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
+
+
+#elif !defined(HB_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+typedef int32_t hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+
+#define hb_atomic_ptr_get(P) (OSMemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) && !defined(__MINGW32__)
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V) __sync_fetch_and_add (&(AI), (V))
+
+#define hb_atomic_ptr_get(P) (void *) (__sync_synchronize (), *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N))
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
+typedef int hb_atomic_int_t;
+#if GLIB_CHECK_VERSION(2,29,5)
+#define hb_atomic_int_add(AI, V) g_atomic_int_add (&(AI), (V))
+#else
+#define hb_atomic_int_add(AI, V) g_atomic_int_exchange_and_add (&(AI), (V))
+#endif
+
+#define hb_atomic_ptr_get(P) g_atomic_pointer_get (P)
+#define hb_atomic_ptr_cmpexch(P,O,N) g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (N))
+
+
+#elif !defined(HB_NO_MT)
+
+#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
+typedef volatile int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V) (((AI) += (V)) - (V))
+
+#define hb_atomic_ptr_get(P) ((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N) (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
+
+
+#else /* HB_NO_MT */
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V) (((AI) += (V)) - (V))
+
+#define hb_atomic_ptr_get(P) ((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
+
+#endif
+
+/* TODO Add tracing. */
+
+#endif /* HB_ATOMIC_PRIVATE_HH */
#include <stdio.h>
#include <errno.h>
-HB_BEGIN_DECLS
#ifndef HB_DEBUG_BLOB
struct _hb_blob_t {
hb_object_header_t header;
+ ASSERT_POD ();
bool immutable;
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);
if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
if (destroy)
destroy (user_data);
- return &_hb_blob_nil;
+ return hb_blob_get_empty ();
}
blob->data = data;
blob->mode = HB_MEMORY_MODE_READONLY;
if (!_try_writable (blob)) {
hb_blob_destroy (blob);
- return &_hb_blob_nil;
+ return hb_blob_get_empty ();
}
}
hb_blob_t *blob;
if (!length || offset >= parent->length)
- return &_hb_blob_nil;
+ return hb_blob_get_empty ();
hb_blob_make_immutable (parent);
hb_blob_t *
hb_blob_get_empty (void)
{
- return &_hb_blob_nil;
+ static const hb_blob_t _hb_blob_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ true, /* immutable */
+
+ NULL, /* data */
+ 0, /* length */
+ HB_MEMORY_MODE_READONLY, /* mode */
+
+ NULL, /* user_data */
+ NULL /* destroy */
+ };
+
+ return const_cast<hb_blob_t *> (&_hb_blob_nil);
}
hb_blob_t *
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 *
if (hb_object_is_inert (blob))
return;
- blob->immutable = TRUE;
+ blob->immutable = true;
}
hb_bool_t
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);
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;
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
}
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);
blob->user_data = new_data;
blob->destroy = free;
- return TRUE;
+ return true;
}
-HB_END_DECLS
* Red Hat Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_BLOB_H
#define HB_BLOB_H
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 *
#include "hb-object-private.hh"
#include "hb-unicode-private.hh"
-HB_BEGIN_DECLS
ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
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 */
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];
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);
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);
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);
};
HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var)
-HB_END_DECLS
#endif /* HB_BUFFER_PRIVATE_HH */
#include <string.h>
-HB_BEGIN_DECLS
#ifndef HB_DEBUG_BUFFER
#define HB_DEBUG_BUFFER (HB_DEBUG+0)
#endif
-
-static hb_buffer_t _hb_buffer_nil = {
- HB_OBJECT_HEADER_STATIC,
-
- &_hb_unicode_funcs_default,
- {
- HB_DIRECTION_INVALID,
- HB_SCRIPT_INVALID,
- NULL,
- },
-
- TRUE, /* in_error */
- TRUE, /* have_output */
- TRUE /* have_positions */
-};
+#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default))
+#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID }
/* Here is how the buffer works internally:
*
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;
done:
if (unlikely (!new_pos || !new_info))
- in_error = TRUE;
+ in_error = true;
if (likely (new_pos))
pos = new_pos;
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)
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;
}
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;
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;
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);
}
if (unlikely (in_error)) return;
assert (have_output);
+ have_output = false;
if (out_info != info)
{
}
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;
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;
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)
{
/* Public API */
hb_buffer_t *
-hb_buffer_create (unsigned int pre_alloc_size)
+hb_buffer_create ()
{
hb_buffer_t *buffer;
if (!(buffer = hb_object_create<hb_buffer_t> ()))
- return &_hb_buffer_nil;
+ return hb_buffer_get_empty ();
buffer->reset ();
- if (pre_alloc_size && !buffer->ensure (pre_alloc_size)) {
- hb_buffer_destroy (buffer);
- return &_hb_buffer_nil;
- }
-
return buffer;
}
hb_buffer_t *
hb_buffer_get_empty (void)
{
- return &_hb_buffer_nil;
+ static const hb_buffer_t _hb_buffer_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ _HB_BUFFER_UNICODE_FUNCS_DEFAULT,
+ _HB_BUFFER_PROPS_DEFAULT,
+
+ true, /* in_error */
+ true, /* have_output */
+ true /* have_positions */
+ };
+
+ return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
}
hb_buffer_t *
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 *
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);
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) {
}
buffer->len = length;
- return TRUE;
+ return true;
}
unsigned int
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) { \
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);
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);
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);
}
-HB_END_DECLS
* Google Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_BUFFER_H
#define HB_BUFFER_H
hb_buffer_t *
-hb_buffer_create (unsigned int pre_alloc_size);
+hb_buffer_create (void);
hb_buffer_t *
hb_buffer_get_empty (void);
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,
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);
void
hb_buffer_reverse_clusters (hb_buffer_t *buffer);
+void
+hb_buffer_guess_properties (hb_buffer_t *buffer);
+
/* Filling the buffer in */
void
hb_buffer_add_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 */
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_CACHE_PRIVATE_HH
+#define HB_CACHE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* Implements a lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+ ASSERT_STATIC (key_bits >= cache_bits);
+ ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int));
+
+ inline void clear (void)
+ {
+ memset (values, 255, sizeof (values));
+ }
+
+ inline bool get (unsigned int key, unsigned int *value)
+ {
+ unsigned int k = key & ((1<<cache_bits)-1);
+ unsigned int v = values[k];
+ if ((v >> value_bits) != (key >> cache_bits))
+ return false;
+ }
+
+ inline bool set (unsigned int key, unsigned int value)
+ {
+ if (unlikely ((key >> key_bits) || (value >> value_bits)))
+ return false; /* Overflows */
+ unsigned int k = key & ((1<<cache_bits)-1);
+ unsigned int v = ((key>>cache_bits)<<value_bits) | value;
+ values[k] = v;
+ return true;
+ }
+
+ private:
+ unsigned int values[1<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_PRIVATE_HH */
/*
* Copyright © 2009,2010 Red Hat, Inc.
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#include <locale.h>
-HB_BEGIN_DECLS
/* hb_tag_t */
hb_tag_t
-hb_tag_from_string (const char *s)
+hb_tag_from_string (const char *s, int len)
{
char tag[4];
unsigned int i;
- if (!s || !*s)
+ if (!s || !len || !*s)
return HB_TAG_NONE;
- for (i = 0; i < 4 && s[i]; i++)
+ if (len < 0 || len > 4)
+ len = 4;
+ for (i = 0; i < (unsigned) len && s[i]; i++)
tag[i] = s[i];
for (; i < 4; i++)
tag[i] = ' ';
};
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
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;
}
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";
}
};
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
struct hb_language_item_t {
+ struct hb_language_item_t *next;
hb_language_t lang;
inline bool operator == (const char *s) const {
void finish (void) { free (lang); }
};
-static hb_static_mutex_t langs_lock;
-static hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> langs;
+
+/* Thread-safe lock-free language list */
+
+static hb_language_item_t *langs;
+
+static
+void free_langs (void)
+{
+ while (langs) {
+ hb_language_item_t *next = langs->next;
+ langs->finish ();
+ free (langs);
+ langs = next;
+ }
+}
+
+static hb_language_item_t *
+lang_find_or_insert (const char *key)
+{
+retry:
+ hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
+
+ for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
+ if (*lang == key)
+ return lang;
+
+ /* Not found; allocate one. */
+ hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
+ if (unlikely (!lang))
+ return NULL;
+ lang->next = first_lang;
+ *lang = key;
+
+ if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
+ free (lang);
+ goto retry;
+ }
+
+#ifdef HAVE_ATEXIT
+ if (!first_lang)
+ atexit (free_langs); /* First person registers atexit() callback. */
+#endif
+
+ return lang;
+}
+
hb_language_t
-hb_language_from_string (const char *str)
+hb_language_from_string (const char *str, int len)
{
- if (!str || !*str)
+ if (!str || !len || !*str)
return HB_LANGUAGE_INVALID;
- hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
+ char strbuf[32];
+ if (len >= 0) {
+ len = MIN (len, (int) sizeof (strbuf) - 1);
+ str = (char *) memcpy (strbuf, str, len);
+ strbuf[len] = '\0';
+ }
+
+ hb_language_item_t *item = lang_find_or_insert (str);
return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
}
/* 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;
}
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
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:
/* 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;
}
/* 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);
}
}
-HB_END_DECLS
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_COMMON_H
#define HB_COMMON_H
#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);
/* 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;
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);
/* User data */
typedef struct _hb_user_data_key_t {
+ /*< private >*/
char unused;
} hb_user_data_key_t;
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
#include "hb-font.h"
#include "hb-object-private.hh"
-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;
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;
};
struct _hb_font_t {
hb_object_header_t header;
+ ASSERT_POD ();
hb_bool_t immutable;
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 */
#include "hb-font-private.hh"
#include "hb-blob.h"
#include "hb-open-file-private.hh"
+#include "hb-ot-head-table.hh"
+
+#include "hb-cache-private.hh"
#include <string.h>
-HB_BEGIN_DECLS
/*
*/
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,
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)
}
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)
}
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,
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,
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,
}
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,
}
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,
}
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,
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,
hb_font_funcs_t *ffuncs;
if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
- return &_hb_font_funcs_nil;
+ return hb_font_funcs_get_empty ();
ffuncs->get = _hb_font_funcs_nil.get;
hb_font_funcs_t *
hb_font_funcs_get_empty (void)
{
- return &_hb_font_funcs_nil;
+ return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil);
}
hb_font_funcs_t *
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 *
if (hb_object_is_inert (ffuncs))
return;
- ffuncs->immutable = TRUE;
+ ffuncs->immutable = true;
}
hb_bool_t
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); \
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 */
* hb_face_t
*/
-static hb_face_t _hb_face_nil = {
+static const hb_face_t _hb_face_nil = {
HB_OBJECT_HEADER_STATIC,
- TRUE, /* immutable */
+ true, /* immutable */
- NULL, /* get_table */
+ NULL, /* reference_table */
NULL, /* user_data */
NULL, /* destroy */
NULL, /* ot_layout */
- 1000
+ 0, /* index */
+ 1000 /* upem */
};
hb_face_t *
-hb_face_create_for_tables (hb_get_table_func_t get_table,
- void *user_data,
- hb_destroy_func_t destroy)
+hb_face_create_for_tables (hb_reference_table_func_t reference_table,
+ void *user_data,
+ hb_destroy_func_t destroy)
{
hb_face_t *face;
- if (!get_table || !(face = hb_object_create<hb_face_t> ())) {
+ if (!reference_table || !(face = hb_object_create<hb_face_t> ())) {
if (destroy)
destroy (user_data);
- return &_hb_face_nil;
+ return hb_face_get_empty ();
}
- face->get_table = get_table;
+ face->reference_table = reference_table;
face->user_data = user_data;
face->destroy = destroy;
face->ot_layout = _hb_ot_layout_create (face);
- face->upem = _hb_ot_layout_get_upem (face);
+ face->upem = 0;
return face;
}
}
static hb_blob_t *
-_hb_face_for_data_get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
{
hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data;
+ if (tag == HB_TAG_NONE)
+ return hb_blob_reference (data->blob);
+
const OpenTypeFontFile &ot_file = *Sanitizer<OpenTypeFontFile>::lock_instance (data->blob);
const OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
hb_face_create (hb_blob_t *blob,
unsigned int index)
{
+ hb_face_t *face;
+
if (unlikely (!blob || !hb_blob_get_length (blob)))
- return &_hb_face_nil;
+ return hb_face_get_empty ();
hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
if (unlikely (!closure))
- return &_hb_face_nil;
+ return hb_face_get_empty ();
- return hb_face_create_for_tables (_hb_face_for_data_get_table,
+ face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
closure,
(hb_destroy_func_t) _hb_face_for_data_closure_destroy);
+
+ hb_face_set_index (face, index);
+
+ return face;
}
hb_face_t *
hb_face_get_empty (void)
{
- return &_hb_face_nil;
+ return const_cast<hb_face_t *> (&_hb_face_nil);
}
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 *
{
hb_blob_t *blob;
- if (unlikely (!face || !face->get_table))
+ if (unlikely (!face || !face->reference_table))
return hb_blob_get_empty ();
- blob = face->get_table (face, tag, face->user_data);
+ blob = face->reference_table (face, tag, face->user_data);
if (unlikely (!blob))
return hb_blob_get_empty ();
return blob;
}
-unsigned int
-hb_face_get_upem (hb_face_t *face)
+hb_blob_t *
+hb_face_reference_blob (hb_face_t *face)
{
- return _hb_ot_layout_get_upem (face);
+ return hb_face_reference_table (face, HB_TAG_NONE);
}
+void
+hb_face_set_index (hb_face_t *face,
+ unsigned int index)
+{
+ if (hb_object_is_inert (face))
+ return;
-/*
- * hb_font_t
- */
+ face->index = index;
+}
-static hb_font_t _hb_font_nil = {
- HB_OBJECT_HEADER_STATIC,
+unsigned int
+hb_face_get_index (hb_face_t *face)
+{
+ return face->index;
+}
- TRUE, /* immutable */
+void
+hb_face_set_upem (hb_face_t *face,
+ unsigned int upem)
+{
+ if (hb_object_is_inert (face))
+ return;
- NULL, /* parent */
- &_hb_face_nil,
+ face->upem = upem;
+}
- 0, /* x_scale */
- 0, /* y_scale */
+unsigned int
+hb_face_get_upem (hb_face_t *face)
+{
+ if (unlikely (!face->upem)) {
+ hb_blob_t *head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
+ const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+ face->upem = head_table->get_upem ();
+ hb_blob_destroy (head_blob);
+ }
+ return face->upem;
+}
- 0, /* x_ppem */
- 0, /* y_ppem */
- &_hb_font_funcs_nil, /* klass */
- NULL, /* user_data */
- NULL /* destroy */
-};
+/*
+ * hb_font_t
+ */
hb_font_t *
hb_font_create (hb_face_t *face)
hb_font_t *font;
if (unlikely (!face))
- face = &_hb_face_nil;
+ face = hb_face_get_empty ();
if (unlikely (hb_object_is_inert (face)))
- return &_hb_font_nil;
+ return hb_font_get_empty ();
if (!(font = hb_object_create<hb_font_t> ()))
- return &_hb_font_nil;
+ return hb_font_get_empty ();
hb_face_make_immutable (face);
font->face = hb_face_reference (face);
- font->klass = &_hb_font_funcs_nil;
+ font->klass = hb_font_funcs_get_empty ();
return font;
}
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);
font->x_ppem = parent->x_ppem;
font->y_ppem = parent->y_ppem;
- font->klass = &_hb_font_funcs_nil;
-
return font;
}
hb_font_t *
hb_font_get_empty (void)
{
- return &_hb_font_nil;
+ static const hb_font_t _hb_font_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ true, /* immutable */
+
+ NULL, /* parent */
+ const_cast<hb_face_t *> (&_hb_face_nil),
+
+ 0, /* x_scale */
+ 0, /* y_scale */
+
+ 0, /* x_ppem */
+ 0, /* y_ppem */
+
+ const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
+ NULL, /* user_data */
+ NULL /* destroy */
+ };
+
+ return const_cast<hb_font_t *> (&_hb_font_nil);
}
hb_font_t *
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 *
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);
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,
}
-HB_END_DECLS
* Red Hat Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_FONT_H
#define HB_FONT_H
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);
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 *
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);
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 *
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
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 */
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 */
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 *
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,
#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:
*
* - 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?
*/
if (unlikely (variation_selector)) {
*glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
if (*glyph)
- return TRUE;
+ return true;
}
#endif
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
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
void *user_data HB_UNUSED)
{
/* We always work in the horizontal coordinates. */
- return TRUE;
+ return true;
}
static hb_bool_t
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
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
int load_flags = FT_LOAD_DEFAULT;
if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
- return FALSE;
+ return false;
if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
- return FALSE;
+ return false;
if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
- return FALSE;
+ return false;
*x = ft_face->glyph->outline.points[point_index].x;
*y = ft_face->glyph->outline.points[point_index].y;
- return TRUE;
+ return true;
}
-static hb_font_funcs_t ft_ffuncs = {
- HB_OBJECT_HEADER_STATIC,
+static hb_bool_t
+hb_ft_get_glyph_name (hb_font_t *font,
+ void *font_data,
+ hb_codepoint_t glyph,
+ char *name, unsigned int size,
+ void *user_data HB_UNUSED)
+{
+ FT_Face ft_face = (FT_Face) font_data;
- TRUE, /* immutable */
+ hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
+ if (!ret)
+ snprintf (name, size, "gid%u", glyph);
- {
- hb_ft_get_glyph,
- hb_ft_get_glyph_h_advance,
- hb_ft_get_glyph_v_advance,
- hb_ft_get_glyph_h_origin,
- hb_ft_get_glyph_v_origin,
- hb_ft_get_glyph_h_kerning,
- hb_ft_get_glyph_v_kerning,
- hb_ft_get_glyph_extents,
- hb_ft_get_glyph_contour_point,
+ return ret;
+}
+
+static hb_bool_t
+hb_ft_get_glyph_from_name (hb_font_t *font,
+ void *font_data,
+ const char *name, int len, /* -1 means nul-terminated */
+ hb_codepoint_t *glyph,
+ void *user_data HB_UNUSED)
+{
+ FT_Face ft_face = (FT_Face) font_data;
+
+ if (len < 0)
+ *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name);
+ else {
+ /* Make a nul-terminated version. */
+ char buf[128];
+ len = MIN (len, (int) sizeof (buf) - 1);
+ strncpy (buf, name, len);
+ buf[len] = '\0';
+ *glyph = FT_Get_Name_Index (ft_face, buf);
}
-};
-hb_font_funcs_t *
-hb_ft_get_font_funcs (void)
+ return *glyph != 0;
+}
+
+
+static hb_font_funcs_t *
+_hb_ft_get_font_funcs (void)
{
- return &ft_ffuncs;
+ static const hb_font_funcs_t ft_ffuncs = {
+ HB_OBJECT_HEADER_STATIC,
+
+ true, /* immutable */
+
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ }
+ };
+
+ return const_cast<hb_font_funcs_t *> (&ft_ffuncs);
}
static hb_blob_t *
-get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
{
FT_Face ft_face = (FT_Face) user_data;
FT_Byte *buffer;
FT_ULong length = 0;
FT_Error error;
- if (unlikely (tag == HB_TAG_NONE))
- return NULL;
+ /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
if (error)
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;
}
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,
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);
}
-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;
+}
#include "hb.h"
-#include "hb-font.h"
-
#include <ft2build.h>
#include FT_FREETYPE_H
/* 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);
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 */
#include "hb-unicode-private.hh"
-#include <glib.h>
-
-HB_BEGIN_DECLS
#if !GLIB_CHECK_VERSION(2,29,14)
static const hb_script_t
/* 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
* sees it and makes sure it's compilable. */
if (!a || !b)
- return FALSE;
+ return false;
gchar utf8[12];
gchar *normalized;
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);
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;
*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. */
/* 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);
}
-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
hb_unicode_funcs_t *
hb_glib_get_unicode_funcs (void)
{
- return &_hb_glib_unicode_funcs;
+ return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
}
-
-HB_END_DECLS
#define HB_GLIB_H
#include "hb.h"
+
#include <glib.h>
HB_BEGIN_DECLS
--- /dev/null
+/*** BEGIN file-header ***/
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+
+/* g++ didn't like older gtype.h gcc-only code path. */
+#include <glib.h>
+#if !GLIB_CHECK_VERSION(2,29,16)
+#undef __GNUC__
+#undef __GNUC_MINOR__
+#define __GNUC__ 2
+#define __GNUC_MINOR__ 6
+#endif
+
+#include "hb-gobject.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+inline static /* TODO(behdad) disable these for now until we fix them... */
+GType
+@enum_name@_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ GType g_define_type_id =
+ g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+
+/* g++ didn't like older gtype.h gcc-only code path. */
+#include <glib.h>
+#if !GLIB_CHECK_VERSION(2,29,16)
+#undef __GNUC__
+#undef __GNUC_MINOR__
+#define __GNUC__ 2
+#define __GNUC_MINOR__ 6
+#endif
+
+#include "hb-gobject.h"
+
+#define _HB_DEFINE_BOXED_TYPE(Name,underscore_name,copy_func,free_func) \
+GType \
+underscore_name##_get_type (void) \
+{ \
+ static volatile gsize type = 0; \
+ if (g_once_init_enter (&type)) { \
+ GType t = g_boxed_type_register_static (g_intern_static_string (#Name), \
+ (GBoxedCopyFunc) copy_func, \
+ (GBoxedFreeFunc) free_func); \
+ g_once_init_leave (&type, t); \
+ } \
+ return type; \
+}
+
+#define HB_DEFINE_BOXED_TYPE(name) \
+ _HB_DEFINE_BOXED_TYPE (hb_##name, hb_gobject_##name, hb_##name##_reference, hb_##name##_destroy);
+
+HB_DEFINE_BOXED_TYPE (buffer)
+HB_DEFINE_BOXED_TYPE (blob)
+HB_DEFINE_BOXED_TYPE (face)
+HB_DEFINE_BOXED_TYPE (font)
+HB_DEFINE_BOXED_TYPE (font_funcs)
+HB_DEFINE_BOXED_TYPE (unicode_funcs)
+
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_GOBJECT_H
+#define HB_GOBJECT_H
+
+#include "hb.h"
+
+#include <glib-object.h>
+
+HB_BEGIN_DECLS
+
+
+/* Objects */
+
+#define HB_GOBJECT_TYPE_BLOB hb_gobject_blob_get_type ()
+GType
+hb_gobject_blob_get_type (void);
+
+#define HB_GOBJECT_TYPE_BUFFER hb_gobject_buffer_get_type ()
+GType
+hb_gobject_buffer_get_type (void);
+
+#define HB_GOBJECT_TYPE_FACE hb_gobject_face_get_type ()
+GType
+hb_gobject_face_get_type (void);
+
+#define HB_GOBJECT_TYPE_FONT hb_gobject_font_get_type ()
+GType
+hb_gobject_font_get_type (void);
+
+#define HB_GOBJECT_TYPE_FONT_FUNCS hb_gobject_font_funcs_get_type ()
+GType
+hb_gobject_font_funcs_get_type (void);
+
+#define HB_GOBJECT_TYPE_UNICODE_FUNCS hb_gobject_unicode_funcs_get_type ()
+GType
+hb_gobject_unicode_funcs_get_type (void);
+
+
+/* Enums */
+
+
+HB_END_DECLS
+
+#endif /* HB_GOBJECT_H */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * Copyright © 2011 Martin Hosken
+ * Copyright © 2011 SIL International
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+
+#include "hb-graphite2.h"
+
+#include "hb-buffer-private.hh"
+#include "hb-font-private.hh"
+#include "hb-ot-tag.h"
+
+#include <graphite2/Font.h>
+#include <graphite2/Segment.h>
+
+
+struct hb_gr_cluster_t {
+ unsigned int base_char;
+ unsigned int num_chars;
+ unsigned int base_glyph;
+ unsigned int num_glyphs;
+};
+
+
+typedef struct hb_gr_tablelist_t {
+ hb_blob_t *blob;
+ struct hb_gr_tablelist_t *next;
+ unsigned int tag;
+} hb_gr_tablelist_t;
+
+static struct hb_gr_face_data_t {
+ hb_face_t *face;
+ gr_face *grface;
+ hb_gr_tablelist_t *tlist;
+} _hb_gr_face_data_nil = {NULL, NULL};
+
+static struct hb_gr_font_data_t {
+ gr_font *grfont;
+ gr_face *grface;
+} _hb_gr_font_data_nil = {NULL, NULL};
+
+
+static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len)
+{
+ hb_gr_tablelist_t *pl = NULL, *p;
+ hb_gr_face_data_t *face = (hb_gr_face_data_t *) data;
+ hb_gr_tablelist_t *tlist = face->tlist;
+
+ for (p = tlist; p; p = p->next)
+ if (p->tag == tag ) {
+ unsigned int tlen;
+ const char *d = hb_blob_get_data (p->blob, &tlen);
+ *len = tlen;
+ return d;
+ } else
+ pl = p;
+
+ if (!face->face)
+ return NULL;
+ hb_blob_t *blob = hb_face_reference_table (face->face, tag);
+
+ if (!pl || pl->blob)
+ {
+ p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t));
+ if (!p) {
+ hb_blob_destroy (blob);
+ return NULL;
+ }
+ p->next = NULL;
+ if (pl)
+ pl->next = p;
+ else
+ face->tlist = p;
+ pl = p;
+ }
+ pl->blob = blob;
+ pl->tag = tag;
+
+ unsigned int tlen;
+ const char *d = hb_blob_get_data (blob, &tlen);
+ *len = tlen;
+ return d;
+}
+
+static float hb_gr_get_advance (const void *hb_font, unsigned short gid)
+{
+ return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid);
+}
+
+static void _hb_gr_face_data_destroy (void *data)
+{
+ hb_gr_face_data_t *f = (hb_gr_face_data_t *) data;
+ hb_gr_tablelist_t *tlist = f->tlist;
+ while (tlist)
+ {
+ hb_gr_tablelist_t *old = tlist;
+ hb_blob_destroy (tlist->blob);
+ tlist = tlist->next;
+ free (old);
+ }
+ gr_face_destroy (f->grface);
+}
+
+static void _hb_gr_font_data_destroy (void *data)
+{
+ hb_gr_font_data_t *f = (hb_gr_font_data_t *) data;
+
+ gr_font_destroy (f->grfont);
+ free (f);
+}
+
+static hb_user_data_key_t hb_gr_data_key;
+
+static hb_gr_face_data_t *
+_hb_gr_face_get_data (hb_face_t *face)
+{
+ hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
+ if (likely (data)) return data;
+
+ data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t));
+ if (unlikely (!data))
+ return &_hb_gr_face_data_nil;
+
+
+ hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf);
+ if (!hb_blob_get_length (silf_blob))
+ {
+ hb_blob_destroy (silf_blob);
+ return &_hb_gr_face_data_nil;
+ }
+
+ data->face = face;
+ data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default);
+
+
+ if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data,
+ (hb_destroy_func_t) _hb_gr_face_data_destroy,
+ false)))
+ {
+ _hb_gr_face_data_destroy (data);
+ data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
+ if (data)
+ return data;
+ else
+ return &_hb_gr_face_data_nil;
+ }
+
+ return data;
+}
+
+static hb_gr_font_data_t *
+_hb_gr_font_get_data (hb_font_t *font)
+{
+ hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
+ if (likely (data)) return data;
+
+ data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t));
+ if (unlikely (!data))
+ return &_hb_gr_font_data_nil;
+
+
+ hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf);
+ if (!hb_blob_get_length (silf_blob))
+ {
+ hb_blob_destroy (silf_blob);
+ return &_hb_gr_font_data_nil;
+ }
+
+ data->grface = _hb_gr_face_get_data (font->face)->grface;
+ int scale;
+ hb_font_get_scale (font, &scale, NULL);
+ data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance, data->grface);
+
+
+ if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data,
+ (hb_destroy_func_t) _hb_gr_font_data_destroy,
+ false)))
+ {
+ _hb_gr_font_data_destroy (data);
+ data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
+ if (data)
+ return data;
+ else
+ return &_hb_gr_font_data_nil;
+ }
+
+ return data;
+}
+
+
+hb_bool_t
+_hb_graphite_shape (hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features)
+{
+
+ buffer->guess_properties ();
+
+ /* XXX We do a hell of a lot of stuff just to figure out this font
+ * is not graphite! Shouldn't do. */
+
+ hb_gr_font_data_t *data = _hb_gr_font_get_data (font);
+ if (!data->grface) return false;
+
+ unsigned int charlen;
+ hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen);
+
+ int success = 0;
+
+ if (!charlen) return true;
+
+ const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
+ const char *lang_end = strchr (lang, '-');
+ int lang_len = lang_end ? lang_end - lang : -1;
+ gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
+
+ while (num_features--)
+ {
+ const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag);
+ if (fref)
+ gr_fref_set_feature_value (fref, features->value, feats);
+ features++;
+ }
+
+ hb_codepoint_t *gids = NULL, *pg;
+ hb_gr_cluster_t *clusters = NULL;
+ gr_segment *seg = NULL;
+ uint32_t *text = NULL;
+ const gr_slot *is;
+ unsigned int ci = 0, ic = 0;
+ float curradvx = 0., curradvy = 0.;
+ unsigned int glyphlen = 0;
+ unsigned int *p;
+
+ text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t));
+ if (!text) goto dieout;
+
+ p = text;
+ for (unsigned int i = 0; i < charlen; ++i)
+ *p++ = bufferi++->codepoint;
+ *p = 0;
+
+ hb_tag_t script_tag[2];
+ hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
+
+ seg = gr_make_seg (data->grfont, data->grface,
+ script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
+ feats,
+ gr_utf32, text, charlen,
+ 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
+ if (!seg) goto dieout;
+
+ glyphlen = gr_seg_n_slots (seg);
+ clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t));
+ if (!glyphlen || !clusters) goto dieout;
+
+ gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t));
+ if (!gids) goto dieout;
+
+ pg = gids;
+ for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
+ {
+ unsigned int before = gr_slot_before (is);
+ unsigned int after = gr_slot_after (is);
+ *pg = gr_slot_gid (is);
+ pg++;
+ while (clusters[ci].base_char > before && ci)
+ {
+ clusters[ci-1].num_chars += clusters[ci].num_chars;
+ clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
+ ci--;
+ }
+
+ if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
+ {
+ hb_gr_cluster_t *c = clusters + ci + 1;
+ c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
+ c->num_chars = before - c->base_char;
+ c->base_glyph = ic;
+ c->num_glyphs = 0;
+ ci++;
+ }
+ clusters[ci].num_glyphs++;
+
+ if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
+ clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
+ }
+ ci++;
+
+ buffer->clear_output ();
+ for (unsigned int i = 0; i < ci; ++i)
+ buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph);
+ buffer->swap_buffers ();
+
+ hb_glyph_position_t *pPos;
+ for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
+ is; pPos++, is = gr_slot_next_in_segment (is))
+ {
+ pPos->x_offset = gr_slot_origin_X(is) - curradvx;
+ pPos->y_offset = gr_slot_origin_Y(is) - curradvy;
+ pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont);
+ pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont);
+// if (pPos->x_advance < 0 && gr_slot_attached_to(is))
+// pPos->x_advance = 0;
+ curradvx += pPos->x_advance;
+ curradvy += pPos->y_advance;
+ }
+ pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
+
+ /* TODO(behdad):
+ * This shaper is badly broken with RTL text. It returns glyphs
+ * in the logical order!
+ */
+// if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+// hb_buffer_reverse (buffer);
+
+ success = 1;
+
+dieout:
+ if (gids) free (gids);
+ if (clusters) free (clusters);
+ if (seg) gr_seg_destroy (seg);
+ if (text) free (text);
+ return success;
+}
--- /dev/null
+/*
+ * 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 */
#include <unicode/uversion.h>
#include <unicode/uchar.h>
#include <unicode/unorm.h>
-#include <unicode/unistr.h>
+#include <unicode/ustring.h>
-HB_BEGIN_DECLS
hb_script_t
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
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);
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;
void *user_data HB_UNUSED)
{
UChar utf16[2], normalized[20];
- gint len;
+ int len;
hb_bool_t ret, err;
UErrorCode icu_err;
/* 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);
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. */
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
hb_unicode_funcs_t *
hb_icu_get_unicode_funcs (void)
{
- return &_hb_icu_unicode_funcs;
+ return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
}
-HB_END_DECLS
#define HB_ICU_H
#include "hb.h"
+
#include <unicode/uscript.h>
/*
* Copyright © 2007 Chris Wilson
* Copyright © 2009,2010 Red Hat, Inc.
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#include "hb-private.hh"
-HB_BEGIN_DECLS
-
/* mutex */
/* We need external help for these */
-#ifdef HAVE_GLIB
+#if 0
+
+
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+
+#include <windows.h>
+typedef CRITICAL_SECTION hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 }
+#define hb_mutex_impl_init(M) InitializeCriticalSection (M)
+#define hb_mutex_impl_lock(M) EnterCriticalSection (M)
+#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M)
+#define hb_mutex_impl_finish(M) DeleteCriticalSection (M)
-#include <glib.h>
+#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__))
+
+#include <pthread.h>
+typedef pthread_mutex_t hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER
+#define hb_mutex_impl_init(M) pthread_mutex_init (M, NULL)
+#define hb_mutex_impl_lock(M) pthread_mutex_lock (M)
+#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M)
+#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M)
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
typedef GStaticMutex hb_mutex_impl_t;
#define HB_MUTEX_IMPL_INIT G_STATIC_MUTEX_INIT
#define hb_mutex_impl_init(M) g_static_mutex_init (M)
#define hb_mutex_impl_lock(M) g_static_mutex_lock (M)
#define hb_mutex_impl_unlock(M) g_static_mutex_unlock (M)
-#define hb_mutex_impl_free(M) g_static_mutex_free (M)
+#define hb_mutex_impl_finish(M) g_static_mutex_free (M)
-#elif defined(_MSC_VER)
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
-#include <Windows.h>
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
-typedef CRITICAL_SECTION hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 }
-#define hb_mutex_impl_init(M) InitializeCriticalSection (M)
-#define hb_mutex_impl_lock(M) EnterCriticalSection (M)
-#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M)
-#define hb_mutex_impl_free(M) DeleteCriticalSection (M)
+/* This actually is not a totally awful implementation. */
+typedef volatile int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT 0
+#define hb_mutex_impl_init(M) *(M) = 0
+#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END
+#define hb_mutex_impl_unlock(M) __sync_lock_release (M)
+#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END
-#else
+#elif !defined(HB_NO_MT)
-#warning "Could not find any system to define platform macros, library will NOT be thread-safe"
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
+#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */
typedef volatile int hb_mutex_impl_t;
#define HB_MUTEX_IMPL_INIT 0
-#define hb_mutex_impl_init(M) ((void) (*(M) = 0))
-#define hb_mutex_impl_lock(M) ((void) (*(M) = 1))
-#define hb_mutex_impl_unlock(M) ((void) (*(M) = 0))
-#define hb_mutex_impl_free(M) ((void) (*(M) = 2))
+#define hb_mutex_impl_init(M) *(M) = 0
+#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END
+#define hb_mutex_impl_unlock(M) (*(M))--;
+#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END
+
+#else /* HB_NO_MT */
+
+typedef int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT 0
+#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END
#endif
+#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT}
struct hb_mutex_t
{
+ /* TODO Add tracing. */
+
hb_mutex_impl_t m;
inline void init (void) { hb_mutex_impl_init (&m); }
inline void lock (void) { hb_mutex_impl_lock (&m); }
inline void unlock (void) { hb_mutex_impl_unlock (&m); }
- inline void free (void) { hb_mutex_impl_free (&m); }
-};
-
-#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT}
-#define hb_mutex_init(M) (M)->init ()
-#define hb_mutex_lock(M) (M)->lock ()
-#define hb_mutex_unlock(M) (M)->unlock ()
-#define hb_mutex_free(M) (M)->free ()
-
-
-struct hb_static_mutex_t : hb_mutex_t
-{
- hb_static_mutex_t (void) { this->init (); }
- ~hb_static_mutex_t (void) { this->free (); }
-
- private:
- NO_COPY (hb_static_mutex_t);
+ inline void finish (void) { hb_mutex_impl_finish (&m); }
};
-HB_END_DECLS
-
#endif /* HB_MUTEX_PRIVATE_HH */
/*
* Copyright © 2007 Chris Wilson
* Copyright © 2009,2010 Red Hat, Inc.
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#include "hb-private.hh"
+#include "hb-atomic-private.hh"
#include "hb-mutex-private.hh"
-HB_BEGIN_DECLS
-
/* Debug */
#endif
-/* atomic_int */
-
-/* We need external help for these */
-
-#ifdef HAVE_GLIB
-
-#include <glib.h>
-
-typedef volatile int hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V) g_atomic_int_exchange_and_add (&(AI), V)
-#define hb_atomic_int_get(AI) g_atomic_int_get (&(AI))
-#define hb_atomic_int_set(AI, V) g_atomic_int_set (&(AI), V)
-
-
-#elif defined(_MSC_VER)
-
-#include <intrin.h>
-
-typedef long hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V) _InterlockedExchangeAdd (&(AI), V)
-#define hb_atomic_int_get(AI) (_ReadBarrier (), (AI))
-#define hb_atomic_int_set(AI, V) ((void) _InterlockedExchange (&(AI), (V)))
-
-
-#else
-
-#warning "Could not find any system to define atomic_int macros, library will NOT be thread-safe"
-
-typedef volatile int hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V) ((AI) += (V), (AI) - (V))
-#define hb_atomic_int_get(AI) (AI)
-#define hb_atomic_int_set(AI, V) ((void) ((AI) = (V)))
-
-
-#endif
-
-
-
-
/* reference_count */
-typedef struct {
- hb_atomic_int_t ref_count;
-
#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+struct hb_reference_count_t
+{
+ hb_atomic_int_t ref_count;
- inline void init (int v) { ref_count = v; /* non-atomic is fine */ }
- inline int inc (void) { return hb_atomic_int_fetch_and_add (ref_count, 1); }
- inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); }
- inline void set (int v) { hb_atomic_int_set (ref_count, v); }
+ inline void init (int v) { ref_count = v; }
+ inline int inc (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count), 1); }
+ inline int dec (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count), -1); }
+ inline void finish (void) { ref_count = HB_REFERENCE_COUNT_INVALID_VALUE; }
- inline int get (void) const { return hb_atomic_int_get (ref_count); }
- inline bool is_invalid (void) const { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; }
+ inline bool is_invalid (void) const { return ref_count == HB_REFERENCE_COUNT_INVALID_VALUE; }
-} hb_reference_count_t;
+};
/* user_data */
-struct hb_user_data_array_t {
+#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT}
+struct hb_user_data_array_t
+{
+ /* TODO Add tracing. */
struct hb_user_data_item_t {
hb_user_data_key_t *key;
void finish (void) { if (destroy) destroy (data); }
};
- hb_lockable_set_t<hb_user_data_item_t, hb_static_mutex_t> items;
+ hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items;
+
+ inline void init (void) { items.init (); }
HB_INTERNAL bool set (hb_user_data_key_t *key,
void * data,
- hb_destroy_func_t destroy);
+ hb_destroy_func_t destroy,
+ hb_bool_t replace,
+ hb_mutex_t &lock);
- HB_INTERNAL void *get (hb_user_data_key_t *key);
+ HB_INTERNAL void *get (hb_user_data_key_t *key,
+ hb_mutex_t &lock);
- HB_INTERNAL void finish (void);
+ HB_INTERNAL void finish (hb_mutex_t &lock);
};
/* object_header */
-typedef struct _hb_object_header_t hb_object_header_t;
-
-struct _hb_object_header_t {
+struct hb_object_header_t
+{
hb_reference_count_t ref_count;
+ hb_mutex_t lock;
hb_user_data_array_t user_data;
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT}
static inline void *create (unsigned int size) {
hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
inline void init (void) {
ref_count.init (1);
+ lock.init ();
+ user_data.init ();
}
inline bool is_inert (void) const {
if (ref_count.dec () != 1)
return false;
- ref_count.init (HB_REFERENCE_COUNT_INVALID_VALUE);
-
- user_data.finish ();
+ ref_count.finish (); /* Do this before user_data */
+ user_data.finish (lock);
+ lock.finish ();
return true;
}
inline bool set_user_data (hb_user_data_key_t *key,
void * data,
- hb_destroy_func_t destroy_func) {
+ hb_destroy_func_t destroy_func,
+ hb_bool_t replace) {
if (unlikely (!this || this->is_inert ()))
return false;
- return user_data.set (key, data, destroy_func);
+ return user_data.set (key, data, destroy_func, replace, lock);
}
inline void *get_user_data (hb_user_data_key_t *key) {
- return user_data.get (key);
+ if (unlikely (!this || this->is_inert ()))
+ return NULL;
+
+ return user_data.get (key, lock);
}
inline void trace (const char *function) const {
+ if (unlikely (!this)) return;
+ /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only
+ * prints the class name and throws away the template info. */
DEBUG_MSG (OBJECT, (void *) this,
- "refcount=%d %s",
- this ? ref_count.get () : 0,
- function);
+ "%s refcount=%d",
+ function,
+ this ? ref_count.ref_count : 0);
}
+ private:
+ ASSERT_POD ();
};
-HB_END_DECLS
-
-
/* object */
template <typename Type>
static inline bool hb_object_set_user_data (Type *obj,
hb_user_data_key_t *key,
void * data,
- hb_destroy_func_t destroy)
+ hb_destroy_func_t destroy,
+ hb_bool_t replace)
{
- return obj->header.set_user_data (key, data, destroy);
+ return obj->header.set_user_data (key, data, destroy, replace);
}
template <typename Type>
}
-HB_BEGIN_DECLS
-
-
-HB_END_DECLS
-
#endif /* HB_OBJECT_PRIVATE_HH */
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
+ * Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
*/
#ifndef HB_OPEN_FILE_PRIVATE_HH
#include "hb-open-type-private.hh"
-HB_BEGIN_DECLS
/*
{
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. */
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:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return table.sanitize (c, this);
+ return TRACE_RETURN (table.sanitize (c, this));
}
private:
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);
}
}
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);
}
}
};
-HB_END_DECLS
#endif /* HB_OPEN_FILE_PRIVATE_HH */
/*
* Copyright © 2007,2008,2009,2010 Red Hat, Inc.
+ * Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
*/
#ifndef HB_OPEN_TYPE_PRIVATE_HH
#include "hb-blob.h"
-HB_BEGIN_DECLS
-HB_END_DECLS
/*
*/
/* 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)
#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)
#define TRACE_SANITIZE() \
- hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, NULL, HB_FUNC);
+ hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
struct hb_sanitize_context_t
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;
inline bool check_range (const void *base, unsigned int len) const
{
const char *p = (const char *) base;
- bool ret = this->start <= p &&
- p <= this->end &&
- (unsigned int) (this->end - p) >= len;
-
- DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
- "%-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s",
- this->debug_depth, this->debug_depth,
- p, p + len, len,
- this->start, this->end,
- ret ? "pass" : "FAIL");
- return likely (ret);
+ hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+ "check_range [%p..%p] (%d bytes) in [%p..%p]",
+ p, p + len, len,
+ this->start, this->end);
+
+ return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
}
inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
const char *p = (const char *) base;
bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
- DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
- "%-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
- this->debug_depth, this->debug_depth,
- p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
- this->start, this->end,
- !overflows ? "does not overflow" : "OVERFLOWS FAIL");
+ hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+ "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
+ p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
+ this->start, this->end);
- return likely (!overflows && this->check_range (base, record_size * len));
+ return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
}
template <typename Type>
return likely (this->check_range (obj, obj->min_size));
}
- inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
+ inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
{
const char *p = (const char *) base;
this->edit_count++;
- DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
- "%-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
- this->debug_depth, this->debug_depth,
- this->edit_count,
- p, p + len, len,
- this->start, this->end,
- this->writable ? "granted" : "REJECTED");
+ hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+ "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
+ this->edit_count,
+ p, p + len, len,
+ this->start, this->end);
- return this->writable;
+ return TRACE_RETURN (this->writable);
}
- unsigned int debug_depth;
+ mutable unsigned int debug_depth;
const char *start, *end;
bool writable;
unsigned int edit_count;
retry:
DEBUG_MSG_FUNC (SANITIZE, blob, "start");
- c->setup ();
+ c->start_processing ();
if (unlikely (!c->start)) {
- c->finish ();
+ c->end_processing ();
return blob;
}
}
}
- c->finish ();
+ c->end_processing ();
DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
if (sane)
*/
-template <typename Type, int Bytes> class BEInt;
+template <typename Type, int Bytes> struct BEInt;
/* LONGTERMTODO: On machines allowing unaligned access, we can make the
* following tighter by using byteswap instructions on ints directly. */
template <typename Type>
-class BEInt<Type, 2>
+struct BEInt<Type, 2>
{
public:
inline void set (Type i) { hb_be_uint16_put (v,i); }
private: uint8_t v[2];
};
template <typename Type>
-class BEInt<Type, 4>
+struct BEInt<Type, 4>
{
public:
inline void set (Type i) { hb_be_uint32_put (v,i); }
inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return likely (c->check_struct (this));
+ return TRACE_RETURN (likely (c->check_struct (this)));
}
protected:
BEInt<Type, sizeof (Type)> v;
DEFINE_SIZE_STATIC (sizeof (Type));
};
+/* Typedef these to avoid clash with windows.h */
+#define USHORT HB_USHORT
+#define SHORT HB_SHORT
+#define ULONG HB_ULONG
+#define LONG HB_LONG
typedef IntType<uint16_t> USHORT; /* 16-bit unsigned integer. */
typedef IntType<int16_t> SHORT; /* 16-bit signed integer. */
typedef IntType<uint32_t> ULONG; /* 32-bit unsigned integer. */
typedef IntType<int32_t> LONG; /* 32-bit signed integer. */
+/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */
+typedef SHORT FWORD;
+
+/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */
+typedef USHORT UFWORD;
+
/* Date represented in number of seconds since 12:00 midnight, January 1,
* 1904. The value is represented as a signed 64-bit integer. */
struct LONGDATETIME
{
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return likely (c->check_struct (this));
+ return TRACE_RETURN (likely (c->check_struct (this)));
}
private:
LONG major;
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
USHORT major;
inline bool sanitize (hb_sanitize_context_t *c, void *base) {
TRACE_SANITIZE ();
- if (unlikely (!c->check_struct (this))) return false;
+ if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
unsigned int offset = *this;
- if (unlikely (!offset)) return true;
+ if (unlikely (!offset)) return TRACE_RETURN (true);
Type &obj = StructAtOffset<Type> (base, offset);
- return likely (obj.sanitize (c)) || neuter (c);
+ return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
}
template <typename T>
inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
TRACE_SANITIZE ();
- if (unlikely (!c->check_struct (this))) return false;
+ if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
unsigned int offset = *this;
- if (unlikely (!offset)) return true;
+ if (unlikely (!offset)) return TRACE_RETURN (true);
Type &obj = StructAtOffset<Type> (base, offset);
- return likely (obj.sanitize (c, user_data)) || neuter (c);
+ return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
}
private:
/* Set the offset to Null */
inline bool neuter (hb_sanitize_context_t *c) {
- if (c->can_edit (this, this->static_size)) {
+ if (c->may_edit (this, this->static_size)) {
this->set (0); /* 0 is Null offset */
return true;
}
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
*/
(void) (false && array[0].sanitize (c));
- return true;
+ return TRACE_RETURN (true);
}
inline bool sanitize (hb_sanitize_context_t *c, void *base) {
TRACE_SANITIZE ();
- if (unlikely (!sanitize_shallow (c))) return false;
+ if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (unlikely (!array[i].sanitize (c, base)))
- return false;
- return true;
+ return TRACE_RETURN (false);
+ return TRACE_RETURN (true);
}
template <typename T>
inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
TRACE_SANITIZE ();
- if (unlikely (!sanitize_shallow (c))) return false;
+ if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (unlikely (!array[i].sanitize (c, base, user_data)))
- return false;
- return true;
+ return TRACE_RETURN (false);
+ return TRACE_RETURN (true);
}
private:
inline bool sanitize_shallow (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this)
- && c->check_array (this, Type::static_size, len);
+ return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
}
public:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return OffsetArrayOf<Type>::sanitize (c, this);
+ return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this));
}
template <typename T>
inline bool sanitize (hb_sanitize_context_t *c, T user_data) {
TRACE_SANITIZE ();
- return OffsetArrayOf<Type>::sanitize (c, this, user_data);
+ return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
}
};
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
*/
(void) (false && array[0].sanitize (c));
- return true;
+ return TRACE_RETURN (true);
}
USHORT len;
template <typename SearchType>
inline int search (const SearchType &x) const {
- class Cmp {
- public: static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
+ struct Cmp {
+ static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
};
const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
return p ? p - this->array : -1;
};
-HB_BEGIN_DECLS
-HB_END_DECLS
#endif /* HB_OPEN_TYPE_PRIVATE_HH */
/*
* Copyright © 2010 Red Hat, Inc.
+ * Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
*/
-#ifndef HB_OT_HEAD_PRIVATE_HH
-#define HB_OT_HEAD_PRIVATE_HH
+#ifndef HB_OT_HEAD_TABLE_HH
+#define HB_OT_HEAD_TABLE_HH
#include "hb-open-type-private.hh"
-HB_BEGIN_DECLS
/*
- * head
+ * head -- Font Header
*/
#define HB_OT_TAG_head HB_TAG('h','e','a','d')
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:
};
-HB_END_DECLS
-#endif /* HB_OT_HEAD_PRIVATE_HH */
+#endif /* HB_OT_HEAD_TABLE_HH */
--- /dev/null
+/*
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_HHEA_TABLE_HH
+#define HB_OT_HHEA_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+
+/*
+ * hhea -- The Horizontal Header Table
+ */
+
+#define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
+
+
+struct hhea
+{
+ static const hb_tag_t Tag = HB_OT_TAG_hhea;
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
+ }
+
+ private:
+ FixedVersion version; /* 0x00010000 for version 1.0. */
+ FWORD ascender; /* Typographic ascent. <a
+ * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
+ * (Distance from baseline of highest
+ * ascender)</a> */
+ FWORD descender; /* Typographic descent. <a
+ * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
+ * (Distance from baseline of lowest
+ * descender)</a> */
+ FWORD lineGap; /* Typographic line gap. Negative
+ * LineGap values are treated as zero
+ * in Windows 3.1, System 6, and
+ * System 7. */
+ UFWORD advanceWidthMax; /* Maximum advance width value in
+ * 'hmtx' table. */
+ FWORD minLeftSideBearing; /* Minimum left sidebearing value in
+ * 'hmtx' table. */
+ FWORD minRightSideBearing; /* Minimum right sidebearing value;
+ * calculated as Min(aw - lsb -
+ * (xMax - xMin)). */
+ FWORD xMaxExtent; /* Max(lsb + (xMax - xMin)). */
+ SHORT caretSlopeRise; /* Used to calculate the slope of the
+ * cursor (rise/run); 1 for vertical. */
+ SHORT caretSlopeRun; /* 0 for vertical. */
+ SHORT caretOffset; /* The amount by which a slanted
+ * highlight on a glyph needs
+ * to be shifted to produce the
+ * best appearance. Set to 0 for
+ * non--slanted fonts */
+ SHORT reserved1; /* set to 0 */
+ SHORT reserved2; /* set to 0 */
+ SHORT reserved3; /* set to 0 */
+ SHORT reserved4; /* set to 0 */
+ SHORT metricDataFormat; /* 0 for current format. */
+ USHORT numberOfHMetrics; /* Number of hMetric entries in 'hmtx'
+ * table */
+ public:
+ DEFINE_SIZE_STATIC (36);
+};
+
+
+#endif /* HB_OT_HHEA_TABLE_HH */
--- /dev/null
+/*
+ * 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 */
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
- * Copyright © 2010 Google, Inc.
+ * Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
#include "hb-ot-layout-private.hh"
-
#include "hb-open-type-private.hh"
+#include "hb-set-private.hh"
-#define NO_CONTEXT ((unsigned int) 0x110000)
#define NOT_COVERED ((unsigned int) 0x110000)
#define MAX_NESTING_LEVEL 8
-HB_BEGIN_DECLS
-HB_END_DECLS
/*
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 */
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return RecordArrayOf<Type>::sanitize (c, this);
+ return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
}
};
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 */
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
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:
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
struct LookupFlag : USHORT
{
- enum {
+ enum Flags {
RightToLeft = 0x0001u,
IgnoreBaseGlyphs = 0x0002u,
IgnoreLigatures = 0x0004u,
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
/* Real sanitize of the subtables is done by GSUB/GPOS/... */
- if (!(c->check_struct (this)
- && subTable.sanitize (c))) return false;
+ if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
{
USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
- if (!markFilteringSet.sanitize (c)) return false;
+ if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
}
- return true;
+ return TRACE_RETURN (true);
}
USHORT lookupType; /* Different enumerations for GSUB and GPOS */
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return glyphArray.sanitize (c);
+ return TRACE_RETURN (glyphArray.sanitize (c));
}
+ inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+ return glyphs->has (glyphArray[index]);
+ }
+
+ struct Iter {
+ inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
+ inline bool more (void) { return i < c->glyphArray.len; }
+ inline void next (void) { i++; }
+ inline uint16_t get_glyph (void) { return c->glyphArray[i]; }
+ inline uint16_t get_coverage (void) { return i; }
+
+ private:
+ const struct CoverageFormat1 *c;
+ unsigned int i;
+ };
+
private:
USHORT coverageFormat; /* Format identifier--format = 1 */
SortedArrayOf<GlyphID>
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return rangeRecord.sanitize (c);
+ return TRACE_RETURN (rangeRecord.sanitize (c));
+ }
+
+ inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+ unsigned int i;
+ unsigned int count = rangeRecord.len;
+ for (i = 0; i < count; i++) {
+ const RangeRecord &range = rangeRecord[i];
+ if (range.value <= index &&
+ index < (unsigned int) range.value + (range.end - range.start) &&
+ range.intersects (glyphs))
+ return true;
+ else if (index < range.value)
+ return false;
+ }
+ return false;
}
+ struct Iter {
+ inline void init (const CoverageFormat2 &c_) {
+ c = &c_;
+ coverage = 0;
+ i = 0;
+ j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
+ }
+ inline bool more (void) { return i < c->rangeRecord.len; }
+ inline void next (void) {
+ coverage++;
+ if (j == c->rangeRecord[i].end) {
+ i++;
+ if (more ())
+ j = c->rangeRecord[i].start;
+ return;
+ }
+ j++;
+ }
+ inline uint16_t get_glyph (void) { return j; }
+ inline uint16_t get_coverage (void) { return coverage; }
+
+ private:
+ const struct CoverageFormat2 *c;
+ unsigned int i, j, coverage;
+ };
+
private:
USHORT coverageFormat; /* Format identifier--format = 2 */
SortedArrayOf<RangeRecord>
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 */
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 */
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 */
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;
}
}
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:
};
-HB_BEGIN_DECLS
-HB_END_DECLS
#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
- * Copyright © 2010,2011 Google, Inc.
+ * Copyright © 2010,2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
-#ifndef HB_OT_LAYOUT_GDEF_PRIVATE_HH
-#define HB_OT_LAYOUT_GDEF_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_GDEF_TABLE_HH
+#define HB_OT_LAYOUT_GDEF_TABLE_HH
#include "hb-ot-layout-common-private.hh"
#include "hb-font-private.hh"
-HB_BEGIN_DECLS
/*
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:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
private:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
private:
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:
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);
}
}
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return carets.sanitize (c, this);
+ return TRACE_RETURN (carets.sanitize (c, this));
}
private:
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:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return coverage.sanitize (c, this);
+ return TRACE_RETURN (coverage.sanitize (c, this));
}
private:
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);
}
}
/*
- * 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,
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)));
}
};
-HB_END_DECLS
-#endif /* HB_OT_LAYOUT_GDEF_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */
/*
* Copyright © 2007,2008,2009,2010 Red Hat, Inc.
- * Copyright © 2010 Google, Inc.
+ * Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
-#ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
-#define HB_OT_LAYOUT_GPOS_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
+#define HB_OT_LAYOUT_GPOS_TABLE_HH
#include "hb-ot-layout-gsubgpos-private.hh"
-HB_BEGIN_DECLS
/* buffer **position** var allocations */
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 */
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);
}
};
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
private:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
private:
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:
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);
}
}
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 */
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:
hb_position_t mark_x, mark_y, base_x, base_y;
- mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->idx].codepoint, &mark_x, &mark_y);
+ mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y);
glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
- hb_glyph_position_t &o = c->buffer->pos[c->buffer->idx];
+ hb_glyph_position_t &o = c->buffer->cur_pos();
o.x_offset = base_x - mark_x;
o.y_offset = base_y - mark_y;
o.attach_lookback() = c->buffer->idx - glyph_pos;
c->buffer->idx++;
- return true;
+ return TRACE_RETURN (true);
}
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return ArrayOf<MarkRecord>::sanitize (c, this);
+ return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
}
};
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:
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:
{
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);
}
}
if (c->buffer->info[pos].codepoint == record->secondGlyph)
{
valueFormats[0].apply_value (c->font, c->direction, this,
- &record->values[0], c->buffer->pos[c->buffer->idx]);
+ &record->values[0], c->buffer->cur_pos());
valueFormats[1].apply_value (c->font, c->direction, this,
&record->values[len1], c->buffer->pos[pos]);
if (len2)
pos++;
c->buffer->idx = pos;
- return true;
+ return TRACE_RETURN (true);
}
record = &StructAtOffset<PairValueRecord> (record, record_size);
}
- return false;
+ return TRACE_RETURN (false);
}
struct sanitize_closure_t {
inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
TRACE_SANITIZE ();
if (!(c->check_struct (this)
- && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
+ && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
unsigned int count = len;
PairValueRecord *record = CastP<PairValueRecord> (array);
- return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
- && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
+ return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
+ && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
}
private:
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) {
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:
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) {
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:
{
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);
}
}
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:
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);
}
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:
{
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);
}
}
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:
{
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);
}
}
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:
{
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);
}
}
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:
{
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);
}
}
};
-HB_BEGIN_DECLS
static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
-HB_END_DECLS
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));
}
};
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));
}
};
{
friend struct PosLookup;
- enum {
+ enum Type {
Single = 1,
Pair = 2,
Cursive = 3,
{
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);
}
}
inline const PosLookupSubTable& get_subtable (unsigned int i) const
{ return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
- inline bool apply_once (hb_font_t *font,
- hb_buffer_t *buffer,
- hb_mask_t lookup_mask,
- unsigned int context_length,
- unsigned int nesting_level_left) const
+ inline bool apply_once (hb_apply_context_t *c) const
{
unsigned int lookup_type = get_type ();
- hb_apply_context_t c[1] = {{0}};
-
- c->font = font;
- c->face = font->face;
- c->buffer = buffer;
- c->direction = buffer->props.direction;
- c->lookup_mask = lookup_mask;
- c->context_length = context_length;
- c->nesting_level_left = nesting_level_left;
- c->lookup_props = get_props ();
-
- if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property))
+
+ if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
return false;
for (unsigned int i = 0; i < get_subtable_count (); i++)
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;
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- if (unlikely (!Lookup::sanitize (c))) return false;
+ if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
- return list.sanitize (c, this, get_type ());
+ return TRACE_RETURN (list.sanitize (c, this, get_type ()));
}
};
typedef OffsetListOf<PosLookup> PosLookupList;
/*
- * GPOS
+ * GPOS -- The Glyph Positioning Table
*/
struct GPOS : GSUBGPOS
inline const PosLookup& get_lookup (unsigned int i) const
{ return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
- inline bool position_lookup (hb_font_t *font,
- hb_buffer_t *buffer,
- unsigned int lookup_index,
- hb_mask_t mask) const
- { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
+ inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
+ { return get_lookup (lookup_index).apply_string (c); }
static inline void position_start (hb_buffer_t *buffer);
static inline void position_finish (hb_buffer_t *buffer);
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+ if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
- return list.sanitize (c, this);
+ return TRACE_RETURN (list.sanitize (c, this));
}
public:
DEFINE_SIZE_STATIC (10);
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);
}
inline bool ExtensionPos::apply (hb_apply_context_t *c) const
{
TRACE_APPLY ();
- return get_subtable ().apply (c, get_type ());
+ return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
}
inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
{
TRACE_SANITIZE ();
- if (unlikely (!Extension::sanitize (c))) return false;
+ if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
unsigned int offset = get_offset ();
- if (unlikely (!offset)) return true;
- return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
+ if (unlikely (!offset)) return TRACE_RETURN (true);
+ return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
}
static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
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);
}
#undef cursive_chain
-HB_END_DECLS
-#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
+++ /dev/null
-/*
- * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
- * Copyright © 2010 Google, Inc.
- *
- * This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
-#define HB_OT_LAYOUT_GSUB_PRIVATE_HH
-
-#include "hb-ot-layout-gsubgpos-private.hh"
-
-HB_BEGIN_DECLS
-
-
-struct SingleSubstFormat1
-{
- friend struct SingleSubst;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
- unsigned int index = (this+coverage) (glyph_id);
- if (likely (index == NOT_COVERED))
- return false;
-
- glyph_id += deltaGlyphID;
- c->replace_glyph (glyph_id);
-
- return true;
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return coverage.sanitize (c, this)
- && deltaGlyphID.sanitize (c);
- }
-
- private:
- USHORT format; /* Format identifier--format = 1 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of Substitution table */
- SHORT deltaGlyphID; /* Add to original GlyphID to get
- * substitute GlyphID */
- public:
- DEFINE_SIZE_STATIC (6);
-};
-
-struct SingleSubstFormat2
-{
- friend struct SingleSubst;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
- unsigned int index = (this+coverage) (glyph_id);
- if (likely (index == NOT_COVERED))
- return false;
-
- if (unlikely (index >= substitute.len))
- return false;
-
- glyph_id = substitute[index];
- c->replace_glyph (glyph_id);
-
- return true;
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return coverage.sanitize (c, this)
- && substitute.sanitize (c);
- }
-
- private:
- USHORT format; /* Format identifier--format = 2 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of Substitution table */
- ArrayOf<GlyphID>
- substitute; /* Array of substitute
- * GlyphIDs--ordered by Coverage Index */
- public:
- DEFINE_SIZE_ARRAY (6, substitute);
-};
-
-struct SingleSubst
-{
- friend struct SubstLookupSubTable;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- switch (u.format) {
- case 1: return u.format1.apply (c);
- case 2: return u.format2.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!u.format.sanitize (c)) return false;
- switch (u.format) {
- case 1: return u.format1.sanitize (c);
- case 2: return u.format2.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT format; /* Format identifier */
- SingleSubstFormat1 format1;
- SingleSubstFormat2 format2;
- } u;
-};
-
-
-struct Sequence
-{
- friend struct MultipleSubstFormat1;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- if (unlikely (!substitute.len))
- return false;
-
- if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
- c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
- c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
-
- return true;
- }
-
- public:
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return substitute.sanitize (c);
- }
-
- private:
- ArrayOf<GlyphID>
- substitute; /* String of GlyphIDs to substitute */
- public:
- DEFINE_SIZE_ARRAY (2, substitute);
-};
-
-struct MultipleSubstFormat1
-{
- friend struct MultipleSubst;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
-
- unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
- if (likely (index == NOT_COVERED))
- return false;
-
- return (this+sequence[index]).apply (c);
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return coverage.sanitize (c, this)
- && sequence.sanitize (c, this);
- }
-
- private:
- USHORT format; /* Format identifier--format = 1 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of Substitution table */
- OffsetArrayOf<Sequence>
- sequence; /* Array of Sequence tables
- * ordered by Coverage Index */
- public:
- DEFINE_SIZE_ARRAY (6, sequence);
-};
-
-struct MultipleSubst
-{
- friend struct SubstLookupSubTable;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- switch (u.format) {
- case 1: return u.format1.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!u.format.sanitize (c)) return false;
- switch (u.format) {
- case 1: return u.format1.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT format; /* Format identifier */
- MultipleSubstFormat1 format1;
- } u;
-};
-
-
-typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
- * arbitrary order */
-
-struct AlternateSubstFormat1
-{
- friend struct AlternateSubst;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
- hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
- hb_mask_t lookup_mask = c->lookup_mask;
-
- unsigned int index = (this+coverage) (glyph_id);
- if (likely (index == NOT_COVERED))
- return false;
-
- const AlternateSet &alt_set = this+alternateSet[index];
-
- if (unlikely (!alt_set.len))
- return false;
-
- /* Note: This breaks badly if two features enabled this lookup together. */
- unsigned int shift = _hb_ctz (lookup_mask);
- unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
-
- if (unlikely (alt_index > alt_set.len || alt_index == 0))
- return false;
-
- glyph_id = alt_set[alt_index - 1];
-
- c->replace_glyph (glyph_id);
-
- return true;
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return coverage.sanitize (c, this)
- && alternateSet.sanitize (c, this);
- }
-
- private:
- USHORT format; /* Format identifier--format = 1 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of Substitution table */
- OffsetArrayOf<AlternateSet>
- alternateSet; /* Array of AlternateSet tables
- * ordered by Coverage Index */
- public:
- DEFINE_SIZE_ARRAY (6, alternateSet);
-};
-
-struct AlternateSubst
-{
- friend struct SubstLookupSubTable;
-
- private:
-
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- switch (u.format) {
- case 1: return u.format1.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!u.format.sanitize (c)) return false;
- switch (u.format) {
- case 1: return u.format1.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT format; /* Format identifier */
- AlternateSubstFormat1 format1;
- } u;
-};
-
-
-struct Ligature
-{
- friend struct LigatureSet;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- unsigned int i, j;
- unsigned int count = component.len;
- unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
- if (unlikely (count < 2 || c->buffer->idx + count > end))
- return false;
-
- bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
- bool found_non_mark = false;
-
- for (i = 1, j = c->buffer->idx + 1; i < count; i++, j++)
- {
- unsigned int property;
- while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property))
- {
- if (unlikely (j + count - i == end))
- return false;
- j++;
- }
-
- found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
-
- if (likely (c->buffer->info[j].codepoint != component[i]))
- return false;
- }
-
- if (first_was_mark && found_non_mark)
- c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
-
- /* Allocate new ligature id */
- unsigned int lig_id = allocate_lig_id (c->buffer);
- c->buffer->info[c->buffer->idx].lig_comp() = 0;
- c->buffer->info[c->buffer->idx].lig_id() = lig_id;
-
- if (j == c->buffer->idx + i) /* No input glyphs skipped */
- {
- c->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph);
- }
- else
- {
- c->replace_glyph (ligGlyph);
-
- /* Now we must do a second loop to copy the skipped glyphs to
- `out' and assign component values to it. We start with the
- glyph after the first component. Glyphs between component
- i and i+1 belong to component i. Together with the lig_id
- value it is later possible to check whether a specific
- component value really belongs to a given ligature. */
-
- for (i = 1; i < count; i++)
- {
- while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL))
- {
- c->buffer->info[c->buffer->idx].lig_comp() = i;
- c->buffer->info[c->buffer->idx].lig_id() = lig_id;
- c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
- }
-
- /* Skip the base glyph */
- c->buffer->idx++;
- }
- }
-
- return true;
- }
-
- public:
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return ligGlyph.sanitize (c)
- && component.sanitize (c);
- }
-
- private:
- GlyphID ligGlyph; /* GlyphID of ligature to substitute */
- HeadlessArrayOf<GlyphID>
- component; /* Array of component GlyphIDs--start
- * with the second component--ordered
- * in writing direction */
- public:
- DEFINE_SIZE_ARRAY (4, component);
-};
-
-struct LigatureSet
-{
- friend struct LigatureSubstFormat1;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- unsigned int num_ligs = ligature.len;
- for (unsigned int i = 0; i < num_ligs; i++)
- {
- const Ligature &lig = this+ligature[i];
- if (lig.apply (c))
- return true;
- }
-
- return false;
- }
-
- public:
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return ligature.sanitize (c, this);
- }
-
- private:
- OffsetArrayOf<Ligature>
- ligature; /* Array LigatureSet tables
- * ordered by preference */
- public:
- DEFINE_SIZE_ARRAY (2, ligature);
-};
-
-struct LigatureSubstFormat1
-{
- friend struct LigatureSubst;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
-
- unsigned int index = (this+coverage) (glyph_id);
- if (likely (index == NOT_COVERED))
- return false;
-
- const LigatureSet &lig_set = this+ligatureSet[index];
- return lig_set.apply (c);
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- return coverage.sanitize (c, this)
- && ligatureSet.sanitize (c, this);
- }
-
- private:
- USHORT format; /* Format identifier--format = 1 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of Substitution table */
- OffsetArrayOf<LigatureSet>
- ligatureSet; /* Array LigatureSet tables
- * ordered by Coverage Index */
- public:
- DEFINE_SIZE_ARRAY (6, ligatureSet);
-};
-
-struct LigatureSubst
-{
- friend struct SubstLookupSubTable;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- switch (u.format) {
- case 1: return u.format1.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!u.format.sanitize (c)) return false;
- switch (u.format) {
- case 1: return u.format1.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT format; /* Format identifier */
- LigatureSubstFormat1 format1;
- } u;
-};
-
-
-HB_BEGIN_DECLS
-static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
-HB_END_DECLS
-
-struct ContextSubst : Context
-{
- friend struct SubstLookupSubTable;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- return Context::apply (c, substitute_lookup);
- }
-};
-
-struct ChainContextSubst : ChainContext
-{
- friend struct SubstLookupSubTable;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- return ChainContext::apply (c, substitute_lookup);
- }
-};
-
-
-struct ExtensionSubst : Extension
-{
- friend struct SubstLookupSubTable;
- friend struct SubstLookup;
-
- private:
- inline const struct SubstLookupSubTable& get_subtable (void) const
- {
- unsigned int offset = get_offset ();
- if (unlikely (!offset)) return Null(SubstLookupSubTable);
- return StructAtOffset<SubstLookupSubTable> (this, offset);
- }
-
- inline bool apply (hb_apply_context_t *c) const;
-
- inline bool sanitize (hb_sanitize_context_t *c);
-
- inline bool is_reverse (void) const;
-};
-
-
-struct ReverseChainSingleSubstFormat1
-{
- friend struct ReverseChainSingleSubst;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- if (unlikely (c->context_length != NO_CONTEXT))
- return false; /* No chaining to this type */
-
- unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
- if (likely (index == NOT_COVERED))
- return false;
-
- const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
- const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-
- if (match_backtrack (c,
- backtrack.len, (USHORT *) backtrack.array,
- match_coverage, this) &&
- match_lookahead (c,
- lookahead.len, (USHORT *) lookahead.array,
- match_coverage, this,
- 1))
- {
- c->buffer->info[c->buffer->idx].codepoint = substitute[index];
- c->buffer->idx--; /* Reverse! */
- return true;
- }
-
- return false;
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!(coverage.sanitize (c, this)
- && backtrack.sanitize (c, this)))
- return false;
- OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
- if (!lookahead.sanitize (c, this))
- return false;
- ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
- return substitute.sanitize (c);
- }
-
- private:
- USHORT format; /* Format identifier--format = 1 */
- OffsetTo<Coverage>
- coverage; /* Offset to Coverage table--from
- * beginning of table */
- OffsetArrayOf<Coverage>
- backtrack; /* Array of coverage tables
- * in backtracking sequence, in glyph
- * sequence order */
- OffsetArrayOf<Coverage>
- lookaheadX; /* Array of coverage tables
- * in lookahead sequence, in glyph
- * sequence order */
- ArrayOf<GlyphID>
- substituteX; /* Array of substitute
- * GlyphIDs--ordered by Coverage Index */
- public:
- DEFINE_SIZE_MIN (10);
-};
-
-struct ReverseChainSingleSubst
-{
- friend struct SubstLookupSubTable;
-
- private:
- inline bool apply (hb_apply_context_t *c) const
- {
- TRACE_APPLY ();
- switch (u.format) {
- case 1: return u.format1.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (!u.format.sanitize (c)) return false;
- switch (u.format) {
- case 1: return u.format1.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT format; /* Format identifier */
- ReverseChainSingleSubstFormat1 format1;
- } u;
-};
-
-
-
-/*
- * SubstLookup
- */
-
-struct SubstLookupSubTable
-{
- friend struct SubstLookup;
-
- enum {
- Single = 1,
- Multiple = 2,
- Alternate = 3,
- Ligature = 4,
- Context = 5,
- ChainContext = 6,
- Extension = 7,
- ReverseChainSingle = 8
- };
-
- inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
- {
- TRACE_APPLY ();
- switch (lookup_type) {
- case Single: return u.single.apply (c);
- case Multiple: return u.multiple.apply (c);
- case Alternate: return u.alternate.apply (c);
- case Ligature: return u.ligature.apply (c);
- case Context: return u.c.apply (c);
- case ChainContext: return u.chainContext.apply (c);
- case Extension: return u.extension.apply (c);
- case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
- default:return false;
- }
- }
-
- inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
- TRACE_SANITIZE ();
- switch (lookup_type) {
- case Single: return u.single.sanitize (c);
- case Multiple: return u.multiple.sanitize (c);
- case Alternate: return u.alternate.sanitize (c);
- case Ligature: return u.ligature.sanitize (c);
- case Context: return u.c.sanitize (c);
- case ChainContext: return u.chainContext.sanitize (c);
- case Extension: return u.extension.sanitize (c);
- case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
- default:return true;
- }
- }
-
- private:
- union {
- USHORT sub_format;
- SingleSubst single;
- MultipleSubst multiple;
- AlternateSubst alternate;
- LigatureSubst ligature;
- ContextSubst c;
- ChainContextSubst chainContext;
- ExtensionSubst extension;
- ReverseChainSingleSubst reverseChainContextSingle;
- } u;
- public:
- DEFINE_SIZE_UNION (2, sub_format);
-};
-
-
-struct SubstLookup : Lookup
-{
- inline const SubstLookupSubTable& get_subtable (unsigned int i) const
- { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
-
- inline static bool lookup_type_is_reverse (unsigned int lookup_type)
- { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
-
- inline bool is_reverse (void) const
- {
- unsigned int type = get_type ();
- if (unlikely (type == SubstLookupSubTable::Extension))
- return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
- return lookup_type_is_reverse (type);
- }
-
-
- inline bool apply_once (hb_face_t *face,
- hb_buffer_t *buffer,
- hb_mask_t lookup_mask,
- unsigned int context_length,
- unsigned int nesting_level_left) const
- {
- unsigned int lookup_type = get_type ();
- hb_apply_context_t c[1] = {{0}};
-
- c->face = face;
- c->buffer = buffer;
- c->direction = buffer->props.direction;
- c->lookup_mask = lookup_mask;
- c->context_length = context_length;
- c->nesting_level_left = nesting_level_left;
- c->lookup_props = get_props ();
-
- if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property))
- return false;
-
- if (unlikely (lookup_type == SubstLookupSubTable::Extension))
- {
- /* The spec says all subtables should have the same type.
- * This is specially important if one has a reverse type!
- *
- * This is rather slow to do this here for every glyph,
- * but it's easiest, and who uses extension lookups anyway?!*/
- unsigned int count = get_subtable_count ();
- unsigned int type = get_subtable(0).u.extension.get_type ();
- for (unsigned int i = 1; i < count; i++)
- if (get_subtable(i).u.extension.get_type () != type)
- return false;
- }
-
- unsigned int count = get_subtable_count ();
- for (unsigned int i = 0; i < count; i++)
- if (get_subtable (i).apply (c, lookup_type))
- return true;
-
- return false;
- }
-
- inline bool apply_string (hb_face_t *face,
- hb_buffer_t *buffer,
- hb_mask_t mask) const
- {
- bool ret = false;
-
- if (unlikely (!buffer->len))
- return false;
-
- if (likely (!is_reverse ()))
- {
- /* in/out forward substitution */
- buffer->clear_output ();
- buffer->idx = 0;
- while (buffer->idx < buffer->len)
- {
- if ((buffer->info[buffer->idx].mask & mask) &&
- apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
- ret = true;
- else
- buffer->next_glyph ();
-
- }
- if (ret)
- buffer->swap_buffers ();
- }
- else
- {
- /* in-place backward substitution */
- buffer->idx = buffer->len - 1;
- do
- {
- if ((buffer->info[buffer->idx].mask & mask) &&
- apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
- ret = true;
- else
- buffer->idx--;
-
- }
- while ((int) buffer->idx >= 0);
- }
-
- return ret;
- }
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (unlikely (!Lookup::sanitize (c))) return false;
- OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
- return list.sanitize (c, this, get_type ());
- }
-};
-
-typedef OffsetListOf<SubstLookup> SubstLookupList;
-
-/*
- * GSUB
- */
-
-struct GSUB : GSUBGPOS
-{
- static const hb_tag_t Tag = HB_OT_TAG_GSUB;
-
- inline const SubstLookup& get_lookup (unsigned int i) const
- { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
-
- inline bool substitute_lookup (hb_face_t *face,
- hb_buffer_t *buffer,
- unsigned int lookup_index,
- hb_mask_t mask) const
- { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
-
- static inline void substitute_start (hb_buffer_t *buffer);
- static inline void substitute_finish (hb_buffer_t *buffer);
-
- inline bool sanitize (hb_sanitize_context_t *c) {
- TRACE_SANITIZE ();
- if (unlikely (!GSUBGPOS::sanitize (c))) return false;
- OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
- return list.sanitize (c, this);
- }
- public:
- DEFINE_SIZE_STATIC (10);
-};
-
-
-void
-GSUB::substitute_start (hb_buffer_t *buffer)
-{
- HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
- HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
- HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
-
- unsigned int count = buffer->len;
- for (unsigned int i = 0; i < count; i++)
- buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
-}
-
-void
-GSUB::substitute_finish (hb_buffer_t *buffer)
-{
-}
-
-
-/* Out-of-class implementation for methods recursing */
-
-inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
-{
- TRACE_APPLY ();
- return get_subtable ().apply (c, get_type ());
-}
-
-inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
-{
- TRACE_SANITIZE ();
- if (unlikely (!Extension::sanitize (c))) return false;
- unsigned int offset = get_offset ();
- if (unlikely (!offset)) return true;
- return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
-}
-
-inline bool ExtensionSubst::is_reverse (void) const
-{
- unsigned int type = get_type ();
- if (unlikely (type == SubstLookupSubTable::Extension))
- return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
- return SubstLookup::lookup_type_is_reverse (type);
-}
-
-static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
-{
- const GSUB &gsub = *(c->face->ot_layout->gsub);
- const SubstLookup &l = gsub.get_lookup (lookup_index);
-
- if (unlikely (c->nesting_level_left == 0))
- return false;
-
- if (unlikely (c->context_length < 1))
- return false;
-
- return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
-}
-
-
-HB_END_DECLS
-
-#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */
--- /dev/null
+/*
+ * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
+ * Copyright © 2010,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
+#define HB_OT_LAYOUT_GSUB_TABLE_HH
+
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+
+
+struct SingleSubstFormat1
+{
+ friend struct SingleSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ hb_codepoint_t glyph_id = iter.get_glyph ();
+ if (c->glyphs->has (glyph_id))
+ c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF);
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ return (this+coverage) (glyph_id) != NOT_COVERED;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+ unsigned int index = (this+coverage) (glyph_id);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ /* According to the Adobe Annotated OpenType Suite, result is always
+ * limited to 16bit. */
+ glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
+ c->replace_glyph (glyph_id);
+
+ return TRACE_RETURN (true);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ SHORT deltaGlyphID; /* Add to original GlyphID to get
+ * substitute GlyphID */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct SingleSubstFormat2
+{
+ friend struct SingleSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ if (c->glyphs->has (iter.get_glyph ()))
+ c->glyphs->add (substitute[iter.get_coverage ()]);
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ return (this+coverage) (glyph_id) != NOT_COVERED;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+ unsigned int index = (this+coverage) (glyph_id);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
+
+ glyph_id = substitute[index];
+ c->replace_glyph (glyph_id);
+
+ return TRACE_RETURN (true);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 2 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ ArrayOf<GlyphID>
+ substitute; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, substitute);
+};
+
+struct SingleSubst
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ switch (u.format) {
+ case 1: u.format1.closure (c); break;
+ case 2: u.format2.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.would_apply (glyph_id);
+ case 2: return u.format2.would_apply (glyph_id);
+ default:return false;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.apply (c));
+ case 2: return TRACE_RETURN (u.format2.apply (c));
+ default:return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.sanitize (c));
+ case 2: return TRACE_RETURN (u.format2.sanitize (c));
+ default:return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ SingleSubstFormat1 format1;
+ SingleSubstFormat2 format2;
+ } u;
+};
+
+
+struct Sequence
+{
+ friend struct MultipleSubstFormat1;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ unsigned int count = substitute.len;
+ for (unsigned int i = 0; i < count; i++)
+ c->glyphs->add (substitute[i]);
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ if (unlikely (!substitute.len)) return TRACE_RETURN (false);
+
+ unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
+ c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass);
+
+ return TRACE_RETURN (true);
+ }
+
+ public:
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (substitute.sanitize (c));
+ }
+
+ private:
+ ArrayOf<GlyphID>
+ substitute; /* String of GlyphIDs to substitute */
+ public:
+ DEFINE_SIZE_ARRAY (2, substitute);
+};
+
+struct MultipleSubstFormat1
+{
+ friend struct MultipleSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ if (c->glyphs->has (iter.get_glyph ()))
+ (this+sequence[iter.get_coverage ()]).closure (c);
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ return (this+coverage) (glyph_id) != NOT_COVERED;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+
+ unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ return TRACE_RETURN ((this+sequence[index]).apply (c));
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<Sequence>
+ sequence; /* Array of Sequence tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, sequence);
+};
+
+struct MultipleSubst
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ switch (u.format) {
+ case 1: u.format1.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.would_apply (glyph_id);
+ default:return false;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.apply (c));
+ default:return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.sanitize (c));
+ default:return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ MultipleSubstFormat1 format1;
+ } u;
+};
+
+
+typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
+ * arbitrary order */
+
+struct AlternateSubstFormat1
+{
+ friend struct AlternateSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ if (c->glyphs->has (iter.get_glyph ())) {
+ const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
+ unsigned int count = alt_set.len;
+ for (unsigned int i = 0; i < count; i++)
+ c->glyphs->add (alt_set[i]);
+ }
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ return (this+coverage) (glyph_id) != NOT_COVERED;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+
+ unsigned int index = (this+coverage) (glyph_id);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ const AlternateSet &alt_set = this+alternateSet[index];
+
+ if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
+
+ hb_mask_t glyph_mask = c->buffer->cur().mask;
+ hb_mask_t lookup_mask = c->lookup_mask;
+
+ /* Note: This breaks badly if two features enabled this lookup together. */
+ unsigned int shift = _hb_ctz (lookup_mask);
+ unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
+
+ if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
+
+ glyph_id = alt_set[alt_index - 1];
+
+ c->replace_glyph (glyph_id);
+
+ return TRACE_RETURN (true);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<AlternateSet>
+ alternateSet; /* Array of AlternateSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, alternateSet);
+};
+
+struct AlternateSubst
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ switch (u.format) {
+ case 1: u.format1.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.would_apply (glyph_id);
+ default:return false;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.apply (c));
+ default:return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.sanitize (c));
+ default:return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ AlternateSubstFormat1 format1;
+ } u;
+};
+
+
+struct Ligature
+{
+ friend struct LigatureSet;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ unsigned int count = component.len;
+ for (unsigned int i = 1; i < count; i++)
+ if (!c->glyphs->has (component[i]))
+ return;
+ c->glyphs->add (ligGlyph);
+ }
+
+ inline bool would_apply (hb_codepoint_t second) const
+ {
+ return component.len == 2 && component[1] == second;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ unsigned int count = component.len;
+ if (unlikely (count < 2)) return TRACE_RETURN (false);
+
+ hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+ if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
+
+ bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+ bool found_non_mark = false;
+
+ for (unsigned int i = 1; i < count; i++)
+ {
+ unsigned int property;
+
+ if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
+
+ found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+ if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
+ }
+
+ unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0;
+
+ /* Allocate new ligature id */
+ unsigned int lig_id = allocate_lig_id (c->buffer);
+ set_lig_props (c->buffer->cur(), lig_id, 0);
+
+ if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
+ {
+ c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass);
+ }
+ else
+ {
+ c->replace_glyph (ligGlyph);
+
+ /* Now we must do a second loop to copy the skipped glyphs to
+ `out' and assign component values to it. We start with the
+ glyph after the first component. Glyphs between component
+ i and i+1 belong to component i. Together with the lig_id
+ value it is later possible to check whether a specific
+ component value really belongs to a given ligature. */
+
+ for (unsigned int i = 1; i < count; i++)
+ {
+ while (c->should_mark_skip_current_glyph ())
+ {
+ set_lig_props (c->buffer->cur(), lig_id, i);
+ c->replace_glyph (c->buffer->cur().codepoint);
+ }
+
+ /* Skip the base glyph */
+ c->buffer->idx++;
+ }
+ }
+
+ return TRACE_RETURN (true);
+ }
+
+ public:
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
+ }
+
+ private:
+ GlyphID ligGlyph; /* GlyphID of ligature to substitute */
+ HeadlessArrayOf<GlyphID>
+ component; /* Array of component GlyphIDs--start
+ * with the second component--ordered
+ * in writing direction */
+ public:
+ DEFINE_SIZE_ARRAY (4, component);
+};
+
+struct LigatureSet
+{
+ friend struct LigatureSubstFormat1;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ unsigned int num_ligs = ligature.len;
+ for (unsigned int i = 0; i < num_ligs; i++)
+ (this+ligature[i]).closure (c);
+ }
+
+ inline bool would_apply (hb_codepoint_t second) const
+ {
+ unsigned int num_ligs = ligature.len;
+ for (unsigned int i = 0; i < num_ligs; i++)
+ {
+ const Ligature &lig = this+ligature[i];
+ if (lig.would_apply (second))
+ return true;
+ }
+ return false;
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ unsigned int num_ligs = ligature.len;
+ for (unsigned int i = 0; i < num_ligs; i++)
+ {
+ const Ligature &lig = this+ligature[i];
+ if (lig.apply (c)) return TRACE_RETURN (true);
+ }
+
+ return TRACE_RETURN (false);
+ }
+
+ public:
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (ligature.sanitize (c, this));
+ }
+
+ private:
+ OffsetArrayOf<Ligature>
+ ligature; /* Array LigatureSet tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, ligature);
+};
+
+struct LigatureSubstFormat1
+{
+ friend struct LigatureSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ if (c->glyphs->has (iter.get_glyph ()))
+ (this+ligatureSet[iter.get_coverage ()]).closure (c);
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+ {
+ unsigned int index;
+ return (index = (this+coverage) (first)) != NOT_COVERED &&
+ (this+ligatureSet[index]).would_apply (second);
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+
+ unsigned int index = (this+coverage) (glyph_id);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ const LigatureSet &lig_set = this+ligatureSet[index];
+ return TRACE_RETURN (lig_set.apply (c));
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<LigatureSet>
+ ligatureSet; /* Array LigatureSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, ligatureSet);
+};
+
+struct LigatureSubst
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ switch (u.format) {
+ case 1: u.format1.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+ {
+ switch (u.format) {
+ case 1: return u.format1.would_apply (first, second);
+ default:return false;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.apply (c));
+ default:return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.sanitize (c));
+ default:return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ LigatureSubstFormat1 format1;
+ } u;
+};
+
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
+
+struct ContextSubst : Context
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ return Context::closure (c, closure_lookup);
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ return TRACE_RETURN (Context::apply (c, substitute_lookup));
+ }
+};
+
+struct ChainContextSubst : ChainContext
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ return ChainContext::closure (c, closure_lookup);
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ return TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
+ }
+};
+
+
+struct ExtensionSubst : Extension
+{
+ friend struct SubstLookupSubTable;
+ friend struct SubstLookup;
+
+ private:
+ inline const struct SubstLookupSubTable& get_subtable (void) const
+ {
+ unsigned int offset = get_offset ();
+ if (unlikely (!offset)) return Null(SubstLookupSubTable);
+ return StructAtOffset<SubstLookupSubTable> (this, offset);
+ }
+
+ inline void closure (hb_closure_context_t *c) const;
+ inline bool would_apply (hb_codepoint_t glyph_id) const;
+ inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
+
+ inline bool apply (hb_apply_context_t *c) const;
+
+ inline bool sanitize (hb_sanitize_context_t *c);
+
+ inline bool is_reverse (void) const;
+};
+
+
+struct ReverseChainSingleSubstFormat1
+{
+ friend struct ReverseChainSingleSubst;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+ unsigned int count;
+
+ count = backtrack.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (!(this+backtrack[i]).intersects (c->glyphs))
+ return;
+
+ count = lookahead.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (!(this+lookahead[i]).intersects (c->glyphs))
+ return;
+
+ const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+ Coverage::Iter iter;
+ for (iter.init (this+coverage); iter.more (); iter.next ()) {
+ if (c->glyphs->has (iter.get_glyph ()))
+ c->glyphs->add (substitute[iter.get_coverage ()]);
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
+ return TRACE_RETURN (false); /* No chaining to this type */
+
+ unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+ const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+
+ if (match_backtrack (c,
+ backtrack.len, (USHORT *) backtrack.array,
+ match_coverage, this) &&
+ match_lookahead (c,
+ lookahead.len, (USHORT *) lookahead.array,
+ match_coverage, this,
+ 1))
+ {
+ c->buffer->cur().codepoint = substitute[index];
+ c->buffer->idx--; /* Reverse! */
+ return TRACE_RETURN (true);
+ }
+
+ return TRACE_RETURN (false);
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+ return TRACE_RETURN (false);
+ OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+ if (!lookahead.sanitize (c, this))
+ return TRACE_RETURN (false);
+ ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+ return TRACE_RETURN (substitute.sanitize (c));
+ }
+
+ private:
+ USHORT format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetArrayOf<Coverage>
+ backtrack; /* Array of coverage tables
+ * in backtracking sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ lookaheadX; /* Array of coverage tables
+ * in lookahead sequence, in glyph
+ * sequence order */
+ ArrayOf<GlyphID>
+ substituteX; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_MIN (10);
+};
+
+struct ReverseChainSingleSubst
+{
+ friend struct SubstLookupSubTable;
+
+ private:
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ TRACE_CLOSURE ();
+ switch (u.format) {
+ case 1: u.format1.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c) const
+ {
+ TRACE_APPLY ();
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.apply (c));
+ default:return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+ switch (u.format) {
+ case 1: return TRACE_RETURN (u.format1.sanitize (c));
+ default:return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT format; /* Format identifier */
+ ReverseChainSingleSubstFormat1 format1;
+ } u;
+};
+
+
+
+/*
+ * SubstLookup
+ */
+
+struct SubstLookupSubTable
+{
+ friend struct SubstLookup;
+
+ enum Type {
+ Single = 1,
+ Multiple = 2,
+ Alternate = 3,
+ Ligature = 4,
+ Context = 5,
+ ChainContext = 6,
+ Extension = 7,
+ ReverseChainSingle = 8
+ };
+
+ inline void closure (hb_closure_context_t *c,
+ unsigned int lookup_type) const
+ {
+ TRACE_CLOSURE ();
+ switch (lookup_type) {
+ case Single: u.single.closure (c); break;
+ case Multiple: u.multiple.closure (c); break;
+ case Alternate: u.alternate.closure (c); break;
+ case Ligature: u.ligature.closure (c); break;
+ case Context: u.c.closure (c); break;
+ case ChainContext: u.chainContext.closure (c); break;
+ case Extension: u.extension.closure (c); break;
+ case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break;
+ default: break;
+ }
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id,
+ unsigned int lookup_type) const
+ {
+ switch (lookup_type) {
+ case Single: return u.single.would_apply (glyph_id);
+ case Multiple: return u.multiple.would_apply (glyph_id);
+ case Alternate: return u.alternate.would_apply (glyph_id);
+ case Extension: return u.extension.would_apply (glyph_id);
+ default: return false;
+ }
+ }
+ inline bool would_apply (hb_codepoint_t first,
+ hb_codepoint_t second,
+ unsigned int lookup_type) const
+ {
+ switch (lookup_type) {
+ case Ligature: return u.ligature.would_apply (first, second);
+ case Extension: return u.extension.would_apply (first, second);
+ default: return false;
+ }
+ }
+
+ inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
+ {
+ TRACE_APPLY ();
+ switch (lookup_type) {
+ case Single: return TRACE_RETURN (u.single.apply (c));
+ case Multiple: return TRACE_RETURN (u.multiple.apply (c));
+ case Alternate: return TRACE_RETURN (u.alternate.apply (c));
+ case Ligature: return TRACE_RETURN (u.ligature.apply (c));
+ case Context: return TRACE_RETURN (u.c.apply (c));
+ case ChainContext: return TRACE_RETURN (u.chainContext.apply (c));
+ case Extension: return TRACE_RETURN (u.extension.apply (c));
+ case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
+ default: return TRACE_RETURN (false);
+ }
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
+ TRACE_SANITIZE ();
+ switch (lookup_type) {
+ case Single: return TRACE_RETURN (u.single.sanitize (c));
+ case Multiple: return TRACE_RETURN (u.multiple.sanitize (c));
+ case Alternate: return TRACE_RETURN (u.alternate.sanitize (c));
+ case Ligature: return TRACE_RETURN (u.ligature.sanitize (c));
+ case Context: return TRACE_RETURN (u.c.sanitize (c));
+ case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c));
+ case Extension: return TRACE_RETURN (u.extension.sanitize (c));
+ case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
+ default: return TRACE_RETURN (true);
+ }
+ }
+
+ private:
+ union {
+ USHORT sub_format;
+ SingleSubst single;
+ MultipleSubst multiple;
+ AlternateSubst alternate;
+ LigatureSubst ligature;
+ ContextSubst c;
+ ChainContextSubst chainContext;
+ ExtensionSubst extension;
+ ReverseChainSingleSubst reverseChainContextSingle;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, sub_format);
+};
+
+
+struct SubstLookup : Lookup
+{
+ inline const SubstLookupSubTable& get_subtable (unsigned int i) const
+ { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
+
+ inline static bool lookup_type_is_reverse (unsigned int lookup_type)
+ { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
+
+ inline bool is_reverse (void) const
+ {
+ unsigned int type = get_type ();
+ if (unlikely (type == SubstLookupSubTable::Extension))
+ return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
+ return lookup_type_is_reverse (type);
+ }
+
+ inline void closure (hb_closure_context_t *c) const
+ {
+ unsigned int lookup_type = get_type ();
+ unsigned int count = get_subtable_count ();
+ for (unsigned int i = 0; i < count; i++)
+ get_subtable (i).closure (c, lookup_type);
+ }
+
+ inline bool would_apply (hb_codepoint_t glyph_id) const
+ {
+ unsigned int lookup_type = get_type ();
+ unsigned int count = get_subtable_count ();
+ for (unsigned int i = 0; i < count; i++)
+ if (get_subtable (i).would_apply (glyph_id, lookup_type))
+ return true;
+ return false;
+ }
+ inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+ {
+ unsigned int lookup_type = get_type ();
+ unsigned int count = get_subtable_count ();
+ for (unsigned int i = 0; i < count; i++)
+ if (get_subtable (i).would_apply (first, second, lookup_type))
+ return true;
+ return false;
+ }
+
+ inline bool apply_once (hb_apply_context_t *c) const
+ {
+ unsigned int lookup_type = get_type ();
+
+ if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
+ return false;
+
+ if (unlikely (lookup_type == SubstLookupSubTable::Extension))
+ {
+ /* The spec says all subtables should have the same type.
+ * This is specially important if one has a reverse type!
+ *
+ * This is rather slow to do this here for every glyph,
+ * but it's easiest, and who uses extension lookups anyway?!*/
+ unsigned int type = get_subtable(0).u.extension.get_type ();
+ unsigned int count = get_subtable_count ();
+ for (unsigned int i = 1; i < count; i++)
+ if (get_subtable(i).u.extension.get_type () != type)
+ return false;
+ }
+
+ unsigned int count = get_subtable_count ();
+ for (unsigned int i = 0; i < count; i++)
+ if (get_subtable (i).apply (c, lookup_type))
+ return true;
+
+ return false;
+ }
+
+ inline bool apply_string (hb_apply_context_t *c) const
+ {
+ bool ret = false;
+
+ if (unlikely (!c->buffer->len))
+ return false;
+
+ c->set_lookup (*this);
+
+ if (likely (!is_reverse ()))
+ {
+ /* in/out forward substitution */
+ c->buffer->clear_output ();
+ c->buffer->idx = 0;
+ while (c->buffer->idx < c->buffer->len)
+ {
+ if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+ ret = true;
+ else
+ c->buffer->next_glyph ();
+
+ }
+ if (ret)
+ c->buffer->swap_buffers ();
+ }
+ else
+ {
+ /* in-place backward substitution */
+ c->buffer->idx = c->buffer->len - 1;
+ do
+ {
+ if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+ ret = true;
+ else
+ c->buffer->idx--;
+
+ }
+ while ((int) c->buffer->idx >= 0);
+ }
+
+ return ret;
+ }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
+ OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
+ return TRACE_RETURN (list.sanitize (c, this, get_type ()));
+ }
+};
+
+typedef OffsetListOf<SubstLookup> SubstLookupList;
+
+/*
+ * GSUB -- The Glyph Substitution Table
+ */
+
+struct GSUB : GSUBGPOS
+{
+ static const hb_tag_t Tag = HB_OT_TAG_GSUB;
+
+ inline const SubstLookup& get_lookup (unsigned int i) const
+ { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
+
+ inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
+ { return get_lookup (lookup_index).apply_string (c); }
+
+ static inline void substitute_start (hb_buffer_t *buffer);
+ static inline void substitute_finish (hb_buffer_t *buffer);
+
+ inline void closure_lookup (hb_closure_context_t *c,
+ unsigned int lookup_index) const
+ { return get_lookup (lookup_index).closure (c); }
+
+ inline bool sanitize (hb_sanitize_context_t *c) {
+ TRACE_SANITIZE ();
+ if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
+ OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
+ return TRACE_RETURN (list.sanitize (c, this));
+ }
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+
+void
+GSUB::substitute_start (hb_buffer_t *buffer)
+{
+ HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
+ HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
+ HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
+
+ unsigned int count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+}
+
+void
+GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED)
+{
+}
+
+
+/* Out-of-class implementation for methods recursing */
+
+inline void ExtensionSubst::closure (hb_closure_context_t *c) const
+{
+ get_subtable ().closure (c, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
+{
+ return get_subtable ().would_apply (glyph_id, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+{
+ return get_subtable ().would_apply (first, second, get_type ());
+}
+
+inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
+{
+ TRACE_APPLY ();
+ return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
+}
+
+inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
+{
+ TRACE_SANITIZE ();
+ if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
+ unsigned int offset = get_offset ();
+ if (unlikely (!offset)) return TRACE_RETURN (true);
+ return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()));
+}
+
+inline bool ExtensionSubst::is_reverse (void) const
+{
+ unsigned int type = get_type ();
+ if (unlikely (type == SubstLookupSubTable::Extension))
+ return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
+ return SubstLookup::lookup_type_is_reverse (type);
+}
+
+static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
+{
+ const GSUB &gsub = *(c->face->ot_layout->gsub);
+ const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+ if (unlikely (c->nesting_level_left == 0))
+ return;
+
+ c->nesting_level_left--;
+ l.closure (c);
+ c->nesting_level_left++;
+}
+
+static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+{
+ const GSUB &gsub = *(c->face->ot_layout->gsub);
+ const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+ if (unlikely (c->nesting_level_left == 0))
+ return false;
+
+ hb_apply_context_t new_c (*c);
+ new_c.nesting_level_left--;
+ new_c.set_lookup (l);
+ return l.apply_once (&new_c);
+}
+
+
+
+#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
/*
* Copyright © 2007,2008,2009,2010 Red Hat, Inc.
- * Copyright © 2010 Google, Inc.
+ * Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
#include "hb-buffer-private.hh"
-#include "hb-ot-layout-gdef-private.hh"
+#include "hb-ot-layout-gdef-table.hh"
-HB_BEGIN_DECLS
-/* buffer var allocations */
-#define lig_id() var2.u8[2] /* unique ligature id */
-#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
+
+/* unique ligature id */
+/* component number in the ligature (0 = base) */
+static inline void
+set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
+{
+ info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F);
+}
+static inline unsigned int
+get_lig_id (hb_glyph_info_t &info)
+{
+ return info.lig_props() >> 4;
+}
+static inline unsigned int
+get_lig_comp (hb_glyph_info_t &info)
+{
+ return info.lig_props() & 0x0F;
+}
static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
- uint8_t lig_id = buffer->next_serial ();
- if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
+ uint8_t lig_id = buffer->next_serial () & 0x0F;
+ if (unlikely (!lig_id))
+ lig_id = allocate_lig_id (buffer); /* in case of overflow */
return lig_id;
}
+#ifndef HB_DEBUG_CLOSURE
+#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
+#endif
+
+#define TRACE_CLOSURE() \
+ hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
+
+
+/* TODO Add TRACE_RETURN annotation for would_apply */
+
+
+struct hb_closure_context_t
+{
+ hb_face_t *face;
+ hb_set_t *glyphs;
+ unsigned int nesting_level_left;
+ unsigned int debug_depth;
+
+
+ hb_closure_context_t (hb_face_t *face_,
+ hb_set_t *glyphs_,
+ unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
+ face (face_), glyphs (glyphs_),
+ nesting_level_left (nesting_level_left_),
+ debug_depth (0) {}
+};
+
+
+
#ifndef HB_DEBUG_APPLY
#define HB_DEBUG_APPLY (HB_DEBUG+0)
#endif
#define TRACE_APPLY() \
- hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC);
+ hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint);
-HB_BEGIN_DECLS
struct hb_apply_context_t
{
- unsigned int debug_depth;
hb_font_t *font;
hb_face_t *face;
hb_buffer_t *buffer;
hb_direction_t direction;
hb_mask_t lookup_mask;
- unsigned int context_length;
unsigned int nesting_level_left;
unsigned int lookup_props;
unsigned int property; /* propety of first glyph */
+ unsigned int debug_depth;
- inline void replace_glyph (hb_codepoint_t glyph_index) const
- {
- clear_property ();
- buffer->replace_glyph (glyph_index);
+ hb_apply_context_t (hb_font_t *font_,
+ hb_face_t *face_,
+ hb_buffer_t *buffer_,
+ hb_mask_t lookup_mask_) :
+ font (font_), face (face_), buffer (buffer_),
+ direction (buffer_->props.direction),
+ lookup_mask (lookup_mask_),
+ nesting_level_left (MAX_NESTING_LEVEL),
+ lookup_props (0), property (0), debug_depth (0) {}
+
+ void set_lookup (const Lookup &l) {
+ lookup_props = l.get_props ();
}
- inline void replace_glyphs_be16 (unsigned int num_in,
- unsigned int num_out,
- const uint16_t *glyph_data_be) const
+
+ struct mark_skipping_forward_iterator_t
{
- clear_property ();
- buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
- }
+ inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
+ unsigned int start_index_,
+ unsigned int num_items_,
+ bool context_match = false)
+ {
+ c = c_;
+ idx = start_index_;
+ num_items = num_items_;
+ mask = context_match ? -1 : c->lookup_mask;
+ syllable = context_match ? 0 : c->buffer->cur().syllable ();
+ end = c->buffer->len;
+ }
+ inline bool has_no_chance (void) const
+ {
+ return unlikely (num_items && idx + num_items >= end);
+ }
+ inline bool next (unsigned int *property_out,
+ unsigned int lookup_props)
+ {
+ assert (num_items > 0);
+ do
+ {
+ if (has_no_chance ())
+ return false;
+ idx++;
+ } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
+ num_items--;
+ return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ());
+ }
+ inline bool next (unsigned int *property_out = NULL)
+ {
+ return next (property_out, c->lookup_props);
+ }
- inline void guess_glyph_class (unsigned int klass)
+ unsigned int idx;
+ private:
+ hb_apply_context_t *c;
+ unsigned int num_items;
+ hb_mask_t mask;
+ uint8_t syllable;
+ unsigned int end;
+ };
+
+ struct mark_skipping_backward_iterator_t
{
- /* XXX if ! has gdef */
- buffer->info[buffer->idx].props_cache() = klass;
+ inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
+ unsigned int start_index_,
+ unsigned int num_items_,
+ hb_mask_t mask_ = 0,
+ bool match_syllable_ = true)
+ {
+ c = c_;
+ idx = start_index_;
+ num_items = num_items_;
+ mask = mask_ ? mask_ : c->lookup_mask;
+ syllable = match_syllable_ ? c->buffer->cur().syllable () : 0;
+ }
+ inline bool has_no_chance (void) const
+ {
+ return unlikely (idx < num_items);
+ }
+ inline bool prev (unsigned int *property_out,
+ unsigned int lookup_props)
+ {
+ assert (num_items > 0);
+ do
+ {
+ if (has_no_chance ())
+ return false;
+ idx--;
+ } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
+ num_items--;
+ return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ());
+ }
+ inline bool prev (unsigned int *property_out = NULL)
+ {
+ return prev (property_out, c->lookup_props);
+ }
+
+ unsigned int idx;
+ private:
+ hb_apply_context_t *c;
+ unsigned int num_items;
+ hb_mask_t mask;
+ uint8_t syllable;
+ };
+
+ inline bool should_mark_skip_current_glyph (void) const
+ {
+ return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL);
}
- private:
- inline void clear_property (void) const
+
+
+ inline void replace_glyph (hb_codepoint_t glyph_index,
+ unsigned int klass = 0) const
{
- /* XXX if has gdef */
- buffer->info[buffer->idx].props_cache() = 0;
+ buffer->cur().props_cache() = klass; /*XXX if has gdef? */
+ buffer->replace_glyph (glyph_index);
+ }
+ inline void replace_glyphs_be16 (unsigned int num_in,
+ unsigned int num_out,
+ const uint16_t *glyph_data_be,
+ unsigned int klass = 0) const
+ {
+ buffer->cur().props_cache() = klass; /* XXX if has gdef? */
+ buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
}
};
+typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
-struct ContextFuncs
+struct ContextClosureFuncs
+{
+ intersects_func_t intersects;
+ closure_lookup_func_t closure;
+};
+struct ContextApplyFuncs
{
match_func_t match;
apply_lookup_func_t apply;
};
+static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+{
+ return glyphs->has (value);
+}
+static inline bool intersects_class (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+ const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+ return class_def.intersects_class (glyphs, value);
+}
+static inline bool intersects_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+ const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+ return (data+coverage).intersects (glyphs);
+}
+
+static inline bool intersects_array (hb_closure_context_t *c,
+ unsigned int count,
+ const USHORT values[],
+ intersects_func_t intersects_func,
+ const void *intersects_data)
+{
+ for (unsigned int i = 0; i < count; i++)
+ if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
+ return false;
+ return true;
+}
+
static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
{
static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
{
const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
- return (data+coverage) (glyph_id) != NOT_COVERED;
+ return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
}
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;
}
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;
}
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
};
-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 */
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;
*/
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)
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);
friend struct RuleSet;
private:
- inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+
+ inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+ {
+ TRACE_CLOSURE ();
+ const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+ context_closure_lookup (c,
+ inputCount, input,
+ lookupCount, lookupRecord,
+ lookup_context);
+ }
+
+ inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
{
TRACE_APPLY ();
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
- return context_lookup (c,
- inputCount, input,
- lookupCount, lookupRecord,
- lookup_context);
+ return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
}
public:
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
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:
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:
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:
friend struct Context;
private:
+
+ inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+ {
+ TRACE_CLOSURE ();
+ if (!(this+coverage[0]).intersects (c->glyphs))
+ return;
+
+ const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_coverage, closure_func},
+ this
+ };
+ context_closure_lookup (c,
+ glyphCount, (const USHORT *) (coverage + 1),
+ lookupCount, lookupRecord,
+ lookup_context);
+ }
+
inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
{
TRACE_APPLY ();
- unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint);
- if (likely (index == NOT_COVERED))
- return false;
+ unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
- struct ContextLookupContext lookup_context = {
+ struct ContextApplyLookupContext lookup_context = {
{match_coverage, apply_func},
this
};
- return context_lookup (c,
- glyphCount, (const USHORT *) (coverage + 1),
- lookupCount, lookupRecord,
- lookup_context);
+ return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
}
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- if (!c->check_struct (this)) return false;
+ if (!c->check_struct (this)) return TRACE_RETURN (false);
unsigned int count = glyphCount;
- if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
+ if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
for (unsigned int i = 0; i < count; i++)
- if (!coverage[i].sanitize (c, this)) return false;
+ if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
- return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
+ return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
}
private:
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);
}
}
/* 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);
friend struct ChainRuleSet;
private:
- inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+
+ inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+ {
+ TRACE_CLOSURE ();
+ const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+ const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+ chain_context_closure_lookup (c,
+ backtrack.len, backtrack.array,
+ input.len, input.array,
+ lookahead.len, lookahead.array,
+ lookup.len, lookup.array,
+ lookup_context);
+ }
+
+ inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
{
TRACE_APPLY ();
const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
- return chain_context_lookup (c,
- backtrack.len, backtrack.array,
- input.len, input.array,
- lookahead.len, lookahead.array,
- lookup.len, lookup.array,
- lookup_context);
+ return TRACE_RETURN (chain_context_apply_lookup (c,
+ backtrack.len, backtrack.array,
+ input.len, input.array,
+ lookahead.len, lookahead.array, lookup.len,
+ lookup.array, lookup_context));
}
public:
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- if (!backtrack.sanitize (c)) return false;
+ if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
- if (!input.sanitize (c)) return false;
+ if (!input.sanitize (c)) return TRACE_RETURN (false);
ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
- if (!lookahead.sanitize (c)) return false;
+ if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
- return lookup.sanitize (c);
+ return TRACE_RETURN (lookup.sanitize (c));
}
private:
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:
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:
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:
private:
+ inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+ {
+ TRACE_CLOSURE ();
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+ if (!(this+input[0]).intersects (c->glyphs))
+ return;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_coverage, closure_func},
+ {this, this, this}
+ };
+ chain_context_closure_lookup (c,
+ backtrack.len, (const USHORT *) backtrack.array,
+ input.len, (const USHORT *) input.array + 1,
+ lookahead.len, (const USHORT *) lookahead.array,
+ lookup.len, lookup.array,
+ lookup_context);
+ }
+
inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
{
TRACE_APPLY ();
const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
- unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint);
- if (likely (index == NOT_COVERED))
- return false;
+ unsigned int index = (this+input[0]) (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
- struct ChainContextLookupContext lookup_context = {
+ struct ChainContextApplyLookupContext lookup_context = {
{match_coverage, apply_func},
{this, this, this}
};
- return chain_context_lookup (c,
- backtrack.len, (const USHORT *) backtrack.array,
- input.len, (const USHORT *) input.array + 1,
- lookahead.len, (const USHORT *) lookahead.array,
- lookup.len, lookup.array,
- lookup_context);
+ return TRACE_RETURN (chain_context_apply_lookup (c,
+ backtrack.len, (const USHORT *) backtrack.array,
+ input.len, (const USHORT *) input.array + 1,
+ lookahead.len, (const USHORT *) lookahead.array,
+ lookup.len, lookup.array, lookup_context));
}
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- if (!backtrack.sanitize (c, this)) return false;
+ if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
- if (!input.sanitize (c, this)) return false;
+ if (!input.sanitize (c, this)) return TRACE_RETURN (false);
OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
- if (!lookahead.sanitize (c, this)) return false;
+ if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
- return lookup.sanitize (c);
+ return TRACE_RETURN (lookup.sanitize (c));
}
private:
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);
}
}
inline bool sanitize (hb_sanitize_context_t *c) {
TRACE_SANITIZE ();
- return c->check_struct (this);
+ return TRACE_RETURN (c->check_struct (this));
}
private:
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);
}
}
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:
};
-HB_END_DECLS
#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
#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,
unsigned int *property_out);
-/*
- * head
- */
-
-HB_INTERNAL unsigned int
-_hb_ot_layout_get_upem (hb_face_t *face);
-
/*
* 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;
};
_hb_ot_layout_destroy (hb_ot_layout_t *layout);
-HB_END_DECLS
#endif /* HB_OT_LAYOUT_PRIVATE_HH */
* Red Hat Author(s): Behdad Esfahbod
*/
-#define HB_OT_LAYOUT_CC
-
#include "hb-ot-layout-private.hh"
-#include "hb-ot-layout-gdef-private.hh"
-#include "hb-ot-layout-gsub-private.hh"
-#include "hb-ot-layout-gpos-private.hh"
-#include "hb-ot-head-private.hh"
-#include "hb-ot-maxp-private.hh"
+#include "hb-ot-layout-gdef-table.hh"
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-maxp-table.hh"
#include <stdlib.h>
#include <string.h>
-HB_BEGIN_DECLS
hb_ot_layout_t *
layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
- layout->head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
- layout->head = Sanitizer<head>::lock_instance (layout->head_blob);
-
return layout;
}
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);
}
{
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);
-}
/*
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
if (g.find_script_index (*script_tags, script_index)) {
if (chosen_script)
*chosen_script = *script_tags;
- return TRUE;
+ return true;
}
script_tags++;
}
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
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
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
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
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
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
}
-/*
- * head
- */
-
-unsigned int
-_hb_ot_layout_get_upem (hb_face_t *face)
-{
- return _get_head (face).get_upem ();
-}
-
-
-HB_END_DECLS
* Red Hat Author(s): Behdad Esfahbod
*/
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
#ifndef HB_OT_LAYOUT_H
#define HB_OT_LAYOUT_H
-#include "hb-common.h"
-#include "hb-buffer.h"
-#include "hb-font.h"
+#include "hb.h"
#include "hb-ot-tag.h"
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
*/
#include "hb-ot-layout.h"
-HB_BEGIN_DECLS
static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
public:
+ hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); }
+
typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data);
typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data);
inline 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 ();
{
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)
};
-HB_END_DECLS
#endif /* HB_OT_MAP_PRIVATE_HH */
#include "hb-ot-shape-private.hh"
-HB_BEGIN_DECLS
void
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)
{
}
-HB_END_DECLS
/*
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
-#ifndef HB_OT_MAXP_PRIVATE_HH
-#define HB_OT_MAXP_PRIVATE_HH
+#ifndef HB_OT_MAXP_TABLE_HH
+#define HB_OT_MAXP_TABLE_HH
#include "hb-open-type-private.hh"
-HB_BEGIN_DECLS
/*
- * maxp
+ * maxp -- The Maximum Profile Table
*/
#define HB_OT_TAG_maxp HB_TAG('m','a','x','p')
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. */
};
-HB_END_DECLS
-#endif /* HB_OT_MAXP_PRIVATE_HH */
+#endif /* HB_OT_MAXP_TABLE_HH */
--- /dev/null
+/*
+ * 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 */
-/*
- * 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[] =
{
#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 == */
*/
#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 */
/*
static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
{
- /* TODO Macroize the magic bit operations */
-
if (likely (hb_in_range<hb_codepoint_t> (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) {
unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST];
if (likely (j_type != JOINING_TYPE_X))
JOINING_TYPE_T : JOINING_TYPE_U;
}
+static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape)
+{
+ if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4)
+ return shaping_table[u - SHAPING_TABLE_FIRST][shape];
+ return u;
+}
+static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second)
+{
+ if (unlikely (!second)) return 0;
+ for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
+ if (ligature_table[i].first == first)
+ for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
+ if (ligature_table[i].ligatures[j].second == second)
+ return ligature_table[i].ligatures[j].ligature;
+ return 0;
+}
static const hb_tag_t arabic_syriac_features[] =
{
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
*/
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);
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;
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;
}
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
--- /dev/null
+
+#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 */
/*
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
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;
}%%
--- /dev/null
+/*
+ * 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 */
-/*
- * 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:
*
* 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
/* 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) */
/* 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) */
/* 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) */
/* 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) */
/* 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)
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);
#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 */
/*
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-private.hh"
-HB_BEGIN_DECLS
-
-
-/* buffer var allocations */
-#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
-#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
-
-#define INDIC_TABLE_ELEMENT_TYPE uint8_t
-
-/* Cateories used in the OpenType spec:
- * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum indic_category_t {
- OT_X = 0,
- OT_C,
- OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
- OT_V,
- OT_N,
- OT_H,
- OT_ZWNJ,
- OT_ZWJ,
- OT_M,
- OT_SM,
- OT_VD,
- OT_A,
- OT_NBSP
+struct indic_options_t
+{
+ int initialized : 1;
+ int uniscribe_bug_compatible : 1;
};
-/* Visual positions in a syllable from left to right. */
-enum indic_position_t {
- POS_PRE,
- POS_BASE,
- POS_ABOVE,
- POS_BELOW,
- POS_POST,
+union indic_options_union_t {
+ int i;
+ indic_options_t opts;
};
+ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t));
-/* Categories used in IndicSyllabicCategory.txt from UCD */
-/* The assignments are guesswork */
-enum indic_syllabic_category_t {
- INDIC_SYLLABIC_CATEGORY_OTHER = OT_X,
-
- INDIC_SYLLABIC_CATEGORY_AVAGRAHA = OT_X,
- INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM,
- INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_NBSP,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_C,
- INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA = OT_C,
- INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X,
- INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N,
- INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_X,
- INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X,
- INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_X,
- INDIC_SYLLABIC_CATEGORY_VIRAMA = OT_H,
- INDIC_SYLLABIC_CATEGORY_VISARGA = OT_SM,
- INDIC_SYLLABIC_CATEGORY_VOWEL = OT_V,
- INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT = OT_M,
- INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT = OT_V
-};
+static indic_options_union_t
+indic_options_init (void)
+{
+ indic_options_union_t u;
+ u.i = 0;
+ u.opts.initialized = 1;
-/* Categories used in IndicSMatraCategory.txt from UCD */
-enum indic_matra_category_t {
- INDIC_MATRA_CATEGORY_NOT_APPLICABLE = POS_BASE,
-
- INDIC_MATRA_CATEGORY_LEFT = POS_PRE,
- INDIC_MATRA_CATEGORY_TOP = POS_ABOVE,
- INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW,
- INDIC_MATRA_CATEGORY_RIGHT = POS_POST,
-
- /* We don't really care much about these since we decompose them
- * in the generic pre-shaping layer. They will only be used if
- * the font does not cover the decomposition. In which case, we
- * define these as aliases to the place we want the split-matra
- * glyph to show up. Quite arbitrary. */
- INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_BOTTOM,
- INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_LEFT,
- INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM,
- INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_BOTTOM,
- INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_LEFT,
- INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_LEFT,
- INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
-
- INDIC_MATRA_CATEGORY_INVISIBLE = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
- INDIC_MATRA_CATEGORY_OVERSTRUCK = INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
- INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = INDIC_MATRA_CATEGORY_NOT_APPLICABLE
-};
+ char *c = getenv ("HB_OT_INDIC_OPTIONS");
+ u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+
+ return u;
+}
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
-#define INDIC_COMBINE_CATEGORIES(S,M) \
- (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
- ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
- ((M << 4) | S))
+inline indic_options_t
+indic_options (void)
+{
+ static indic_options_union_t options;
-#include "hb-ot-shape-complex-indic-table.hh"
+ if (unlikely (!options.i)) {
+ /* This is idempotent and threadsafe. */
+ options = indic_options_init ();
+ }
-/* XXX
- * This is a hack for now. We should:
- * 1. Move this data into the main Indic table,
- * and/or
- * 2. Probe font lookups to determine consonant positions.
- */
-static const struct consonant_position_t {
- hb_codepoint_t u;
- indic_position_t position;
-} consonant_positions[] = {
- {0x0930, POS_BELOW},
- {0x09AC, POS_BELOW},
- {0x09AF, POS_POST},
- {0x09B0, POS_BELOW},
- {0x09F0, POS_BELOW},
- {0x0A2F, POS_POST},
- {0x0A30, POS_BELOW},
- {0x0A35, POS_BELOW},
- {0x0A39, POS_BELOW},
- {0x0AB0, POS_BELOW},
- {0x0B24, POS_BELOW},
- {0x0B28, POS_BELOW},
- {0x0B2C, POS_BELOW},
- {0x0B2D, POS_BELOW},
- {0x0B2E, POS_BELOW},
- {0x0B2F, POS_POST},
- {0x0B30, POS_BELOW},
- {0x0B32, POS_BELOW},
- {0x0B33, POS_BELOW},
- {0x0B5F, POS_POST},
- {0x0B71, POS_BELOW},
- {0x0C15, POS_BELOW},
- {0x0C16, POS_BELOW},
- {0x0C17, POS_BELOW},
- {0x0C18, POS_BELOW},
- {0x0C19, POS_BELOW},
- {0x0C1A, POS_BELOW},
- {0x0C1B, POS_BELOW},
- {0x0C1C, POS_BELOW},
- {0x0C1D, POS_BELOW},
- {0x0C1E, POS_BELOW},
- {0x0C1F, POS_BELOW},
- {0x0C20, POS_BELOW},
- {0x0C21, POS_BELOW},
- {0x0C22, POS_BELOW},
- {0x0C23, POS_BELOW},
- {0x0C24, POS_BELOW},
- {0x0C25, POS_BELOW},
- {0x0C26, POS_BELOW},
- {0x0C27, POS_BELOW},
- {0x0C28, POS_BELOW},
- {0x0C2A, POS_BELOW},
- {0x0C2B, POS_BELOW},
- {0x0C2C, POS_BELOW},
- {0x0C2D, POS_BELOW},
- {0x0C2E, POS_BELOW},
- {0x0C2F, POS_BELOW},
- {0x0C30, POS_BELOW},
- {0x0C32, POS_BELOW},
- {0x0C33, POS_BELOW},
- {0x0C35, POS_BELOW},
- {0x0C36, POS_BELOW},
- {0x0C37, POS_BELOW},
- {0x0C38, POS_BELOW},
- {0x0C39, POS_BELOW},
- {0x0C95, POS_BELOW},
- {0x0C96, POS_BELOW},
- {0x0C97, POS_BELOW},
- {0x0C98, POS_BELOW},
- {0x0C99, POS_BELOW},
- {0x0C9A, POS_BELOW},
- {0x0C9B, POS_BELOW},
- {0x0C9C, POS_BELOW},
- {0x0C9D, POS_BELOW},
- {0x0C9E, POS_BELOW},
- {0x0C9F, POS_BELOW},
- {0x0CA0, POS_BELOW},
- {0x0CA1, POS_BELOW},
- {0x0CA2, POS_BELOW},
- {0x0CA3, POS_BELOW},
- {0x0CA4, POS_BELOW},
- {0x0CA5, POS_BELOW},
- {0x0CA6, POS_BELOW},
- {0x0CA7, POS_BELOW},
- {0x0CA8, POS_BELOW},
- {0x0CAA, POS_BELOW},
- {0x0CAB, POS_BELOW},
- {0x0CAC, POS_BELOW},
- {0x0CAD, POS_BELOW},
- {0x0CAE, POS_BELOW},
- {0x0CAF, POS_BELOW},
- {0x0CB0, POS_BELOW},
- {0x0CB2, POS_BELOW},
- {0x0CB3, POS_BELOW},
- {0x0CB5, POS_BELOW},
- {0x0CB6, POS_BELOW},
- {0x0CB7, POS_BELOW},
- {0x0CB8, POS_BELOW},
- {0x0CB9, POS_BELOW},
- {0x0CDE, POS_BELOW},
- {0x0D2F, POS_POST},
- {0x0D30, POS_POST},
- {0x0D32, POS_BELOW},
- {0x0D35, POS_POST},
-};
+ return options.opts;
+}
-/* XXX
- * This is a hack for now. We should move this data into the main Indic table.
- */
-static const hb_codepoint_t ra_chars[] = {
- 0x0930, /* Devanagari */
- 0x09B0, /* Bengali */
- 0x09F0, /* Bengali */
-//0x09F1, /* Bengali */
-//0x0A30, /* Gurmukhi */
- 0x0AB0, /* Gujarati */
- 0x0B30, /* Oriya */
-//0x0BB0, /* Tamil */
-//0x0C30, /* Telugu */
- 0x0CB0, /* Kannada */
-//0x0D30, /* Malayalam */
-};
static int
compare_codepoint (const void *pa, const void *pb)
sizeof (consonant_positions[0]),
compare_codepoint);
- return record ? record->position : POS_BASE;
+ return record ? record->position : POS_BASE_C;
}
static bool
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 */
_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
};
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
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);
unsigned int count = buffer->len;
for (unsigned int i = 0; i < count; i++)
{
- unsigned int type = get_indic_categories (buffer->info[i].codepoint);
-
- buffer->info[i].indic_category() = type & 0x0F;
- buffer->info[i].indic_position() = type >> 4;
-
- if (buffer->info[i].indic_category() == OT_C) {
- buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
- if (is_ra (buffer->info[i].codepoint))
- buffer->info[i].indic_category() = OT_Ra;
- } else if (buffer->info[i].codepoint == 0x200C)
- buffer->info[i].indic_category() = OT_ZWNJ;
- else if (buffer->info[i].codepoint == 0x200D)
- buffer->info[i].indic_category() = OT_ZWJ;
+ hb_glyph_info_t &info = buffer->info[i];
+ unsigned int type = get_indic_categories (info.codepoint);
+
+ info.indic_category() = type & 0x0F;
+ info.indic_position() = type >> 4;
+
+ /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe
+ * treats U+0951..U+0952 all as OT_VD.
+ * TESTS:
+ * U+092E,U+0947,U+0952
+ * U+092E,U+0952,U+0947
+ * U+092E,U+0947,U+0951
+ * U+092E,U+0951,U+0947
+ * */
+ if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954)))
+ info.indic_category() = OT_VD;
+
+ if (info.indic_category() == OT_C) {
+ info.indic_position() = consonant_position (info.codepoint);
+ if (is_ra (info.codepoint))
+ info.indic_category() = OT_Ra;
+ } else if (info.indic_category() == OT_SM ||
+ info.indic_category() == OT_VD) {
+ info.indic_position() = POS_SMVD;
+ } else if (unlikely (info.codepoint == 0x200C))
+ info.indic_category() = OT_ZWNJ;
+ else if (unlikely (info.codepoint == 0x200D))
+ info.indic_category() = OT_ZWJ;
+ else if (unlikely (info.codepoint == 0x25CC))
+ info.indic_category() = OT_DOTTEDCIRCLE;
}
}
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:
*
*/
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:
/* 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. */
}
/* 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;
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]));
}
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. */
#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)
{
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
* 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:
+ /* 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
#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
*/
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 ();
+}
/*
- * Copyright © 2010,2011 Google, Inc.
+ * Copyright © 2010,2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#include "hb-private.hh"
#include "hb-ot-map-private.hh"
+#include "hb-ot-shape-normalize-private.hh"
-HB_BEGIN_DECLS
/* buffer var allocations, used during the entire shaping process */
-#define general_category() var1.u8[0] /* unicode general_category (hb_unicode_general_category_t) */
-#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */
+#define unicode_props0() var1.u8[0]
+#define unicode_props1() var1.u8[1]
+
+/* buffer var allocations, used during the GSUB/GPOS processing */
+#define props_cache() var1.u16[1] /* GSUB/GPOS glyph_props cache */
+#define syllable() var2.u8[0] /* GSUB/GPOS shaping boundaries */
+#define lig_props() var2.u8[1] /* GSUB/GPOS ligature tracking */
/* buffer var allocations, used by complex shapers */
-#define complex_var_persistent_u8_0() var2.u8[0]
-#define complex_var_persistent_u8_1() var2.u8[1]
-#define complex_var_persistent_u16() var2.u16[0]
-#define complex_var_temporary_u8_0() var2.u8[2]
-#define complex_var_temporary_u8_1() var2.u8[3]
-#define complex_var_temporary_u16() var2.u16[1]
+#define complex_var_persistent_u8_0() var2.u8[2]
+#define complex_var_persistent_u8_1() var2.u8[3]
+#define complex_var_temporary_u8() var2.u8[0]
#define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
/* ^--- Add new shapers here */
enum hb_ot_complex_shaper_t {
#define HB_COMPLEX_SHAPER_IMPLEMENT(name) hb_ot_complex_shaper_##name,
HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
+ /* Just here to avoid enum trailing comma: */
+ hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default
#undef HB_COMPLEX_SHAPER_IMPLEMENT
};
static inline hb_ot_complex_shaper_t
hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
{
- switch ((int) props->script)
+ switch ((hb_tag_t) props->script)
{
default:
return hb_ot_complex_shaper_default;
+
+ /* Unicode-1.1 additions */
case HB_SCRIPT_ARABIC:
- case HB_SCRIPT_MANDAIC:
case HB_SCRIPT_MONGOLIAN:
- case HB_SCRIPT_NKO:
case HB_SCRIPT_SYRIAC:
+
+ /* Unicode-5.0 additions */
+ case HB_SCRIPT_NKO:
+
+ /* Unicode-6.0 additions */
+ case HB_SCRIPT_MANDAIC:
+
return hb_ot_complex_shaper_arabic;
- /* TODO: These are all the scripts that the ucd/IndicSyllabicCategory.txt covers.
- * Quite possibly many of these need no shaping, and some other are encoded visually.
- * Needs to be refined.
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_HANGUL:
+
+ return hb_ot_complex_shaper_hangul;
+
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_THAI:
+ case HB_SCRIPT_LAO:
+
+ return hb_ot_complex_shaper_thai;
+
+
+
+ /* ^--- Add new shapers here */
+
+
+#if 0
+ /* Note:
+ *
+ * These disabled scripts are listed in ucd/IndicSyllabicCategory.txt, but according
+ * to Martin Hosken and Jonathan Kew do not require complex shaping.
+ *
+ * TODO We should automate figuring out which scripts do not need complex shaping
+ *
+ * TODO We currently keep data for these scripts in our indic table. Need to fix the
+ * generator to not do that.
*/
- case HB_SCRIPT_BALINESE:
+
+
+ /* Simple? */
+
+ /* Unicode-3.2 additions */
+ case HB_SCRIPT_BUHID:
+ case HB_SCRIPT_HANUNOO:
+
+ /* Unicode-5.1 additions */
+ case HB_SCRIPT_SAURASHTRA:
+
+ /* Unicode-5.2 additions */
+ case HB_SCRIPT_MEETEI_MAYEK:
+
+ /* Unicode-6.0 additions */
case HB_SCRIPT_BATAK:
- case HB_SCRIPT_BENGALI:
case HB_SCRIPT_BRAHMI:
- case HB_SCRIPT_BUGINESE:
- case HB_SCRIPT_BUHID:
- case HB_SCRIPT_CHAM:
+
+
+ /* Simple */
+
+ /* Unicode-1.1 additions */
+ /* These have their own shaper now. */
+ case HB_SCRIPT_LAO:
+ case HB_SCRIPT_THAI:
+
+ /* Unicode-2.0 additions */
+ case HB_SCRIPT_TIBETAN:
+
+ /* Unicode-3.2 additions */
+ case HB_SCRIPT_TAGALOG:
+ case HB_SCRIPT_TAGBANWA:
+
+ /* Unicode-4.0 additions */
+ case HB_SCRIPT_LIMBU:
+ case HB_SCRIPT_TAI_LE:
+
+ /* Unicode-4.1 additions */
+ case HB_SCRIPT_SYLOTI_NAGRI:
+
+ /* Unicode-5.0 additions */
+ case HB_SCRIPT_PHAGS_PA:
+
+ /* Unicode-5.1 additions */
+ case HB_SCRIPT_KAYAH_LI:
+
+ /* Unicode-5.2 additions */
+ case HB_SCRIPT_TAI_VIET:
+
+
+ /* May need Indic treatment in the future? */
+
+ /* Unicode-3.0 additions */
+ case HB_SCRIPT_MYANMAR:
+
+
+#endif
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_BENGALI:
case HB_SCRIPT_DEVANAGARI:
case HB_SCRIPT_GUJARATI:
case HB_SCRIPT_GURMUKHI:
- case HB_SCRIPT_HANUNOO:
- case HB_SCRIPT_JAVANESE:
- case HB_SCRIPT_KAITHI:
case HB_SCRIPT_KANNADA:
- case HB_SCRIPT_KAYAH_LI:
- case HB_SCRIPT_KHAROSHTHI:
- case HB_SCRIPT_KHMER:
- case HB_SCRIPT_LAO:
- case HB_SCRIPT_LEPCHA:
- case HB_SCRIPT_LIMBU:
case HB_SCRIPT_MALAYALAM:
- case HB_SCRIPT_MEETEI_MAYEK:
- case HB_SCRIPT_MYANMAR:
- case HB_SCRIPT_NEW_TAI_LUE:
case HB_SCRIPT_ORIYA:
- case HB_SCRIPT_PHAGS_PA:
- case HB_SCRIPT_REJANG:
- case HB_SCRIPT_SAURASHTRA:
+ case HB_SCRIPT_TAMIL:
+ case HB_SCRIPT_TELUGU:
+
+ /* Unicode-3.0 additions */
+ case HB_SCRIPT_KHMER:
case HB_SCRIPT_SINHALA:
+
+ /* Unicode-4.1 additions */
+ case HB_SCRIPT_BUGINESE:
+ case HB_SCRIPT_KHAROSHTHI:
+ case HB_SCRIPT_NEW_TAI_LUE:
+
+ /* Unicode-5.0 additions */
+ case HB_SCRIPT_BALINESE:
+
+ /* Unicode-5.1 additions */
+ case HB_SCRIPT_CHAM:
+ case HB_SCRIPT_LEPCHA:
+ case HB_SCRIPT_REJANG:
case HB_SCRIPT_SUNDANESE:
- case HB_SCRIPT_SYLOTI_NAGRI:
- case HB_SCRIPT_TAGALOG:
- case HB_SCRIPT_TAGBANWA:
- case HB_SCRIPT_TAI_LE:
+
+ /* Unicode-5.2 additions */
+ case HB_SCRIPT_JAVANESE:
+ case HB_SCRIPT_KAITHI:
case HB_SCRIPT_TAI_THAM:
- case HB_SCRIPT_TAI_VIET:
- case HB_SCRIPT_TAMIL:
- case HB_SCRIPT_TELUGU:
- case HB_SCRIPT_THAI:
- case HB_SCRIPT_TIBETAN:
+
+ /* Unicode-6.1 additions */
+ case HB_SCRIPT_CHAKMA:
+ case HB_SCRIPT_SHARADA:
+ case HB_SCRIPT_TAKRI:
+
return hb_ot_complex_shaper_indic;
/* ^--- Add new shapers here */
/*
- * 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
}
* 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
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 */
--- /dev/null
+/*
+ * 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 */
/*
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Google Author(s): Behdad Esfahbod
*/
+#include "hb-ot-shape-normalize-private.hh"
#include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-complex-private.hh"
-HB_BEGIN_DECLS
/*
* HIGHLEVEL DESIGN:
* 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.
*
* 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.
*
*/
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
{
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! */
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
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
#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;
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 */
*/
#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:
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,
}
+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++)
{
/* 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
{
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
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 ();
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 */
&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;
}
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! */
/* 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);
hb_substitute_default (c);
hb_ot_substitute_complex (c);
-
- if (!c->applied_substitute_complex)
- hb_substitute_complex_fallback (c);
}
/* POSITION */
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;
{
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);
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));
+}
#include <string.h>
-HB_BEGIN_DECLS
/* hb_script_t */
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;
}
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";
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);
}
}
}
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
* Red Hat Author(s): Behdad Esfahbod
*/
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
#ifndef HB_OT_TAG_H
#define HB_OT_TAG_H
-#include "hb-common.h"
+#include "hb.h"
HB_BEGIN_DECLS
#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 */
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
- * Copyright © 2011 Google, Inc.
+ * Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
#ifndef HB_PRIVATE_HH
#define HB_PRIVATE_HH
-#if HAVE_CONFIG_H
+#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include "hb-common.h"
+#include "hb.h"
+#define HB_H_IN
+#ifdef HAVE_OT
+#include "hb-ot.h"
+#define HB_OT_H_IN
+#endif
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <stdarg.h>
-HB_BEGIN_DECLS
/* Essentials */
# define NULL ((void *) 0)
#endif
-#undef FALSE
-#define FALSE 0
-
-#undef TRUE
-#define TRUE 1
-
/* Basics */
-HB_END_DECLS
#undef MIN
template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
#undef MAX
template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
-HB_BEGIN_DECLS
#undef ARRAY_LENGTH
#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
#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. */
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 */
#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))
#endif
#ifndef HB_INTERNAL
-# define HB_INTERNAL __attribute__((__visibility__("hidden")))
+# ifndef __MINGW32__
+# define HB_INTERNAL __attribute__((__visibility__("hidden")))
+# else
+# define HB_INTERNAL
+# endif
#endif
typedef int (*hb_compare_func_t) (const void *, const void *);
-HB_END_DECLS
/* arrays and maps */
+#define HB_PREALLOCED_ARRAY_INIT {0}
template <typename Type, unsigned int StaticSize>
-struct hb_prealloced_array_t {
-
+struct hb_prealloced_array_t
+{
unsigned int len;
unsigned int allocated;
Type *array;
Type static_array[StaticSize];
+ void init (void) { memset (this, 0, sizeof (*this)); }
+
inline Type& operator [] (unsigned int i) { return array[i]; }
inline const Type& operator [] (unsigned int i) const { return array[i]; }
}
};
-template <typename Type>
-struct hb_array_t : hb_prealloced_array_t<Type, 2> {};
-
+#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT}
template <typename item_t, typename lock_t>
struct hb_lockable_set_t
{
- hb_array_t <item_t> items;
+ hb_prealloced_array_t <item_t, 2> items;
+
+ inline void init (void) { items.init (); }
template <typename T>
- inline item_t *replace_or_insert (T v, lock_t &l)
+ inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
{
l.lock ();
item_t *item = items.find (v);
if (item) {
- item_t old = *item;
- *item = v;
- l.unlock ();
- old.finish ();
+ if (replace) {
+ item_t old = *item;
+ *item = v;
+ l.unlock ();
+ old.finish ();
+ }
+ else {
+ item = NULL;
+ l.unlock ();
+ }
} else {
item = items.push ();
if (likely (item))
};
-HB_BEGIN_DECLS
/* Big-endian handling */
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])
/* Debug */
-HB_END_DECLS
#ifndef HB_DEBUG
#define HB_DEBUG 0
#define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
#define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
+_hb_debug_msg_va (const char *what,
+ const void *obj,
+ const char *func,
+ bool indented,
+ unsigned int level,
+ int level_dir,
+ const char *message,
+ va_list ap)
+{
+ if (!_hb_debug (level, max_level))
+ return;
+
+ fprintf (stderr, "%-10s", what ? what : "");
+
+ if (obj)
+ fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
+ else
+ fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), "");
+
+ if (indented) {
+ static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
+ fprintf (stderr, "%2d %s├%s",
+ level,
+ bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
+ level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴");
+ } else
+ fprintf (stderr, " ├╴");
+
+ if (func) {
+ /* If there's a class name, just write that. */
+ const char *dotdot = strstr (func, "::");
+ const char *space = strchr (func, ' ');
+ if (space && dotdot && space < dotdot)
+ func = space + 1;
+ unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+ fprintf (stderr, "%.*s: ", func_len, func);
+ }
+
+ if (message)
+ vfprintf (stderr, message, ap);
+
+ fprintf (stderr, "\n");
+}
+template <> inline void
+_hb_debug_msg_va<0> (const char *what HB_UNUSED,
+ const void *obj HB_UNUSED,
+ const char *func HB_UNUSED,
+ bool indented HB_UNUSED,
+ unsigned int level HB_UNUSED,
+ int level_dir HB_UNUSED,
+ const char *message HB_UNUSED,
+ va_list ap HB_UNUSED) {}
+
+template <int max_level> inline void
_hb_debug_msg (const char *what,
const void *obj,
const char *func,
bool indented,
- int level,
+ unsigned int level,
+ int level_dir,
const char *message,
- ...) HB_PRINTF_FUNC(6, 7);
-template <int max_level> inline bool /* always returns TRUE */
+ ...) HB_PRINTF_FUNC(7, 8);
+template <int max_level> inline void
_hb_debug_msg (const char *what,
const void *obj,
const char *func,
bool indented,
- int level,
+ unsigned int level,
+ int level_dir,
const char *message,
...)
{
va_list ap;
va_start (ap, message);
-
- (void) (_hb_debug (level, max_level) &&
- fprintf (stderr, "%s(%p): ", what, obj) &&
- (func && fprintf (stderr, "%s: ", func), TRUE) &&
- (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
- vfprintf (stderr, message, ap) &&
- fprintf (stderr, "\n"));
-
+ _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
va_end (ap);
-
- return TRUE;
-}
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
- const void *obj,
- const char *func,
- bool indented,
- int level,
- const char *message,
- ...) HB_PRINTF_FUNC(6, 7);
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
- const void *obj,
- const char *func,
- bool indented,
- int level,
- const char *message,
- ...)
-{
- return TRUE;
}
-
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, FALSE, (LEVEL), __VA_ARGS__)
-#define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__)
-#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__)
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+ const void *obj HB_UNUSED,
+ const char *func HB_UNUSED,
+ bool indented HB_UNUSED,
+ unsigned int level HB_UNUSED,
+ int level_dir HB_UNUSED,
+ const char *message HB_UNUSED,
+ ...) HB_PRINTF_FUNC(7, 8);
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+ const void *obj HB_UNUSED,
+ const char *func HB_UNUSED,
+ bool indented HB_UNUSED,
+ unsigned int level HB_UNUSED,
+ int level_dir HB_UNUSED,
+ const char *message HB_UNUSED,
+ ...) {}
+
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
+#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, false, 0, 0, __VA_ARGS__)
+#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__)
/*
template <int max_level>
struct hb_auto_trace_t {
explicit inline hb_auto_trace_t (unsigned int *plevel_,
- const char *what,
- const void *obj,
+ const char *what_,
+ const void *obj_,
const char *func,
- const char *message) : plevel(plevel_)
+ const char *message,
+ ...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
{
- if (max_level) ++*plevel;
- /* TODO support variadic args here */
- _hb_debug_msg<max_level> (what, obj, func, TRUE, *plevel, "%s", message);
+ if (plevel) ++*plevel;
+
+ va_list ap;
+ va_start (ap, message);
+ _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
+ va_end (ap);
+ }
+ inline ~hb_auto_trace_t (void)
+ {
+ if (unlikely (!returned)) {
+ fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN. This is a bug, please report. Level was %d.\n", plevel ? *plevel : -1);
+ _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " ");
+ return;
+ }
+
+ if (plevel) --*plevel;
+ }
+
+ inline bool ret (bool v)
+ {
+ if (unlikely (returned)) {
+ fprintf (stderr, "OUCH, double calls to TRACE_RETURN. This is a bug, please report.\n");
+ return v;
+ }
+
+ _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
+ if (plevel) --*plevel;
+ plevel = NULL;
+ returned = true;
+ return v;
}
- ~hb_auto_trace_t (void) { if (max_level) --*plevel; }
private:
unsigned int *plevel;
+ bool returned;
+ const char *what;
+ const void *obj;
};
template <> /* Optimize when tracing is disabled */
struct hb_auto_trace_t<0> {
- explicit inline hb_auto_trace_t (unsigned int *plevel_,
- const char *what,
- const void *obj,
- const char *func,
- const char *message) {}
+ explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED,
+ const char *what HB_UNUSED,
+ const void *obj HB_UNUSED,
+ const char *func HB_UNUSED,
+ const char *message HB_UNUSED,
+ ...) {}
+
+ template <typename T>
+ inline T ret (T v) { return v; }
};
+#define TRACE_RETURN(RET) trace.ret (RET)
/* Misc */
}
-HB_BEGIN_DECLS
-HB_END_DECLS
#endif /* HB_PRIVATE_HH */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-set-private.hh"
+
+
+
+/* Public API */
+
+
+hb_set_t *
+hb_set_create ()
+{
+ hb_set_t *set;
+
+ if (!(set = hb_object_create<hb_set_t> ()))
+ return hb_set_get_empty ();
+
+ set->clear ();
+
+ return set;
+}
+
+hb_set_t *
+hb_set_get_empty (void)
+{
+ static const hb_set_t _hb_set_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ {0} /* elts */
+ };
+
+ return const_cast<hb_set_t *> (&_hb_set_nil);
+}
+
+hb_set_t *
+hb_set_reference (hb_set_t *set)
+{
+ return hb_object_reference (set);
+}
+
+void
+hb_set_destroy (hb_set_t *set)
+{
+ if (!hb_object_destroy (set)) return;
+
+ set->fini ();
+
+ free (set);
+}
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t *set,
+ hb_user_data_key_t *key,
+ void * data,
+ hb_destroy_func_t destroy,
+ hb_bool_t replace)
+{
+ return hb_object_set_user_data (set, key, data, destroy, replace);
+}
+
+void *
+hb_set_get_user_data (hb_set_t *set,
+ hb_user_data_key_t *key)
+{
+ return hb_object_get_user_data (set, key);
+}
+
+
+hb_bool_t
+hb_set_allocation_successful (hb_set_t *set HB_UNUSED)
+{
+ return true;
+}
+
+void
+hb_set_clear (hb_set_t *set)
+{
+ set->clear ();
+}
+
+hb_bool_t
+hb_set_empty (hb_set_t *set)
+{
+ return set->empty ();
+}
+
+hb_bool_t
+hb_set_has (hb_set_t *set,
+ hb_codepoint_t codepoint)
+{
+ return set->has (codepoint);
+}
+
+void
+hb_set_add (hb_set_t *set,
+ hb_codepoint_t codepoint)
+{
+ set->add (codepoint);
+}
+
+void
+hb_set_del (hb_set_t *set,
+ hb_codepoint_t codepoint)
+{
+ set->del (codepoint);
+}
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+ hb_set_t *other)
+{
+ return set->equal (other);
+}
+
+void
+hb_set_set (hb_set_t *set,
+ hb_set_t *other)
+{
+ set->set (other);
+}
+
+void
+hb_set_union (hb_set_t *set,
+ hb_set_t *other)
+{
+ set->union_ (other);
+}
+
+void
+hb_set_intersect (hb_set_t *set,
+ hb_set_t *other)
+{
+ set->intersect (other);
+}
+
+void
+hb_set_subtract (hb_set_t *set,
+ hb_set_t *other)
+{
+ set->subtract (other);
+}
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+ hb_set_t *other)
+{
+ set->symmetric_difference (other);
+}
+
+hb_codepoint_t
+hb_set_min (hb_set_t *set)
+{
+ return set->get_min ();
+}
+
+hb_codepoint_t
+hb_set_max (hb_set_t *set)
+{
+ return set->get_max ();
+}
+
+hb_bool_t
+hb_set_next (hb_set_t *set,
+ hb_codepoint_t *codepoint)
+{
+ return set->next (codepoint);
+}
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_SET_H
+#define HB_SET_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+typedef struct _hb_set_t hb_set_t;
+
+
+hb_set_t *
+hb_set_create (void);
+
+hb_set_t *
+hb_set_get_empty (void);
+
+hb_set_t *
+hb_set_reference (hb_set_t *set);
+
+void
+hb_set_destroy (hb_set_t *set);
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t *set,
+ hb_user_data_key_t *key,
+ void * data,
+ hb_destroy_func_t destroy,
+ hb_bool_t replace);
+
+void *
+hb_set_get_user_data (hb_set_t *set,
+ hb_user_data_key_t *key);
+
+
+/* Returns false if allocation has failed before */
+hb_bool_t
+hb_set_allocation_successful (hb_set_t *set);
+
+void
+hb_set_clear (hb_set_t *set);
+
+hb_bool_t
+hb_set_empty (hb_set_t *set);
+
+hb_bool_t
+hb_set_has (hb_set_t *set,
+ hb_codepoint_t codepoint);
+
+/* Right now limited to 16-bit integers. Eventually will do full codepoint range, sans -1
+ * which we will use as a sentinel. */
+void
+hb_set_add (hb_set_t *set,
+ hb_codepoint_t codepoint);
+
+void
+hb_set_del (hb_set_t *set,
+ hb_codepoint_t codepoint);
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+ hb_set_t *other);
+
+void
+hb_set_set (hb_set_t *set,
+ hb_set_t *other);
+
+void
+hb_set_union (hb_set_t *set,
+ hb_set_t *other);
+
+void
+hb_set_intersect (hb_set_t *set,
+ hb_set_t *other);
+
+void
+hb_set_subtract (hb_set_t *set,
+ hb_set_t *other);
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+ hb_set_t *other);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_min (hb_set_t *set);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_max (hb_set_t *set);
+
+/* Pass -1 in to get started. */
+hb_bool_t
+hb_set_next (hb_set_t *set,
+ hb_codepoint_t *codepoint);
+
+/* TODO: Add faster iteration API? */
+
+
+HB_END_DECLS
+
+#endif /* HB_SET_H */
#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);
+}
* Red Hat Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_SHAPE_H
#define HB_SHAPE_H
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
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-font-private.hh" /* Shall be first since may include windows.h */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-hmtx-table.hh"
+
+#include "hb-blob.h"
+
+#include <string.h>
+
+
+
+#if 0
+struct hb_tt_font_t
+{
+ const struct hhea *hhea;
+ hb_blob_t *hhea_blob;
+};
+
+
+static hb_tt_font_t *
+_hb_tt_font_create (hb_font_t *font)
+{
+ /* TODO Remove this object altogether */
+ hb_tt_font_t *tt = (hb_tt_font_t *) calloc (1, sizeof (hb_tt_font_t));
+
+ tt->hhea_blob = Sanitizer<hhea>::sanitize (hb_face_reference_table (font->face, HB_OT_TAG_hhea));
+ tt->hhea = Sanitizer<hhea>::lock_instance (tt->hhea_blob);
+
+ return tt;
+}
+
+static void
+_hb_tt_font_destroy (hb_tt_font_t *tt)
+{
+ hb_blob_destroy (tt->hhea_blob);
+
+ free (tt);
+}
+
+static inline const hhea&
+_get_hhea (hb_face_t *face)
+{
+// return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea);
+}
+
+
+/*
+ * hb_tt_font_funcs_t
+ */
+
+static hb_bool_t
+hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t unicode,
+ hb_codepoint_t variation_selector,
+ hb_codepoint_t *glyph,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent)
+ return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
+
+ *glyph = 0;
+ return false;
+}
+
+static hb_position_t
+hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent)
+ return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph));
+
+ return font->x_scale;
+}
+
+static hb_position_t
+hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent)
+ return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph));
+
+ return font->y_scale;
+}
+
+static hb_bool_t
+hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ hb_position_t *x,
+ hb_position_t *y,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent) {
+ hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent,
+ glyph,
+ x, y);
+ if (ret)
+ font->parent_scale_position (x, y);
+ return ret;
+ }
+
+ *x = *y = 0;
+ return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ hb_position_t *x,
+ hb_position_t *y,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent) {
+ hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent,
+ glyph,
+ x, y);
+ if (ret)
+ font->parent_scale_position (x, y);
+ return ret;
+ }
+
+ *x = *y = 0;
+ return false;
+}
+
+static hb_position_t
+hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t left_glyph,
+ hb_codepoint_t right_glyph,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent)
+ return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph));
+
+ return 0;
+}
+
+static hb_position_t
+hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t top_glyph,
+ hb_codepoint_t bottom_glyph,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent)
+ return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph));
+
+ return 0;
+}
+
+static hb_bool_t
+hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent) {
+ hb_bool_t ret = hb_font_get_glyph_extents (font->parent,
+ glyph,
+ extents);
+ if (ret) {
+ font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
+ font->parent_scale_distance (&extents->width, &extents->height);
+ }
+ return ret;
+ }
+
+ memset (extents, 0, sizeof (*extents));
+ return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
+ void *font_data HB_UNUSED,
+ hb_codepoint_t glyph,
+ unsigned int point_index,
+ hb_position_t *x,
+ hb_position_t *y,
+ void *user_data HB_UNUSED)
+{
+ if (font->parent) {
+ hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent,
+ glyph, point_index,
+ x, y);
+ if (ret)
+ font->parent_scale_position (x, y);
+ return ret;
+ }
+
+ *x = *y = 0;
+ return false;
+}
+
+
+static hb_font_funcs_t _hb_font_funcs_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ true, /* immutable */
+
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ }
+};
+#endif
+
#include "hb-unicode.h"
#include "hb-object-private.hh"
-HB_BEGIN_DECLS
/*
struct _hb_unicode_funcs_t {
hb_object_header_t header;
+ ASSERT_POD ();
hb_unicode_funcs_t *parent;
};
-#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 */
#include "hb-unicode-private.hh"
-HB_BEGIN_DECLS
/*
hb_codepoint_t *ab HB_UNUSED,
void *user_data HB_UNUSED)
{
- /* TODO handle Hangul jamo here? */
- return FALSE;
+ return false;
}
static hb_bool_t
hb_codepoint_t *b HB_UNUSED,
void *user_data HB_UNUSED)
{
- /* TODO handle Hangul jamo here? */
- return FALSE;
+ return false;
}
-hb_unicode_funcs_t _hb_unicode_funcs_nil = {
- HB_OBJECT_HEADER_STATIC,
-
- NULL, /* parent */
- TRUE, /* immutable */
- {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
- HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
- }
-};
-
hb_unicode_funcs_t *
hb_unicode_funcs_get_default (void)
{
- return &_hb_unicode_funcs_default;
+ return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default);
}
hb_unicode_funcs_t *
hb_unicode_funcs_t *ufuncs;
if (!(ufuncs = hb_object_create<hb_unicode_funcs_t> ()))
- return &_hb_unicode_funcs_nil;
+ return hb_unicode_funcs_get_empty ();
if (!parent)
- parent = &_hb_unicode_funcs_nil;
+ parent = hb_unicode_funcs_get_empty ();
hb_unicode_funcs_make_immutable (parent);
ufuncs->parent = hb_unicode_funcs_reference (parent);
return ufuncs;
}
+
+//extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
+const hb_unicode_funcs_t _hb_unicode_funcs_nil = {
+ HB_OBJECT_HEADER_STATIC,
+
+ NULL, /* parent */
+ true, /* immutable */
+ {
+#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
+ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+ }
+};
+
hb_unicode_funcs_t *
hb_unicode_funcs_get_empty (void)
{
- return &_hb_unicode_funcs_nil;
+ return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil);
}
hb_unicode_funcs_t *
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 *
if (hb_object_is_inert (ufuncs))
return;
- ufuncs->immutable = TRUE;
+ ufuncs->immutable = true;
}
hb_bool_t
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 ();
}
return ufuncs->func.decompose (ufuncs, ab, a, b, ufuncs->user_data.decompose);
}
-HB_END_DECLS
+
+
+unsigned int
+_hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode)
+{
+ int c = hb_unicode_combining_class (ufuncs, unicode);
+
+ if (unlikely (hb_in_range<int> (c, 27, 33)))
+ {
+ /* Modify the combining-class to suit Arabic better. See:
+ * http://unicode.org/faq/normalization.html#8
+ * http://unicode.org/faq/normalization.html#9
+ */
+ c = c == 33 ? 27 : c + 1;
+ }
+ else if (unlikely (hb_in_range<int> (c, 10, 25)))
+ {
+ /* The equivalent fix for Hebrew is more complex.
+ *
+ * We permute the "fixed-position" classes 10-25 into the order
+ * described in the SBL Hebrew manual:
+ *
+ * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
+ *
+ * (as recommended by:
+ * http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
+ *
+ * More details here:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
+ */
+ static const int permuted_hebrew_classes[25 - 10 + 1] = {
+ /* 10 sheva */ 22,
+ /* 11 hataf segol */ 15,
+ /* 12 hataf patah */ 16,
+ /* 13 hataf qamats */ 17,
+ /* 14 hiriq */ 23,
+ /* 15 tsere */ 18,
+ /* 16 segol */ 19,
+ /* 17 patah */ 20,
+ /* 18 qamats */ 21,
+ /* 19 holam */ 14,
+ /* 20 qubuts */ 24,
+ /* 21 dagesh */ 12,
+ /* 22 meteg */ 25,
+ /* 23 rafe */ 13,
+ /* 24 shin dot */ 10,
+ /* 25 sin dot */ 11,
+ };
+ c = permuted_hebrew_classes[c - 10];
+ }
+
+ return c;
+}
+
* Google Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_UNICODE_H
#define HB_UNICODE_H
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,
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#define _WIN32_WINNT 0x0500
+
+#include "hb-private.hh"
+
+#include <windows.h>
+#include <usp10.h>
+
+typedef ULONG WIN_ULONG;
+
+#include "hb-uniscribe.h"
+
+#include "hb-ot-name-table.hh"
+#include "hb-ot-tag.h"
+
+#include "hb-font-private.hh"
+#include "hb-buffer-private.hh"
+
+
+
+#ifndef HB_DEBUG_UNISCRIBE
+#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
+#endif
+
+
+/*
+DWORD GetFontData(
+ __in HDC hdc,
+ __in DWORD dwTable,
+ __in DWORD dwOffset,
+ __out LPVOID lpvBuffer,
+ __in DWORD cbData
+);
+*/
+
+static bool
+populate_log_font (LOGFONTW *lf,
+ HDC hdc,
+ hb_font_t *font)
+{
+ memset (lf, 0, sizeof (*lf));
+ int dpi = GetDeviceCaps (hdc, LOGPIXELSY);
+ lf->lfHeight = -font->y_scale;
+
+ hb_blob_t *blob = Sanitizer<name>::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e')));
+ const name *name_table = Sanitizer<name>::lock_instance (blob);
+ unsigned int len = name_table->get_name (3, 1, 0x409, 4,
+ lf->lfFaceName,
+ sizeof (lf->lfFaceName[0]) * LF_FACESIZE)
+ / sizeof (lf->lfFaceName[0]);
+ hb_blob_destroy (blob);
+
+ if (unlikely (!len)) {
+ DEBUG_MSG (UNISCRIBE, NULL, "Didn't find English name table entry");
+ return false;
+ }
+ if (unlikely (len >= LF_FACESIZE)) {
+ DEBUG_MSG (UNISCRIBE, NULL, "Font name too long");
+ return false;
+ }
+
+ for (unsigned int i = 0; i < len; i++)
+ lf->lfFaceName[i] = hb_be_uint16 (lf->lfFaceName[i]);
+ lf->lfFaceName[len] = 0;
+
+ return true;
+}
+
+
+static hb_user_data_key_t hb_uniscribe_data_key;
+
+
+static struct hb_uniscribe_face_data_t {
+ HANDLE fh;
+} _hb_uniscribe_face_data_nil = {0};
+
+static void
+_hb_uniscribe_face_data_destroy (hb_uniscribe_face_data_t *data)
+{
+ if (data->fh)
+ RemoveFontMemResourceEx (data->fh);
+ free (data);
+}
+
+static hb_uniscribe_face_data_t *
+_hb_uniscribe_face_get_data (hb_face_t *face)
+{
+ hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key);
+ if (likely (data)) return data;
+
+ data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t));
+ if (unlikely (!data))
+ return &_hb_uniscribe_face_data_nil;
+
+
+ hb_blob_t *blob = hb_face_reference_blob (face);
+ unsigned int blob_length;
+ const char *blob_data = hb_blob_get_data (blob, &blob_length);
+ if (unlikely (!blob_length))
+ DEBUG_MSG (UNISCRIBE, face, "Face has empty blob");
+
+ DWORD num_fonts_installed;
+ data->fh = AddFontMemResourceEx ((void *) blob_data, blob_length, 0, &num_fonts_installed);
+ hb_blob_destroy (blob);
+ if (unlikely (!data->fh))
+ DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed");
+
+
+ if (unlikely (!hb_face_set_user_data (face, &hb_uniscribe_data_key, data,
+ (hb_destroy_func_t) _hb_uniscribe_face_data_destroy,
+ false)))
+ {
+ _hb_uniscribe_face_data_destroy (data);
+ data = (hb_uniscribe_face_data_t *) hb_face_get_user_data (face, &hb_uniscribe_data_key);
+ if (data)
+ return data;
+ else
+ return &_hb_uniscribe_face_data_nil;
+ }
+
+ return data;
+}
+
+
+static struct hb_uniscribe_font_data_t {
+ HDC hdc;
+ LOGFONTW log_font;
+ HFONT hfont;
+ SCRIPT_CACHE script_cache;
+} _hb_uniscribe_font_data_nil = {NULL, NULL, NULL};
+
+static void
+_hb_uniscribe_font_data_destroy (hb_uniscribe_font_data_t *data)
+{
+ if (data->hdc)
+ ReleaseDC (NULL, data->hdc);
+ if (data->hfont)
+ DeleteObject (data->hfont);
+ if (data->script_cache)
+ ScriptFreeCache (&data->script_cache);
+ free (data);
+}
+
+static hb_uniscribe_font_data_t *
+_hb_uniscribe_font_get_data (hb_font_t *font)
+{
+ hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key);
+ if (likely (data)) return data;
+
+ data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t));
+ if (unlikely (!data))
+ return &_hb_uniscribe_font_data_nil;
+
+ data->hdc = GetDC (NULL);
+
+ if (unlikely (!populate_log_font (&data->log_font, data->hdc, font)))
+ DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed");
+ else {
+ data->hfont = CreateFontIndirectW (&data->log_font);
+ if (unlikely (!data->hfont))
+ DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed");
+ if (!SelectObject (data->hdc, data->hfont))
+ DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed");
+ }
+
+ if (unlikely (!hb_font_set_user_data (font, &hb_uniscribe_data_key, data,
+ (hb_destroy_func_t) _hb_uniscribe_font_data_destroy,
+ false)))
+ {
+ _hb_uniscribe_font_data_destroy (data);
+ data = (hb_uniscribe_font_data_t *) hb_font_get_user_data (font, &hb_uniscribe_data_key);
+ if (data)
+ return data;
+ else
+ return &_hb_uniscribe_font_data_nil;
+ }
+
+ return data;
+}
+
+LOGFONTW *
+hb_uniscribe_font_get_logfontw (hb_font_t *font)
+{
+ hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+ if (unlikely (!font_data))
+ return NULL;
+ return &font_data->log_font;
+}
+
+HFONT
+hb_uniscribe_font_get_hfont (hb_font_t *font)
+{
+ hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+ if (unlikely (!font_data))
+ return 0;
+ return font_data->hfont;
+}
+
+
+hb_bool_t
+_hb_uniscribe_shape (hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features)
+{
+ buffer->guess_properties ();
+
+#define FAIL(...) \
+ HB_STMT_START { \
+ DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \
+ return false; \
+ } HB_STMT_END;
+
+ hb_uniscribe_face_data_t *face_data = _hb_uniscribe_face_get_data (font->face);
+ if (unlikely (!face_data->fh))
+ FAIL ("Couldn't get face data");
+
+ hb_uniscribe_font_data_t *font_data = _hb_uniscribe_font_get_data (font);
+ if (unlikely (!font_data->hfont))
+ FAIL ("Couldn't get font font");
+
+ if (unlikely (!buffer->len))
+ return true;
+
+ HRESULT hr;
+
+retry:
+
+ unsigned int scratch_size;
+ char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+
+ /* Allocate char buffers; they all fit */
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+ Type *name = (Type *) scratch; \
+ scratch += len * sizeof (name[0]); \
+ scratch_size -= len * sizeof (name[0]);
+
+#define utf16_index() var1.u32
+
+ WCHAR *pchars = (WCHAR *) scratch;
+ unsigned int chars_len = 0;
+ for (unsigned int i = 0; i < buffer->len; i++) {
+ hb_codepoint_t c = buffer->info[i].codepoint;
+ buffer->info[i].utf16_index() = chars_len;
+ if (likely (c < 0x10000))
+ pchars[chars_len++] = c;
+ else if (unlikely (c >= 0x110000))
+ pchars[chars_len++] = 0xFFFD;
+ else {
+ pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10);
+ pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1));
+ }
+ }
+
+ ALLOCATE_ARRAY (WCHAR, wchars, chars_len);
+ ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
+ ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len);
+
+ /* On Windows, we don't care about alignment...*/
+ unsigned int glyphs_size = scratch_size / (sizeof (WORD) +
+ sizeof (SCRIPT_GLYPHPROP) +
+ sizeof (int) +
+ sizeof (GOFFSET) +
+ sizeof (uint32_t));
+
+ ALLOCATE_ARRAY (WORD, glyphs, glyphs_size);
+ ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size);
+ ALLOCATE_ARRAY (int, advances, glyphs_size);
+ ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size);
+ ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
+
+
+#define MAX_ITEMS 10
+
+ SCRIPT_ITEM items[MAX_ITEMS + 1];
+ SCRIPT_CONTROL bidi_control = {0};
+ SCRIPT_STATE bidi_state = {0};
+ WIN_ULONG script_tags[MAX_ITEMS];
+ int item_count;
+
+ /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */
+ //bidi_control.fMergeNeutralItems = true;
+ *(uint32_t*)&bidi_control |= 1<<24;
+
+ bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
+// bidi_state.fOverrideDirection = 1;
+
+ hr = ScriptItemizeOpenType (wchars,
+ chars_len,
+ MAX_ITEMS,
+ &bidi_control,
+ &bidi_state,
+ items,
+ script_tags,
+ &item_count);
+ if (unlikely (FAILED (hr)))
+ FAIL ("ScriptItemizeOpenType() failed: 0x%08xL", hr);
+
+#undef MAX_ITEMS
+
+ int *range_char_counts = NULL;
+ TEXTRANGE_PROPERTIES **range_properties = NULL;
+ int range_count = 0;
+ if (num_features) {
+ /* TODO setup ranges */
+ }
+
+ OPENTYPE_TAG language_tag = hb_ot_tag_from_language (buffer->props.language);
+
+ unsigned int glyphs_offset = 0;
+ unsigned int glyphs_len;
+ for (unsigned int i = 0; i < item_count; i++)
+ {
+ unsigned int chars_offset = items[i].iCharPos;
+ unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset;
+ OPENTYPE_TAG script_tag = script_tags[i]; /* XXX buffer->props.script */
+
+ hr = ScriptShapeOpenType (font_data->hdc,
+ &font_data->script_cache,
+ &items[i].a,
+ script_tag,
+ language_tag,
+ range_char_counts,
+ range_properties,
+ range_count,
+ wchars + chars_offset,
+ item_chars_len,
+ glyphs_size - glyphs_offset,
+ /* out */
+ log_clusters + chars_offset,
+ char_props + chars_offset,
+ glyphs + glyphs_offset,
+ glyph_props + glyphs_offset,
+ (int *) &glyphs_len);
+
+ for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++)
+ log_clusters[j] += glyphs_offset;
+
+ if (unlikely (items[i].a.fNoGlyphIndex))
+ FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
+ if (unlikely (hr == E_OUTOFMEMORY))
+ {
+ buffer->ensure (buffer->allocated * 2);
+ if (buffer->in_error)
+ FAIL ("Buffer resize failed");
+ goto retry;
+ }
+ if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT))
+ FAIL ("ScriptShapeOpenType() failed: Font doesn't support script");
+ if (unlikely (FAILED (hr)))
+ FAIL ("ScriptShapeOpenType() failed: 0x%08xL", hr);
+
+ hr = ScriptPlaceOpenType (font_data->hdc,
+ &font_data->script_cache,
+ &items[i].a,
+ script_tag,
+ language_tag,
+ range_char_counts,
+ range_properties,
+ range_count,
+ wchars + chars_offset,
+ log_clusters + chars_offset,
+ char_props + chars_offset,
+ item_chars_len,
+ glyphs + glyphs_offset,
+ glyph_props + glyphs_offset,
+ glyphs_len,
+ /* out */
+ advances + glyphs_offset,
+ offsets + glyphs_offset,
+ NULL);
+ if (unlikely (FAILED (hr)))
+ FAIL ("ScriptPlaceOpenType() failed: 0x%08xL", hr);
+
+ glyphs_offset += glyphs_len;
+ }
+ glyphs_len = glyphs_offset;
+
+ /* Ok, we've got everything we need, now compose output buffer,
+ * very, *very*, carefully! */
+
+ /* Calculate visual-clusters. That's what we ship. */
+ for (unsigned int i = 0; i < glyphs_len; i++)
+ vis_clusters[i] = -1;
+ for (unsigned int i = 0; i < buffer->len; i++) {
+ uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]];
+ *p = MIN (*p, buffer->info[i].cluster);
+ }
+ if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+ for (unsigned int i = 1; i < glyphs_len; i++)
+ if (!glyph_props[i].sva.fClusterStart)
+ vis_clusters[i] = vis_clusters[i - 1];
+ } else {
+ for (int i = glyphs_len - 2; i >= 0; i--)
+ if (!glyph_props[i].sva.fClusterStart)
+ vis_clusters[i] = vis_clusters[i + 1];
+ }
+
+#undef utf16_index
+
+ buffer->ensure (glyphs_len);
+ if (buffer->in_error)
+ FAIL ("Buffer in error");
+
+#undef FAIL
+
+ /* Set glyph infos */
+ buffer->len = 0;
+ for (unsigned int i = 0; i < glyphs_len; i++)
+ {
+ hb_glyph_info_t *info = &buffer->info[buffer->len++];
+
+ info->codepoint = glyphs[i];
+ info->cluster = vis_clusters[i];
+
+ /* The rest is crap. Let's store position info there for now. */
+ info->mask = advances[i];
+ info->var1.u32 = offsets[i].du;
+ info->var2.u32 = offsets[i].dv;
+ }
+
+ /* Set glyph positions */
+ buffer->clear_positions ();
+ for (unsigned int i = 0; i < glyphs_len; i++)
+ {
+ hb_glyph_info_t *info = &buffer->info[i];
+ hb_glyph_position_t *pos = &buffer->pos[i];
+
+ /* TODO vertical */
+ pos->x_advance = info->mask;
+ pos->x_offset = info->var1.u32;
+ pos->y_offset = info->var2.u32;
+ }
+
+ /* Wow, done! */
+ return true;
+}
+
+
/*
- * Copyright © 2010 Red Hat, Inc.
+ * Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
- * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
*/
-#ifndef HB_OT_SHAPE_H
-#define HB_OT_SHAPE_H
+#ifndef HB_UNISCRIBE_H
+#define HB_UNISCRIBE_H
-#include "hb-common.h"
-#include "hb-shape.h"
+#include "hb.h"
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
HB_BEGIN_DECLS
-void
-hb_ot_shape (hb_font_t *font,
- hb_buffer_t *buffer,
- const hb_feature_t *features,
- unsigned int num_features);
+LOGFONTW *
+hb_uniscribe_font_get_logfontw (hb_font_t *font);
+
+HFONT
+hb_uniscribe_font_get_hfont (hb_font_t *font);
HB_END_DECLS
-#endif /* HB_OT_SHAPE_H */
+#endif /* HB_UNISCRIBE_H */
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_VERSION_H
+#define HB_VERSION_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_VERSION_MAJOR 0
+#define HB_VERSION_MINOR 9
+#define HB_VERSION_MICRO 0
+
+#define HB_VERSION_STRING "0.9.0"
+
+#define HB_VERSION_CHECK(major,minor,micro) \
+ ((major)*10000+(minor)*100+(micro) >= \
+ HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
+
+
+void
+hb_version (unsigned int *major,
+ unsigned int *minor,
+ unsigned int *micro);
+
+const char *
+hb_version_string (void);
+
+hb_bool_t
+hb_version_check (unsigned int major,
+ unsigned int minor,
+ unsigned int micro);
+
+
+HB_END_DECLS
+
+#endif /* HB_VERSION_H */
* Google Author(s): Behdad Esfahbod
*/
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
#ifndef HB_VERSION_H
#define HB_VERSION_H
+++ /dev/null
-/*
- * Copyright © 2010 Behdad Esfahbod
- * Copyright © 2011 Google, Inc.
- *
- * This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <unistd.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <locale.h>
-
-#include <glib.h>
-
-#include <cairo-ft.h>
-#include <hb-ft.h>
-
-HB_BEGIN_DECLS
-
-
-/* Controlled by cmd-line options */
-static int margin_t = 10;
-static int margin_b = 10;
-static int margin_l = 10;
-static int margin_r = 10;
-static int line_space = 0;
-static int face_index = 0;
-static double font_size = 18;
-static const char *fore = "#000000";
-static const char *back = "#ffffff";
-static const char *text = NULL;
-static const char *font_file = NULL;
-static const char *out_file = "/dev/stdout";
-static const char *direction = NULL;
-static const char *script = NULL;
-static const char *language = NULL;
-static hb_feature_t *features = NULL;
-static unsigned int num_features;
-static hb_bool_t annotate = FALSE;
-static hb_bool_t debug = FALSE;
-
-/* Ugh, global vars. Ugly, but does the job */
-static int width = 0;
-static int height = 0;
-static cairo_surface_t *surface = NULL;
-static cairo_pattern_t *fore_pattern = NULL;
-static cairo_pattern_t *back_pattern = NULL;
-static cairo_font_face_t *cairo_face;
-
-
-G_GNUC_NORETURN static void
-usage (FILE *f, int status)
-{
- fprintf (f, "usage: hb-view [OPTS...] font-file text\n");
- exit (status);
-}
-
-G_GNUC_NORETURN static void
-version (void)
-{
- printf ("hb-view (harfbuzz) %s\n", HB_VERSION_STRING);
- exit (0);
-}
-
-static void parse_features (char *s);
-
-static void
-parse_opts (int argc, char **argv)
-{
- argv[0] = (char *) "hb-view";
- while (1)
- {
- int option_index = 0, c;
- static const struct option long_options[] = {
- {"annotate", 0, &annotate, TRUE},
- {"background", 1, 0, 'B'},
- {"debug", 0, &debug, TRUE},
- {"direction", 1, 0, 'd'},
- {"features", 1, 0, 'f'},
- {"font-size", 1, 0, 's'},
- {"face-index", 1, 0, 'i'},
- {"foreground", 1, 0, 'F'},
- {"help", 0, 0, 'h'},
- {"language", 1, 0, 'L'},
- {"line-space", 1, 0, 'l'},
- {"margin", 1, 0, 'm'},
- {"output", 1, 0, 'o'},
- {"script", 1, 0, 'S'},
- {"version", 0, 0, 'v'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long (argc, argv, "", long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c)
- {
- case 0:
- break;
- case 'h':
- usage (stdout, 0);
- break;
- case 'v':
- version ();
- break;
- case 'i':
- face_index = atoi (optarg);
- break;
- case 'l':
- line_space = atoi (optarg);
- break;
- case 'm':
- switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) {
- default: break;
- case 1: margin_r = margin_t;
- case 2: margin_b = margin_t;
- case 3: margin_l = margin_r;
- }
- break;
- case 's':
- font_size = strtod (optarg, NULL);
- break;
- case 'f':
- parse_features (optarg);
- break;
- case 'F':
- fore = optarg;
- break;
- case 'B':
- back = optarg;
- break;
- case 't':
- text = optarg;
- break;
- case 'd':
- direction = optarg;
- break;
- case 'S':
- script = optarg;
- break;
- case 'L':
- language = optarg;
- break;
- case 'o':
- out_file = optarg;
- break;
- case '?':
- usage (stdout, 1);
- break;
- default:
- break;
- }
- }
- if (optind + 2 != argc)
- usage (stderr, 1);
- font_file = argv[optind++];
- text = argv[optind++];
-}
-
-
-static void
-parse_space (char **pp)
-{
- char c;
-#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
- while (c = **pp, ISSPACE (c))
- (*pp)++;
-#undef ISSPACE
-}
-
-static hb_bool_t
-parse_char (char **pp, char c)
-{
- parse_space (pp);
-
- if (**pp != c)
- return FALSE;
-
- (*pp)++;
- return TRUE;
-}
-
-static hb_bool_t
-parse_uint (char **pp, unsigned int *pv)
-{
- char *p = *pp;
- unsigned int v;
-
- v = strtol (p, pp, 0);
-
- if (p == *pp)
- return FALSE;
-
- *pv = v;
- return TRUE;
-}
-
-
-static hb_bool_t
-parse_feature_value_prefix (char **pp, hb_feature_t *feature)
-{
- if (parse_char (pp, '-'))
- feature->value = 0;
- else {
- parse_char (pp, '+');
- feature->value = 1;
- }
-
- return TRUE;
-}
-
-static hb_bool_t
-parse_feature_tag (char **pp, hb_feature_t *feature)
-{
- char *p = *pp, c;
-
- parse_space (pp);
-
-#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
- while (c = **pp, ISALNUM(c))
- (*pp)++;
-#undef ISALNUM
-
- if (p == *pp)
- return FALSE;
-
- **pp = '\0';
- feature->tag = hb_tag_from_string (p);
- **pp = c;
-
- return TRUE;
-}
-
-static hb_bool_t
-parse_feature_indices (char **pp, hb_feature_t *feature)
-{
- hb_bool_t has_start;
-
- feature->start = 0;
- feature->end = (unsigned int) -1;
-
- if (!parse_char (pp, '['))
- return TRUE;
-
- has_start = parse_uint (pp, &feature->start);
-
- if (parse_char (pp, ':')) {
- parse_uint (pp, &feature->end);
- } else {
- if (has_start)
- feature->end = feature->start + 1;
- }
-
- return parse_char (pp, ']');
-}
-
-static hb_bool_t
-parse_feature_value_postfix (char **pp, hb_feature_t *feature)
-{
- return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
-}
-
-
-static hb_bool_t
-parse_one_feature (char **pp, hb_feature_t *feature)
-{
- return parse_feature_value_prefix (pp, feature) &&
- parse_feature_tag (pp, feature) &&
- parse_feature_indices (pp, feature) &&
- parse_feature_value_postfix (pp, feature) &&
- (parse_char (pp, ',') || **pp == '\0');
-}
-
-static void
-skip_one_feature (char **pp)
-{
- char *e;
- e = strchr (*pp, ',');
- if (e)
- *pp = e + 1;
- else
- *pp = *pp + strlen (*pp);
-}
-
-static void parse_features (char *s)
-{
- char *p;
-
- num_features = 0;
- features = NULL;
-
- if (!*s)
- return;
-
- /* count the features first, so we can allocate memory */
- p = s;
- do {
- num_features++;
- p = strchr (p, ',');
- if (p)
- p++;
- } while (p);
-
- features = (hb_feature_t *) calloc (num_features, sizeof (*features));
-
- /* now do the actual parsing */
- p = s;
- num_features = 0;
- while (*p) {
- if (parse_one_feature (&p, &features[num_features]))
- num_features++;
- else
- skip_one_feature (&p);
- }
-}
-
-
-static cairo_glyph_t *
-_hb_cr_text_glyphs (cairo_t *cr,
- const char *utf8, int len,
- unsigned int *pnum_glyphs)
-{
- cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
- FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
- hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
- hb_buffer_t *hb_buffer;
- cairo_glyph_t *cairo_glyphs;
- hb_glyph_info_t *hb_glyph;
- hb_glyph_position_t *hb_position;
- unsigned int num_glyphs, i;
- hb_position_t x, y;
-
- hb_buffer = hb_buffer_create (0);
-
- if (direction)
- hb_buffer_set_direction (hb_buffer, hb_direction_from_string (direction));
- if (script)
- hb_buffer_set_script (hb_buffer, hb_script_from_string (script));
- if (language)
- hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
-
- if (len < 0)
- len = strlen (utf8);
- hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len);
-
- hb_shape (hb_font, hb_buffer, features, num_features);
-
- num_glyphs = hb_buffer_get_length (hb_buffer);
- hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
- hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
- cairo_glyphs = cairo_glyph_allocate (num_glyphs);
- x = 0;
- y = 0;
- for (i = 0; i < num_glyphs; i++)
- {
- cairo_glyphs[i].index = hb_glyph->codepoint;
- cairo_glyphs[i].x = ( hb_position->x_offset + x) * (1./64);
- cairo_glyphs[i].y = (-hb_position->y_offset + y) * (1./64);
- x += hb_position->x_advance;
- y += -hb_position->y_advance;
-
- hb_glyph++;
- hb_position++;
- }
- hb_buffer_destroy (hb_buffer);
- hb_font_destroy (hb_font);
- cairo_ft_scaled_font_unlock_face (scaled_font);
-
- if (pnum_glyphs)
- *pnum_glyphs = num_glyphs;
- return cairo_glyphs;
-}
-
-static cairo_t *
-create_context (void)
-{
- cairo_t *cr;
- unsigned int fr, fg, fb, fa, br, bg, bb, ba;
-
- if (surface)
- cairo_surface_destroy (surface);
- if (back_pattern)
- cairo_pattern_destroy (back_pattern);
- if (fore_pattern)
- cairo_pattern_destroy (fore_pattern);
-
- br = bg = bb = ba = 255;
- sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
- fr = fg = fb = 0; fa = 255;
- sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
-
- if (!annotate && ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
- /* grayscale. use A8 surface */
- surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
- cr = cairo_create (surface);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
- cairo_paint (cr);
- back_pattern = cairo_pattern_reference (cairo_get_source (cr));
- cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
- fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
- } else {
- /* color. use (A)RGB surface */
- if (ba != 255)
- surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
- else
- surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
- cr = cairo_create (surface);
- cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
- cairo_paint (cr);
- back_pattern = cairo_pattern_reference (cairo_get_source (cr));
- cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
- fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
- }
-
- cairo_set_font_face (cr, cairo_face);
-
- return cr;
-}
-
-static void
-draw (void)
-{
- cairo_t *cr;
- cairo_font_extents_t font_extents;
-
- cairo_glyph_t *glyphs = NULL;
- unsigned int num_glyphs = 0;
-
- const char *end, *p = text;
- double x, y;
-
- cr= create_context ();
-
- cairo_set_font_size (cr, font_size);
- cairo_font_extents (cr, &font_extents);
-
- height = 0;
- width = 0;
-
- x = margin_l;
- y = margin_t;
-
- do {
- cairo_text_extents_t extents;
-
- end = strchr (p, '\n');
- if (!end)
- end = p + strlen (p);
-
- if (p != text)
- y += line_space;
-
- if (p != end) {
- glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
-
- cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
-
- y += ceil (font_extents.ascent);
- width = MAX (width, extents.x_advance);
- cairo_save (cr);
- cairo_translate (cr, x, y);
- if (annotate) {
- unsigned int i;
- cairo_save (cr);
-
- /* Draw actual glyph origins */
- cairo_set_source_rgba (cr, 1., 0., 0., .5);
- cairo_set_line_width (cr, 5);
- cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
- for (i = 0; i < num_glyphs; i++) {
- cairo_move_to (cr, glyphs[i].x, glyphs[i].y);
- cairo_rel_line_to (cr, 0, 0);
- }
- cairo_stroke (cr);
-
- cairo_restore (cr);
- }
- cairo_show_glyphs (cr, glyphs, num_glyphs);
- cairo_restore (cr);
- y += ceil (font_extents.height - ceil (font_extents.ascent));
-
- cairo_glyph_free (glyphs);
- }
-
- p = end + 1;
- } while (*end);
-
- height = y + margin_b;
- width += margin_l + margin_r;
-
- cairo_destroy (cr);
-}
-
-
-
-int
-main (int argc, char **argv)
-{
- static FT_Library ft_library;
- static FT_Face ft_face;
- cairo_status_t status;
-
- setlocale (LC_ALL, "");
-
- parse_opts (argc, argv);
-
- FT_Init_FreeType (&ft_library);
- if (FT_New_Face (ft_library, font_file, face_index, &ft_face)) {
- fprintf (stderr, "Failed to open font file `%s'\n", font_file);
- exit (1);
- }
- cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
-
- draw ();
- draw ();
-
- status = cairo_surface_write_to_png (surface, out_file);
- if (status != CAIRO_STATUS_SUCCESS) {
- fprintf (stderr, "Failed to write output file `%s': %s\n",
- out_file, cairo_status_to_string (status));
- exit (1);
- }
-
- if (debug) {
- free (features);
-
- cairo_pattern_destroy (fore_pattern);
- cairo_pattern_destroy (back_pattern);
- cairo_surface_destroy (surface);
- cairo_font_face_destroy (cairo_face);
- cairo_debug_reset_static_data ();
-
- FT_Done_Face (ft_face);
- FT_Done_FreeType (ft_library);
- }
-
- return 0;
-}
-
-
-HB_END_DECLS
--- /dev/null
+/*
+ * 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
#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"
HB_BEGIN_DECLS
HB_END_DECLS
+#undef HB_H_IN
#endif /* HB_H */
--- /dev/null
+/*
+ * 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);
+ }
+}
* 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
#include <stdlib.h>
#include <stdio.h>
-HB_BEGIN_DECLS
int
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
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");
}
-HB_END_DECLS
+++ /dev/null
-/*
- * Copyright © 2010 Google, Inc.
- *
- * This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- * Google Author(s): Behdad Esfahbod
- */
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "hb.h"
-
-#ifdef HAVE_GLIB
-#include <glib.h>
-#endif
-#include <stdlib.h>
-#include <stdio.h>
-
-HB_BEGIN_DECLS
-
-
-int
-main (int argc, char **argv)
-{
- hb_blob_t *blob = NULL;
- hb_face_t *face = NULL;
-
- if (argc != 2) {
- fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
- exit (1);
- }
-
- /* Create the blob */
- {
- const char *font_data;
- unsigned int len;
- hb_destroy_func_t destroy;
- void *user_data;
- hb_memory_mode_t mm;
-
-#ifdef HAVE_GLIB
- GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL);
- font_data = g_mapped_file_get_contents (mf);
- len = g_mapped_file_get_length (mf);
- destroy = (hb_destroy_func_t) g_mapped_file_unref;
- user_data = (void *) mf;
- mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
-#else
- FILE *f = fopen (argv[1], "rb");
- fseek (f, 0, SEEK_END);
- len = ftell (f);
- fseek (f, 0, SEEK_SET);
- font_data = (const char *) malloc (len);
- if (!font_data) len = 0;
- len = fread ((char *) font_data, 1, len, f);
- destroy = free;
- user_data = (void *) font_data;
- fclose (f);
- mm = HB_MEMORY_MODE_WRITABLE;
-#endif
-
- blob = hb_blob_create (font_data, len, mm, user_data, destroy);
- }
-
- printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob));
-
- /* Create the face */
- face = hb_face_create (blob, 0 /* first face */);
-
- /* So, what now? */
-
- hb_face_destroy (face);
- hb_blob_destroy (blob);
-
- return 0;
-}
-
-
-HB_END_DECLS
# 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
--- /dev/null
+# 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
#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)
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))
{
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 */
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++)
{
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++)
{
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++)
{
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));
#include <hb.h>
-#if HAVE_GLIB
+#ifdef HAVE_GLIB
#include <hb-glib.h>
#endif
-#if HAVE_ICU
+#ifdef HAVE_ICU
#include <hb-icu.h>
#endif
-#if HAVE_FREETYPE
+#ifdef HAVE_FREETYPE
#include <hb-ft.h>
#endif
+#ifdef HAVE_OT
+#include <hb-ot.h>
+#endif
+
+#ifdef HAVE_UNISCRIBE
+#include <hb-uniscribe.h>
+#endif
+
int
main (int argc, char **argv)
{
- return 0;
+ return !*hb_shape_list_shapers ();
}
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)));
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
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);
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);
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
/* Unit tests for hb-object-private.h */
-#ifdef HAVE_FREETYPE
-#include <hb-ft.h>
-#endif
-
-
static void *
create_blob (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 *
static void *
create_font_funcs_inert (void)
{
-#ifdef HAVE_FREETYPE
- return hb_ft_get_font_funcs ();
-#else
return NULL;
-#endif
}
static 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);
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) {
}
/* 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++)
* 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);
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);
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);
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);
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);
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);
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);
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);
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);
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
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);
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)
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();
}
{ 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 },
}
-/* TODO test compose() and decompose() */
static void
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);
--- /dev/null
+# 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
--- /dev/null
+# 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:
--- /dev/null
+#!/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:])
--- /dev/null
+#!/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))
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (FilterHelpers.filter_printer_function_no_newline (DiffFilters.filter_failures))
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (DiffSinks.print_ngrams)
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_files (DiffSinks.print_stat)
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_args (FilterHelpers.filter_printer_function (Manifest.read), mnemonic="DIR")
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.process_multiple_args (Manifest.update_recursive, mnemonic="DIR")
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.decode, "UNICODE_STRING")
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.encode, "UNICODE_STRING", '')
--- /dev/null
+#!/usr/bin/python
+
+from hb_test_tools import *
+
+UtilMains.filter_multiple_strings_or_stdin (Unicode.pretty_names, "UNICODE_CODEPOINTS", \
+ concat_separator = ' ')
--- /dev/null
+#!/usr/bin/python
+
+import sys, os, re, difflib, unicodedata, errno, cgi
+from itertools import *
+
+diff_symbols = "-+=*&^%$#@!~/"
+diff_colors = ['red', 'green', 'blue']
+
+class ColorFormatter:
+
+ class Null:
+ @staticmethod
+ def start_color (c): return ''
+ @staticmethod
+ def end_color (): return ''
+ @staticmethod
+ def escape (s): return s
+ @staticmethod
+ def newline (): return '\n'
+
+ class ANSI:
+ @staticmethod
+ def start_color (c):
+ return {
+ 'red': '\033[41;37;1m',
+ 'green': '\033[42;37;1m',
+ 'blue': '\033[44;37;1m',
+ }[c]
+ @staticmethod
+ def end_color ():
+ return '\033[m'
+ @staticmethod
+ def escape (s): return s
+ @staticmethod
+ def newline (): return '\n'
+
+ class HTML:
+ @staticmethod
+ def start_color (c):
+ return '<span style="background:%s">' % c
+ @staticmethod
+ def end_color ():
+ return '</span>'
+ @staticmethod
+ def escape (s): return cgi.escape (s)
+ @staticmethod
+ def newline (): return '<br/>\n'
+
+ @staticmethod
+ def Auto (argv = [], out = sys.stdout):
+ format = ColorFormatter.ANSI
+ if "--format" in argv:
+ argv.remove ("--format")
+ format = ColorFormatter.ANSI
+ if "--format=ansi" in argv:
+ argv.remove ("--format=ansi")
+ format = ColorFormatter.ANSI
+ if "--format=html" in argv:
+ argv.remove ("--format=html")
+ format = ColorFormatter.HTML
+ if "--no-format" in argv:
+ argv.remove ("--no-format")
+ format = ColorFormatter.Null
+ return format
+
+
+class DiffColorizer:
+
+ diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)')
+
+ def __init__ (self, formatter, colors=diff_colors, symbols=diff_symbols):
+ self.formatter = formatter
+ self.colors = colors
+ self.symbols = symbols
+
+ def colorize_lines (self, lines):
+ lines = (l if l else '' for l in lines)
+ ss = [self.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in lines]
+ oo = ["",""]
+ st = [False, False]
+ for l in difflib.Differ().compare (*ss):
+ if l[0] == '?':
+ continue
+ if l[0] == ' ':
+ for i in range(2):
+ if st[i]:
+ oo[i] += self.formatter.end_color ()
+ st[i] = False
+ oo = [o + self.formatter.escape (l[2:]) for o in oo]
+ continue
+ if l[0] in self.symbols:
+ i = self.symbols.index (l[0])
+ if not st[i]:
+ oo[i] += self.formatter.start_color (self.colors[i])
+ st[i] = True
+ oo[i] += self.formatter.escape (l[2:])
+ continue
+ for i in range(2):
+ if st[i]:
+ oo[i] += self.formatter.end_color ()
+ st[i] = False
+ oo = [o.replace ('\n', '') for o in oo]
+ return [s1+s2+self.formatter.newline () for (s1,s2) in zip (self.symbols, oo) if s2]
+
+ def colorize_diff (self, f):
+ lines = [None, None]
+ for l in f:
+ if l[0] not in self.symbols:
+ yield self.formatter.escape (l).replace ('\n', self.formatter.newline ())
+ continue
+ i = self.symbols.index (l[0])
+ if lines[i]:
+ # Flush
+ for line in self.colorize_lines (lines):
+ yield line
+ lines = [None, None]
+ lines[i] = l[1:]
+ if (all (lines)):
+ # Flush
+ for line in self.colorize_lines (lines):
+ yield line
+ lines = [None, None]
+ if (any (lines)):
+ # Flush
+ for line in self.colorize_lines (lines):
+ yield line
+
+
+class ZipDiffer:
+
+ @staticmethod
+ def diff_files (files, symbols=diff_symbols):
+ files = tuple (files) # in case it's a generator, copy it
+ try:
+ for lines in izip_longest (*files):
+ if all (lines[0] == line for line in lines[1:]):
+ sys.stdout.writelines ([" ", lines[0]])
+ continue
+
+ for i, l in enumerate (lines):
+ if l:
+ sys.stdout.writelines ([symbols[i], l])
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+ sys.exit (1)
+
+
+class DiffFilters:
+
+ @staticmethod
+ def filter_failures (f):
+ for key, lines in DiffHelpers.separate_test_cases (f):
+ lines = list (lines)
+ if not DiffHelpers.test_passed (lines):
+ for l in lines: yield l
+
+class Stat:
+
+ def __init__ (self):
+ self.count = 0
+ self.freq = 0
+
+ def add (self, test):
+ self.count += 1
+ self.freq += test.freq
+
+class Stats:
+
+ def __init__ (self):
+ self.passed = Stat ()
+ self.failed = Stat ()
+ self.total = Stat ()
+
+ def add (self, test):
+ self.total.add (test)
+ if test.passed:
+ self.passed.add (test)
+ else:
+ self.failed.add (test)
+
+ def mean (self):
+ return float (self.passed.count) / self.total.count
+
+ def variance (self):
+ return (float (self.passed.count) / self.total.count) * \
+ (float (self.failed.count) / self.total.count)
+
+ def stddev (self):
+ return self.variance () ** .5
+
+ def zscore (self, population):
+ """Calculate the standard score.
+ Population is the Stats for population.
+ Self is Stats for sample.
+ Returns larger absolute value if sample is highly unlikely to be random.
+ Anything outside of -3..+3 is very unlikely to be random.
+ See: http://en.wikipedia.org/wiki/Standard_score"""
+
+ return (self.mean () - population.mean ()) / population.stddev ()
+
+
+
+
+class DiffSinks:
+
+ @staticmethod
+ def print_stat (f):
+ passed = 0
+ failed = 0
+ # XXX port to Stats, but that would really slow us down here
+ for key, lines in DiffHelpers.separate_test_cases (f):
+ if DiffHelpers.test_passed (lines):
+ passed += 1
+ else:
+ failed += 1
+ total = passed + failed
+ print "%d out of %d tests passed. %d failed (%g%%)" % (passed, total, failed, 100. * failed / total)
+
+ @staticmethod
+ def print_ngrams (f, ns=(1,2,3)):
+ gens = tuple (Ngram.generator (n) for n in ns)
+ allstats = Stats ()
+ allgrams = {}
+ for key, lines in DiffHelpers.separate_test_cases (f):
+ test = Test (lines)
+ allstats.add (test)
+
+ for gen in gens:
+ for ngram in gen (test.unicodes):
+ if ngram not in allgrams:
+ allgrams[ngram] = Stats ()
+ allgrams[ngram].add (test)
+
+ importantgrams = {}
+ for ngram, stats in allgrams.iteritems ():
+ if stats.failed.count >= 30: # for statistical reasons
+ importantgrams[ngram] = stats
+ allgrams = importantgrams
+ del importantgrams
+
+ for ngram, stats in allgrams.iteritems ():
+ print "zscore: %9f failed: %6d passed: %6d ngram: <%s>" % (stats.zscore (allstats), stats.failed.count, stats.passed.count, ','.join ("U+%04X" % u for u in ngram))
+
+
+
+class Test:
+
+ def __init__ (self, lines):
+ self.freq = 1
+ self.passed = True
+ self.identifier = None
+ self.text = None
+ self.unicodes = None
+ self.glyphs = None
+ for l in lines:
+ symbol = l[0]
+ if symbol != ' ':
+ self.passed = False
+ i = 1
+ if ':' in l:
+ i = l.index (':')
+ if not self.identifier:
+ self.identifier = l[1:i]
+ i = i + 2 # Skip colon and space
+ j = -1
+ if l[j] == '\n':
+ j -= 1
+ brackets = l[i] + l[j]
+ l = l[i+1:-2]
+ if brackets == '()':
+ self.text = l
+ elif brackets == '<>':
+ self.unicodes = Unicode.parse (l)
+ elif brackets == '[]':
+ # XXX we don't handle failed tests here
+ self.glyphs = l
+
+
+class DiffHelpers:
+
+ @staticmethod
+ def separate_test_cases (f):
+ '''Reads lines from f, and if the lines have identifiers, ie.
+ have a colon character, groups them by identifier,
+ yielding lists of all lines with the same identifier.'''
+
+ def identifier (l):
+ if ':' in l[1:]:
+ return l[1:l.index (':')]
+ return l
+ return groupby (f, key=identifier)
+
+ @staticmethod
+ def test_passed (lines):
+ return all (l[0] == ' ' for l in lines)
+
+
+class FilterHelpers:
+
+ @staticmethod
+ def filter_printer_function (filter_callback):
+ def printer (f):
+ for line in filter_callback (f):
+ print line
+ return printer
+
+ @staticmethod
+ def filter_printer_function_no_newline (filter_callback):
+ def printer (f):
+ for line in filter_callback (f):
+ sys.stdout.writelines ([line])
+ return printer
+
+
+class Ngram:
+
+ @staticmethod
+ def generator (n):
+
+ def gen (f):
+ l = []
+ for x in f:
+ l.append (x)
+ if len (l) == n:
+ yield tuple (l)
+ l[:1] = []
+
+ gen.n = n
+ return gen
+
+
+class UtilMains:
+
+ @staticmethod
+ def process_multiple_files (callback, mnemonic = "FILE"):
+
+ if "--help" in sys.argv:
+ print "Usage: %s %s..." % (sys.argv[0], mnemonic)
+ sys.exit (1)
+
+ try:
+ files = sys.argv[1:] if len (sys.argv) > 1 else ['-']
+ for s in files:
+ callback (FileHelpers.open_file_or_stdin (s))
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+ sys.exit (1)
+
+ @staticmethod
+ def process_multiple_args (callback, mnemonic):
+
+ if len (sys.argv) == 1 or "--help" in sys.argv:
+ print "Usage: %s %s..." % (sys.argv[0], mnemonic)
+ sys.exit (1)
+
+ try:
+ for s in sys.argv[1:]:
+ callback (s)
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+ sys.exit (1)
+
+ @staticmethod
+ def filter_multiple_strings_or_stdin (callback, mnemonic, \
+ separator = " ", \
+ concat_separator = False):
+
+ if "--help" in sys.argv:
+ print "Usage:\n %s %s...\nor:\n %s\n\nWhen called with no arguments, input is read from standard input." \
+ % (sys.argv[0], mnemonic, sys.argv[0])
+ sys.exit (1)
+
+ try:
+ if len (sys.argv) == 1:
+ while (1):
+ line = sys.stdin.readline ()
+ if not len (line):
+ break
+ if line[-1] == '\n':
+ line = line[:-1]
+ print callback (line)
+ else:
+ args = sys.argv[1:]
+ if concat_separator != False:
+ args = [concat_separator.join (args)]
+ print separator.join (callback (x) for x in (args))
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
+ sys.exit (1)
+
+
+class Unicode:
+
+ @staticmethod
+ def decode (s):
+ return '<' + u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8') + '>'
+
+ @staticmethod
+ def parse (s):
+ s = re.sub (r"[<+>,\\uU\n ]", " ", s)
+ s = re.sub (r"0[xX]", " ", s)
+ return [int (x, 16) for x in s.split (' ') if len (x)]
+
+ @staticmethod
+ def encode (s):
+ return u''.join (unichr (x) for x in Unicode.parse (s)).encode ('utf-8')
+
+ shorthands = {
+ "ZERO WIDTH NON-JOINER": "ZWNJ",
+ "ZERO WIDTH JOINER": "ZWJ",
+ "NARROW NO-BREAK SPACE": "NNBSP",
+ "COMBINING GRAPHEME JOINER": "CGJ",
+ "LEFT-TO-RIGHT MARK": "LRM",
+ "RIGHT-TO-LEFT MARK": "RLM",
+ "LEFT-TO-RIGHT EMBEDDING": "LRE",
+ "RIGHT-TO-LEFT EMBEDDING": "RLE",
+ "POP DIRECTIONAL FORMATTING": "PDF",
+ "LEFT-TO-RIGHT OVERRIDE": "LRO",
+ "RIGHT-TO-LEFT OVERRIDE": "RLO",
+ }
+
+ @staticmethod
+ def pretty_name (u):
+ try:
+ s = unicodedata.name (u)
+ except ValueError:
+ return "XXX"
+ s = re.sub (".* LETTER ", "", s)
+ s = re.sub (".* VOWEL SIGN (.*)", r"\1-MATRA", s)
+ s = re.sub (".* SIGN ", "", s)
+ s = re.sub (".* COMBINING ", "", s)
+ if re.match (".* VIRAMA", s):
+ s = "HALANT"
+ if s in Unicode.shorthands:
+ s = Unicode.shorthands[s]
+ return s
+
+ @staticmethod
+ def pretty_names (s):
+ s = re.sub (r"[<+>\\uU]", " ", s)
+ s = re.sub (r"0[xX]", " ", s)
+ s = [unichr (int (x, 16)) for x in re.split ('[, \n]', s) if len (x)]
+ return u' + '.join (Unicode.pretty_name (x) for x in s).encode ('utf-8')
+
+
+class FileHelpers:
+
+ @staticmethod
+ def open_file_or_stdin (f):
+ if f == '-':
+ return sys.stdin
+ return file (f)
+
+
+class Manifest:
+
+ @staticmethod
+ def read (s, strict = True):
+
+ if not os.path.exists (s):
+ if strict:
+ print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], s)
+ sys.exit (1)
+ return
+
+ s = os.path.normpath (s)
+
+ if os.path.isdir (s):
+
+ try:
+ m = file (os.path.join (s, "MANIFEST"))
+ items = [x.strip () for x in m.readlines ()]
+ for f in items:
+ for p in Manifest.read (os.path.join (s, f)):
+ yield p
+ except IOError:
+ if strict:
+ print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], os.path.join (s, "MANIFEST"))
+ sys.exit (1)
+ return
+ else:
+ yield s
+
+ @staticmethod
+ def update_recursive (s):
+
+ for dirpath, dirnames, filenames in os.walk (s, followlinks=True):
+
+ for f in ["MANIFEST", "README", "LICENSE", "COPYING", "AUTHORS", "SOURCES", "ChangeLog"]:
+ if f in dirnames:
+ dirnames.remove (f)
+ if f in filenames:
+ filenames.remove (f)
+ dirnames.sort ()
+ filenames.sort ()
+ ms = os.path.join (dirpath, "MANIFEST")
+ print " GEN %s" % ms
+ m = open (ms, "w")
+ for f in filenames:
+ print >> m, f
+ for f in dirnames:
+ print >> m, f
+ for f in dirnames:
+ Manifest.update_recursive (os.path.join (dirpath, f))
+
+if __name__ == '__main__':
+ pass
--- /dev/null
+# 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
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ansi-print.hh"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for isatty() */
+#endif
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define CELL_W 8
+#define CELL_H (2 * CELL_W)
+
+struct color_diff_t
+{
+ int dot (const color_diff_t &o)
+ { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; }
+
+ int v[4];
+};
+
+struct color_t
+{
+ static color_t from_ansi (unsigned int x)
+ {
+ color_t c = {(0xFF<<24) | ((0xFF*(x&1))<<16) | ((0xFF*((x >> 1)&1))<<8) | (0xFF*((x >> 2)&1))};
+ return c;
+ }
+ unsigned int to_ansi (void)
+ {
+ return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4);
+ }
+
+ color_diff_t diff (const color_t &o)
+ {
+ color_diff_t d;
+ for (unsigned int i = 0; i < 4; i++)
+ d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF);
+ return d;
+ }
+
+ uint32_t v;
+};
+
+struct image_t
+{
+ public:
+
+ image_t (unsigned int width_,
+ unsigned int height_,
+ const uint32_t *data_,
+ unsigned int stride_) :
+ width (width_),
+ height (height_),
+ own_data (false),
+ data ((color_t *) data_),
+ stride (stride_) {}
+ image_t (unsigned int width_,
+ unsigned int height_) :
+ width (width_),
+ height (height_),
+ own_data (true),
+ data ((color_t *) malloc (sizeof (data[0]) * width * height)),
+ stride (width) {}
+ ~image_t (void)
+ { if (own_data) free (data); }
+
+ color_t &operator () (unsigned int x, unsigned int y)
+ { return data[x + y * stride]; }
+
+ color_t operator () (unsigned int x, unsigned int y) const
+ { return data[x + y * stride]; }
+
+ void
+ copy_sub_image (const image_t &s,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+ {
+ assert (x < width);
+ assert (y < height);
+ for (unsigned int row = 0; row < h; row++) {
+ color_t *p = data + x + MIN (y + row, height - 1) * stride;
+ color_t *q = s.data + row * s.stride;
+ if (x + w <= width)
+ for (unsigned int col = 0; col < w; col++)
+ *q++ = *p++;
+ else {
+ unsigned int limit = width - x;
+ for (unsigned int col = 0; col < limit; col++)
+ *q++ = *p++;
+ p--;
+ for (unsigned int col = limit; col < w; col++)
+ *q++ = *p;
+ }
+ }
+ }
+
+ const unsigned int width;
+ const unsigned int height;
+
+ private:
+ bool own_data;
+ color_t * const data;
+ const unsigned int stride;
+};
+
+struct biimage_t
+{
+ public:
+
+ biimage_t (unsigned int width, unsigned int height) :
+ width (width),
+ height (height),
+ data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {}
+ ~biimage_t (void)
+ { free (data); }
+
+ void set (const image_t &image)
+ {
+ assert (image.width == width);
+ assert (image.height == height);
+ int freq[8] = {0};
+ for (unsigned int y = 0; y < height; y++)
+ for (unsigned int x = 0; x < width; x++) {
+ color_t c = image (x, y);
+ freq[c.to_ansi ()]++;
+ }
+ bg = 0;
+ for (unsigned int i = 1; i < 8; i++)
+ if (freq[bg] < freq[i])
+ bg = i;
+ fg = 0;
+ for (unsigned int i = 1; i < 8; i++)
+ if (i != bg && freq[fg] < freq[i])
+ fg = i;
+ if (fg == bg || freq[fg] == 0) {
+ fg = bg;
+ unicolor = true;
+ }
+ else
+ unicolor = false;
+
+ /* Set the data... */
+
+ if (unicolor) {
+ memset (data, 0, sizeof (data[0]) * width * height);
+ return;
+ }
+
+ color_t bgc = color_t::from_ansi (bg);
+ color_t fgc = color_t::from_ansi (fg);
+ color_diff_t diff = fgc.diff (bgc);
+ int dd = diff.dot (diff);
+ for (unsigned int y = 0; y < height; y++)
+ for (unsigned int x = 0; x < width; x++) {
+ int d = diff.dot (image (x, y).diff (bgc));
+ (*this)(x, y) = d < 0 ? 0 : d > dd ? 255 : lround (d * 255. / dd);
+ }
+ }
+
+ uint8_t &operator () (unsigned int x, unsigned int y)
+ { return data[x + y * width]; }
+
+ uint8_t operator () (unsigned int x, unsigned int y) const
+ { return data[x + y * width]; }
+
+ const unsigned int width;
+ const unsigned int height;
+ unsigned int bg;
+ unsigned int fg;
+ bool unicolor;
+
+ private:
+ uint8_t * const data;
+};
+
+const char *
+block_best (const biimage_t &bi, unsigned int *score, bool *inverse)
+{
+ assert (bi.width <= CELL_W);
+ assert (bi.height <= CELL_H);
+
+ unsigned int row_sum[CELL_H] = {0};
+ unsigned int col_sum[CELL_W] = {0};
+ unsigned int row_sum_i[CELL_H] = {0};
+ unsigned int col_sum_i[CELL_W] = {0};
+ unsigned int quad[2][2] = {{0}};
+ unsigned int quad_i[2][2] = {{0}};
+ unsigned int total = 0;
+ unsigned int total_i = 0;
+ for (unsigned int y = 0; y < bi.height; y++)
+ for (unsigned int x = 0; x < bi.width; x++) {
+ unsigned int c = bi (x, y);
+ unsigned int c_i = 255 - c;
+ row_sum[y] += c;
+ row_sum_i[y] += c_i;
+ col_sum[x] += c;
+ col_sum_i[x] += c_i;
+ quad[2 * y / bi.height][2 * x / bi.width] += c;
+ quad_i[2 * y / bi.height][2 * x / bi.width] += c_i;
+ total += c;
+ total_i += c_i;
+ }
+
+ /* Make the sums cummulative */
+ for (unsigned int i = 1; i < bi.height; i++) {
+ row_sum[i] += row_sum[i - 1];
+ row_sum_i[i] += row_sum_i[i - 1];
+ }
+ for (unsigned int i = 1; i < bi.width; i++) {
+ col_sum[i] += col_sum[i - 1];
+ col_sum_i[i] += col_sum_i[i - 1];
+ }
+
+ const char *best_c = " ";
+
+ /* Maybe empty is better! */
+ if (total < *score) {
+ *score = total;
+ *inverse = false;
+ best_c = " ";
+ }
+ /* Maybe full is better! */
+ if (total_i < *score) {
+ *score = total_i;
+ *inverse = true;
+ best_c = " ";
+ }
+
+ /* Find best lower line */
+ if (1) {
+ unsigned int best_s = (unsigned int) -1;
+ bool best_inv = false;
+ int best_i = 0;
+ for (unsigned int i = 0; i < bi.height - 1; i++)
+ {
+ unsigned int s;
+ s = row_sum[i] + total_i - row_sum_i[i];
+ if (s < best_s) {
+ best_s = s;
+ best_i = i;
+ best_inv = false;
+ }
+ s = row_sum_i[i] + total - row_sum[i];
+ if (s < best_s) {
+ best_s = s;
+ best_i = i;
+ best_inv = true;
+ }
+ }
+ if (best_s < *score) {
+ static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"};
+ unsigned int which = lround (((best_i + 1) * 8) / bi.height);
+ if (1 <= which && which <= 7) {
+ *score = best_s;
+ *inverse = best_inv;
+ best_c = lower[7 - which];
+ }
+ }
+ }
+
+ /* Find best left line */
+ if (1) {
+ unsigned int best_s = (unsigned int) -1;
+ bool best_inv = false;
+ int best_i = 0;
+ for (unsigned int i = 0; i < bi.width - 1; i++)
+ {
+ unsigned int s;
+ s = col_sum[i] + total_i - col_sum_i[i];
+ if (s < best_s) {
+ best_s = s;
+ best_i = i;
+ best_inv = true;
+ }
+ s = col_sum_i[i] + total - col_sum[i];
+ if (s < best_s) {
+ best_s = s;
+ best_i = i;
+ best_inv = false;
+ }
+ }
+ if (best_s < *score) {
+ static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"};
+ unsigned int which = lround (((best_i + 1) * 8) / bi.width);
+ if (1 <= which && which <= 7) {
+ *score = best_s;
+ *inverse = best_inv;
+ best_c = left[which - 1];
+ }
+ }
+ }
+
+ /* Find best quadrant */
+ if (1) {
+ unsigned int q = 0;
+ unsigned int qs = 0;
+ for (unsigned int i = 0; i < 2; i++)
+ for (unsigned int j = 0; j < 2; j++)
+ if (quad[i][j] > quad_i[i][j]) {
+ q += 1 << (2 * i + j);
+ qs += quad_i[i][j];
+ } else
+ qs += quad[i][j];
+ if (qs < *score) {
+ const char *c = NULL;
+ bool inv = false;
+ switch (q) {
+ case 1: c = "▟"; inv = true; break;
+ case 2: c = "▙"; inv = true; break;
+ case 4: c = "▖"; inv = false; break;
+ case 8: c = "▗"; inv = false; break;
+ case 9: c = "▚"; inv = false; break;
+ case 6: c = "▞"; inv = false; break;
+ case 7: c = "▜"; inv = true; break;
+ case 11: c = "▜"; inv = true; break;
+ case 13: c = "▙"; inv = true; break;
+ case 14: c = "▟"; inv = true; break;
+ }
+ if (c) {
+ *score = qs;
+ *inverse = inv;
+ best_c = c;
+ }
+ }
+ }
+
+ return best_c;
+}
+
+void
+ansi_print_image_rgb24 (const uint32_t *data,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride)
+{
+ image_t image (width, height, data, stride);
+
+ unsigned int rows = (height + CELL_H - 1) / CELL_H;
+ unsigned int cols = (width + CELL_W - 1) / CELL_W;
+ image_t cell (CELL_W, CELL_H);
+ biimage_t bi (CELL_W, CELL_H);
+ unsigned int last_bg = -1, last_fg = -1;
+ for (unsigned int row = 0; row < rows; row++) {
+ for (unsigned int col = 0; col < cols; col++) {
+ image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H);
+ bi.set (cell);
+ if (bi.unicolor) {
+ if (last_bg != bi.bg) {
+ printf ("\e[%dm", 40 + bi.bg);
+ last_bg = bi.bg;
+ }
+ printf (" ");
+ } else {
+ /* Figure out the closest character to the biimage */
+ unsigned int score = (unsigned int) -1;
+ bool inverse = false;
+ const char *c = block_best (bi, &score, &inverse);
+ if (inverse) {
+ if (last_bg != bi.fg || last_fg != bi.bg) {
+ printf ("\e[%d;%dm", 30 + bi.bg, 40 + bi.fg);
+ last_bg = bi.fg;
+ last_fg = bi.bg;
+ }
+ } else {
+ if (last_bg != bi.bg || last_fg != bi.fg) {
+ printf ("\e[%d;%dm", 40 + bi.bg, 30 + bi.fg);
+ last_bg = bi.bg;
+ last_fg = bi.fg;
+ }
+ }
+ printf ("%s", c);
+ }
+ }
+ printf ("\e[0m\n"); /* Reset */
+ last_bg = last_fg = -1;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef ANSI_PRINT_HH
+#define ANSI_PRINT_HH
+
+#include <hb.h> /* for int types */
+
+void
+ansi_print_image_rgb24 (const uint32_t *data,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "main-font-text.hh"
+
+#ifdef HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
+
+struct shape_closure_consumer_t : option_group_t
+{
+ shape_closure_consumer_t (option_parser_t *parser) :
+ shaper (parser),
+ show_glyph_names (true)
+ {
+ add_options (parser);
+ }
+
+ void add_options (struct option_parser_t *parser)
+ {
+ GOptionEntry entries[] =
+ {
+ {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", NULL},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "format",
+ "Format options:",
+ "Options controlling output formatting",
+ this);
+ }
+
+ void init (const font_options_t *font_opts)
+ {
+ glyphs = hb_set_create ();
+ font = hb_font_reference (font_opts->get_font ());
+ failed = false;
+ }
+ void consume_line (hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len)
+ {
+ hb_set_clear (glyphs);
+ shaper.shape_closure (text, text_len, font, buffer, glyphs);
+
+ if (hb_set_empty (glyphs))
+ return;
+
+ /* Print it out! */
+ bool first = true;
+ for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);)
+ if (hb_set_has (glyphs, i)) {
+ if (first)
+ first = false;
+ else
+ printf (" ");
+ char glyph_name[32];
+ if (show_glyph_names) {
+ hb_font_get_glyph_name (font, i, glyph_name, sizeof (glyph_name));
+ printf ("%s", glyph_name);
+ } else
+ printf ("%u", i);
+ }
+ }
+ void finish (const font_options_t *font_opts)
+ {
+ printf ("\n");
+ hb_font_destroy (font);
+ font = NULL;
+ hb_set_destroy (glyphs);
+ glyphs = NULL;
+ }
+
+ bool failed;
+
+ protected:
+ shape_options_t shaper;
+ hb_bool_t show_glyph_names;
+
+ hb_set_t *glyphs;
+ hb_font_t *font;
+};
+
+int
+main (int argc, char **argv)
+{
+ main_font_text_t<shape_closure_consumer_t> driver;
+ return driver.main (argc, argv);
+}
--- /dev/null
+/*
+ * Copyright © 2010 Behdad Esfahbod
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "main-font-text.hh"
+#include "shape-consumer.hh"
+
+struct output_buffer_t
+{
+ output_buffer_t (option_parser_t *parser)
+ : options (parser),
+ format (parser) {}
+
+ void init (const font_options_t *font_opts)
+ {
+ options.get_file_handle ();
+ gs = g_string_new (NULL);
+ line_no = 0;
+ font = hb_font_reference (font_opts->get_font ());
+ }
+ void new_line (void)
+ {
+ line_no++;
+ }
+ void consume_text (hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len,
+ hb_bool_t utf8_clusters)
+ {
+ g_string_set_size (gs, 0);
+ format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+ fprintf (options.fp, "%s", gs->str);
+ }
+ void shape_failed (hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len,
+ hb_bool_t utf8_clusters)
+ {
+ g_string_set_size (gs, 0);
+ format.serialize_message (line_no, "msg: all shapers failed", gs);
+ fprintf (options.fp, "%s", gs->str);
+ }
+ void consume_glyphs (hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len,
+ hb_bool_t utf8_clusters)
+ {
+ g_string_set_size (gs, 0);
+ format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+ fprintf (options.fp, "%s", gs->str);
+ }
+ void finish (const font_options_t *font_opts)
+ {
+ hb_font_destroy (font);
+ g_string_free (gs, true);
+ gs = NULL;
+ font = NULL;
+ }
+
+ protected:
+ output_options_t options;
+ format_options_t format;
+
+ GString *gs;
+ unsigned int line_no;
+ hb_font_t *font;
+};
+
+int
+main (int argc, char **argv)
+{
+ main_font_text_t<shape_consumer_t<output_buffer_t> > driver;
+ return driver.main (argc, argv);
+}
--- /dev/null
+/*
+ * Copyright © 2010 Behdad Esfahbod
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "main-font-text.hh"
+#include "shape-consumer.hh"
+#include "view-cairo.hh"
+
+int
+main (int argc, char **argv)
+{
+ main_font_text_t<shape_consumer_t<view_cairo_t> > driver;
+ return driver.main (argc, argv);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include <cairo.h>
+
+#ifndef HELPER_CAIRO_ANSI_HH
+#define HELPER_CAIRO_ANSI_HH
+
+
+cairo_status_t
+helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface,
+ cairo_write_func_t write_func,
+ void *closure);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "helper-cairo.hh"
+
+#include <cairo-ft.h>
+#include <hb-ft.h>
+
+#include "helper-cairo-ansi.hh"
+#ifdef CAIRO_HAS_SVG_SURFACE
+# include <cairo-svg.h>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+# include <cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+# include <cairo-ps.h>
+# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
+# define HAS_EPS 1
+
+static cairo_surface_t *
+_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
+ void *closure,
+ double width,
+ double height)
+{
+ cairo_surface_t *surface;
+
+ surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
+ cairo_ps_surface_set_eps (surface, true);
+
+ return surface;
+}
+
+# else
+# undef HAS_EPS
+# endif
+#endif
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+ double font_size)
+{
+ hb_font_t *font = hb_font_reference (font_opts->get_font ());
+
+ cairo_font_face_t *cairo_face;
+ FT_Face ft_face = hb_ft_font_get_face (font);
+ if (!ft_face)
+ /* This allows us to get some boxes at least... */
+ cairo_face = cairo_toy_font_face_create ("@cairo:sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ else
+ cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+ cairo_matrix_t ctm, font_matrix;
+ cairo_font_options_t *font_options;
+
+ cairo_matrix_init_identity (&ctm);
+ cairo_matrix_init_scale (&font_matrix,
+ font_size, font_size);
+ font_options = cairo_font_options_create ();
+ cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+
+ cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
+ &font_matrix,
+ &ctm,
+ font_options);
+
+ cairo_font_options_destroy (font_options);
+ cairo_font_face_destroy (cairo_face);
+
+ static cairo_user_data_key_t key;
+ if (cairo_scaled_font_set_user_data (scaled_font,
+ &key,
+ (void *) font,
+ (cairo_destroy_func_t) hb_font_destroy))
+ hb_font_destroy (font);
+
+ return scaled_font;
+}
+
+
+struct finalize_closure_t {
+ void (*callback)(finalize_closure_t *);
+ cairo_surface_t *surface;
+ cairo_write_func_t write_func;
+ void *closure;
+};
+static cairo_user_data_key_t finalize_closure_key;
+
+
+static void
+finalize_ansi (finalize_closure_t *closure)
+{
+ cairo_status_t status;
+ status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
+ closure->write_func,
+ closure->closure);
+ if (status != CAIRO_STATUS_SUCCESS)
+ fail (false, "Failed to write output: %s",
+ cairo_status_to_string (status));
+}
+
+static cairo_surface_t *
+_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
+ void *closure,
+ double width,
+ double height,
+ cairo_content_t content)
+{
+ cairo_surface_t *surface;
+ int w = ceil (width);
+ int h = ceil (height);
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
+ break;
+ default:
+ case CAIRO_CONTENT_COLOR:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+ break;
+ }
+ cairo_status_t status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ fail (false, "Failed to create cairo surface: %s",
+ cairo_status_to_string (status));
+
+ finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
+ ansi_closure->callback = finalize_ansi;
+ ansi_closure->surface = surface;
+ ansi_closure->write_func = write_func;
+ ansi_closure->closure = closure;
+
+ if (cairo_surface_set_user_data (surface,
+ &finalize_closure_key,
+ (void *) ansi_closure,
+ (cairo_destroy_func_t) g_free))
+ g_free ((void *) closure);
+
+ return surface;
+}
+
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+
+static void
+finalize_png (finalize_closure_t *closure)
+{
+ cairo_status_t status;
+ status = cairo_surface_write_to_png_stream (closure->surface,
+ closure->write_func,
+ closure->closure);
+ if (status != CAIRO_STATUS_SUCCESS)
+ fail (false, "Failed to write output: %s",
+ cairo_status_to_string (status));
+}
+
+static cairo_surface_t *
+_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
+ void *closure,
+ double width,
+ double height,
+ cairo_content_t content)
+{
+ cairo_surface_t *surface;
+ int w = ceil (width);
+ int h = ceil (height);
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
+ break;
+ default:
+ case CAIRO_CONTENT_COLOR:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+ break;
+ }
+ cairo_status_t status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ fail (false, "Failed to create cairo surface: %s",
+ cairo_status_to_string (status));
+
+ finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
+ png_closure->callback = finalize_png;
+ png_closure->surface = surface;
+ png_closure->write_func = write_func;
+ png_closure->closure = closure;
+
+ if (cairo_surface_set_user_data (surface,
+ &finalize_closure_key,
+ (void *) png_closure,
+ (cairo_destroy_func_t) g_free))
+ g_free ((void *) closure);
+
+ return surface;
+}
+
+#endif
+
+static cairo_status_t
+stdio_write_func (void *closure,
+ const unsigned char *data,
+ unsigned int size)
+{
+ FILE *fp = (FILE *) closure;
+
+ while (size) {
+ size_t ret = fwrite (data, 1, size, fp);
+ size -= ret;
+ data += ret;
+ if (size && ferror (fp))
+ fail (false, "Failed to write output: %s", strerror (errno));
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+ view_options_t *view_opts,
+ output_options_t *out_opts)
+{
+ cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
+ void *closure,
+ double width,
+ double height) = NULL;
+ cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
+ void *closure,
+ double width,
+ double height,
+ cairo_content_t content) = NULL;
+
+ const char *extension = out_opts->output_format;
+ if (!extension) {
+#if HAVE_ISATTY
+ if (isatty (fileno (out_opts->get_file_handle ())))
+ extension = "ansi";
+ else
+#endif
+ extension = "png";
+ }
+ if (0)
+ ;
+ else if (0 == strcasecmp (extension, "ansi"))
+ constructor2 = _cairo_ansi_surface_create_for_stream;
+ #ifdef CAIRO_HAS_PNG_FUNCTIONS
+ else if (0 == strcasecmp (extension, "png"))
+ constructor2 = _cairo_png_surface_create_for_stream;
+ #endif
+ #ifdef CAIRO_HAS_SVG_SURFACE
+ else if (0 == strcasecmp (extension, "svg"))
+ constructor = cairo_svg_surface_create_for_stream;
+ #endif
+ #ifdef CAIRO_HAS_PDF_SURFACE
+ else if (0 == strcasecmp (extension, "pdf"))
+ constructor = cairo_pdf_surface_create_for_stream;
+ #endif
+ #ifdef CAIRO_HAS_PS_SURFACE
+ else if (0 == strcasecmp (extension, "ps"))
+ constructor = cairo_ps_surface_create_for_stream;
+ #ifdef HAS_EPS
+ else if (0 == strcasecmp (extension, "eps"))
+ constructor = _cairo_eps_surface_create_for_stream;
+ #endif
+ #endif
+
+
+ unsigned int fr, fg, fb, fa, br, bg, bb, ba;
+ br = bg = bb = 0; ba = 255;
+ sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
+ fr = fg = fb = 0; fa = 255;
+ sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
+
+ cairo_content_t content;
+ if (!view_opts->annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
+ content = CAIRO_CONTENT_ALPHA;
+ else if (ba == 255)
+ content = CAIRO_CONTENT_COLOR;
+ else
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cairo_surface_t *surface;
+ FILE *f = out_opts->get_file_handle ();
+ if (constructor)
+ surface = constructor (stdio_write_func, f, w, h);
+ else if (constructor2)
+ surface = constructor2 (stdio_write_func, f, w, h, content);
+ else
+ fail (false, "Unknown output format `%s'", extension);
+
+ cairo_t *cr = cairo_create (surface);
+ content = cairo_surface_get_content (surface);
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
+ cairo_paint (cr);
+ cairo_set_source_rgba (cr, 1., 1., 1.,
+ (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
+ break;
+ default:
+ case CAIRO_CONTENT_COLOR:
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
+ break;
+ }
+
+ cairo_surface_destroy (surface);
+ return cr;
+}
+
+void
+helper_cairo_destroy_context (cairo_t *cr)
+{
+ finalize_closure_t *closure = (finalize_closure_t *)
+ cairo_surface_get_user_data (cairo_get_target (cr),
+ &finalize_closure_key);
+ if (closure)
+ closure->callback (closure);
+
+ cairo_status_t status = cairo_status (cr);
+ if (status != CAIRO_STATUS_SUCCESS)
+ fail (false, "Failed: %s",
+ cairo_status_to_string (status));
+ cairo_destroy (cr);
+}
+
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+ hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len,
+ double scale,
+ hb_bool_t utf8_clusters)
+{
+ memset (l, 0, sizeof (*l));
+
+ l->num_glyphs = hb_buffer_get_length (buffer);
+ hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
+ hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
+ l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
+
+ if (text) {
+ l->utf8 = g_strndup (text, text_len);
+ l->utf8_len = text_len;
+ l->num_clusters = l->num_glyphs ? 1 : 0;
+ for (unsigned int i = 1; i < l->num_glyphs; i++)
+ if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+ l->num_clusters++;
+ l->clusters = cairo_text_cluster_allocate (l->num_clusters);
+ }
+
+ if ((l->num_glyphs && !l->glyphs) ||
+ (l->utf8_len && !l->utf8) ||
+ (l->num_clusters && !l->clusters))
+ {
+ l->finish ();
+ return;
+ }
+
+ hb_position_t x = 0, y = 0;
+ int i;
+ for (i = 0; i < (int) l->num_glyphs; i++)
+ {
+ l->glyphs[i].index = hb_glyph[i].codepoint;
+ l->glyphs[i].x = ( hb_position->x_offset + x) * scale;
+ l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
+ x += hb_position->x_advance;
+ y += -hb_position->y_advance;
+
+ hb_position++;
+ }
+ l->glyphs[i].index = -1;
+ l->glyphs[i].x = x * scale;
+ l->glyphs[i].y = y * scale;
+
+ if (l->num_clusters) {
+ memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
+ hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
+ l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
+ unsigned int cluster = 0;
+ const char *start = l->utf8, *end = start;
+ l->clusters[cluster].num_glyphs++;
+ if (backward) {
+ for (i = l->num_glyphs - 2; i >= 0; i--) {
+ if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
+ g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
+ if (utf8_clusters)
+ end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
+ else
+ end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster);
+ l->clusters[cluster].num_bytes = end - start;
+ start = end;
+ cluster++;
+ }
+ l->clusters[cluster].num_glyphs++;
+ }
+ l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
+ } else {
+ for (i = 1; i < (int) l->num_glyphs; i++) {
+ if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
+ g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
+ if (utf8_clusters)
+ end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
+ else
+ end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster);
+ l->clusters[cluster].num_bytes = end - start;
+ start = end;
+ cluster++;
+ }
+ l->clusters[cluster].num_glyphs++;
+ }
+ l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "options.hh"
+
+#include <cairo.h>
+
+#ifndef HELPER_CAIRO_HH
+#define HELPER_CAIRO_HH
+
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+ double font_size);
+
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+ view_options_t *view_opts,
+ output_options_t *out_opts);
+
+void
+helper_cairo_destroy_context (cairo_t *cr);
+
+
+struct helper_cairo_line_t {
+ cairo_glyph_t *glyphs;
+ unsigned int num_glyphs;
+ char *utf8;
+ unsigned int utf8_len;
+ cairo_text_cluster_t *clusters;
+ unsigned int num_clusters;
+ cairo_text_cluster_flags_t cluster_flags;
+
+ void finish (void) {
+ if (glyphs)
+ cairo_glyph_free (glyphs);
+ if (clusters)
+ cairo_text_cluster_free (clusters);
+ if (utf8)
+ g_free (utf8);
+ }
+
+ void get_advance (double *x_advance, double *y_advance) {
+ *x_advance = glyphs[num_glyphs].x;
+ *y_advance = glyphs[num_glyphs].y;
+ }
+};
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+ hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len,
+ double scale,
+ hb_bool_t utf8_clusters);
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "options.hh"
+
+#ifndef HB_MAIN_FONT_TEXT_HH
+#define HB_MAIN_FONT_TEXT_HH
+
+/* main() body for utilities taking font and processing text.*/
+
+template <typename consumer_t>
+struct main_font_text_t
+{
+ main_font_text_t (void)
+ : options ("[FONT-FILE] [TEXT]"),
+ font_opts (&options),
+ input (&options),
+ consumer (&options) {}
+
+ int
+ main (int argc, char **argv)
+ {
+ options.parse (&argc, &argv);
+
+ argc--, argv++;
+ if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++;
+ if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++;
+ if (argc)
+ fail (true, "Too many arguments on the command line");
+ if (!font_opts.font_file)
+ options.usage ();
+ if (!input.text && !input.text_file)
+ input.text_file = "-";
+
+ consumer.init (&font_opts);
+
+ hb_buffer_t *buffer = hb_buffer_create ();
+ unsigned int text_len;
+ const char *text;
+ while ((text = input.get_line (&text_len)))
+ consumer.consume_line (buffer, text, text_len);
+ hb_buffer_destroy (buffer);
+
+ consumer.finish (&font_opts);
+
+ return consumer.failed ? 1 : 0;
+ }
+
+ protected:
+ option_parser_t options;
+ font_options_t font_opts;
+ text_options_t input;
+ consumer_t consumer;
+};
+
+#endif
+
--- /dev/null
+/*
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "options.hh"
+
+#ifdef HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
+
+
+void
+fail (hb_bool_t suggest_help, const char *format, ...)
+{
+ const char *msg;
+
+ va_list vap;
+ va_start (vap, format);
+ msg = g_strdup_vprintf (format, vap);
+ const char *prgname = g_get_prgname ();
+ g_printerr ("%s: %s\n", prgname, msg);
+ if (suggest_help)
+ g_printerr ("Try `%s --help' for more information.\n", prgname);
+
+ exit (1);
+}
+
+
+hb_bool_t debug = false;
+
+static gchar *
+shapers_to_string (void)
+{
+ GString *shapers = g_string_new (NULL);
+ const char **shaper_list = hb_shape_list_shapers ();
+
+ for (; *shaper_list; shaper_list++) {
+ g_string_append (shapers, *shaper_list);
+ g_string_append_c (shapers, ',');
+ }
+ g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
+
+ return g_string_free (shapers, false);
+}
+
+static G_GNUC_NORETURN gboolean
+show_version (const char *name G_GNUC_UNUSED,
+ const char *arg G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED,
+ GError **error G_GNUC_UNUSED)
+{
+ g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
+
+ char *shapers = shapers_to_string ();
+ g_printf ("Available shapers: %s\n", shapers);
+ g_free (shapers);
+ if (strcmp (HB_VERSION_STRING, hb_version_string ()))
+ g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
+
+ exit(0);
+}
+
+
+void
+option_parser_t::add_main_options (void)
+{
+ GOptionEntry entries[] =
+ {
+ {"version", 0, G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", NULL},
+ {"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", NULL},
+ {NULL}
+ };
+ g_option_context_add_main_entries (context, entries, NULL);
+}
+
+static gboolean
+pre_parse (GOptionContext *context G_GNUC_UNUSED,
+ GOptionGroup *group G_GNUC_UNUSED,
+ gpointer data,
+ GError **error)
+{
+ option_group_t *option_group = (option_group_t *) data;
+ option_group->pre_parse (error);
+ return *error == NULL;
+}
+
+static gboolean
+post_parse (GOptionContext *context G_GNUC_UNUSED,
+ GOptionGroup *group G_GNUC_UNUSED,
+ gpointer data,
+ GError **error)
+{
+ option_group_t *option_group = static_cast<option_group_t *>(data);
+ option_group->post_parse (error);
+ return *error == NULL;
+}
+
+void
+option_parser_t::add_group (GOptionEntry *entries,
+ const gchar *name,
+ const gchar *description,
+ const gchar *help_description,
+ option_group_t *option_group)
+{
+ GOptionGroup *group = g_option_group_new (name, description, help_description,
+ static_cast<gpointer>(option_group), NULL);
+ g_option_group_add_entries (group, entries);
+ g_option_group_set_parse_hooks (group, pre_parse, post_parse);
+ g_option_context_add_group (context, group);
+}
+
+void
+option_parser_t::parse (int *argc, char ***argv)
+{
+ setlocale (LC_ALL, "");
+
+ GError *parse_error = NULL;
+ if (!g_option_context_parse (context, argc, argv, &parse_error))
+ {
+ if (parse_error != NULL) {
+ fail (true, "%s", parse_error->message);
+ //g_error_free (parse_error);
+ } else
+ fail (true, "Option parse error");
+ }
+}
+
+
+static gboolean
+parse_margin (const char *name G_GNUC_UNUSED,
+ const char *arg,
+ gpointer data,
+ GError **error G_GNUC_UNUSED)
+{
+ view_options_t *view_opts = (view_options_t *) data;
+ view_options_t::margin_t &m = view_opts->margin;
+ switch (sscanf (arg, "%lf %lf %lf %lf", &m.t, &m.r, &m.b, &m.l)) {
+ case 1: m.r = m.t;
+ case 2: m.b = m.t;
+ case 3: m.l = m.r;
+ case 4: return true;
+ default:
+ g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "%s argument should be one to four space-separated numbers",
+ name);
+ return false;
+ }
+}
+
+
+static gboolean
+parse_shapers (const char *name G_GNUC_UNUSED,
+ const char *arg,
+ gpointer data,
+ GError **error G_GNUC_UNUSED)
+{
+ shape_options_t *shape_opts = (shape_options_t *) data;
+ g_free (shape_opts->shapers);
+ shape_opts->shapers = g_strsplit (arg, ",", 0);
+ return true;
+}
+
+static G_GNUC_NORETURN gboolean
+list_shapers (const char *name G_GNUC_UNUSED,
+ const char *arg G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED,
+ GError **error G_GNUC_UNUSED)
+{
+ for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
+ g_printf ("%s\n", *shaper);
+
+ exit(0);
+}
+
+
+
+static void
+parse_space (char **pp)
+{
+ char c;
+#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
+ while (c = **pp, ISSPACE (c))
+ (*pp)++;
+#undef ISSPACE
+}
+
+static hb_bool_t
+parse_char (char **pp, char c)
+{
+ parse_space (pp);
+
+ if (**pp != c)
+ return false;
+
+ (*pp)++;
+ return true;
+}
+
+static hb_bool_t
+parse_uint (char **pp, unsigned int *pv)
+{
+ char *p = *pp;
+ unsigned int v;
+
+ v = strtol (p, pp, 0);
+
+ if (p == *pp)
+ return false;
+
+ *pv = v;
+ return true;
+}
+
+
+static hb_bool_t
+parse_feature_value_prefix (char **pp, hb_feature_t *feature)
+{
+ if (parse_char (pp, '-'))
+ feature->value = 0;
+ else {
+ parse_char (pp, '+');
+ feature->value = 1;
+ }
+
+ return true;
+}
+
+static hb_bool_t
+parse_feature_tag (char **pp, hb_feature_t *feature)
+{
+ char *p = *pp, c;
+
+ parse_space (pp);
+
+#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
+ while (c = **pp, ISALNUM(c))
+ (*pp)++;
+#undef ISALNUM
+
+ if (p == *pp)
+ return false;
+
+ feature->tag = hb_tag_from_string (p, *pp - p);
+ return true;
+}
+
+static hb_bool_t
+parse_feature_indices (char **pp, hb_feature_t *feature)
+{
+ parse_space (pp);
+
+ hb_bool_t has_start;
+
+ feature->start = 0;
+ feature->end = (unsigned int) -1;
+
+ if (!parse_char (pp, '['))
+ return true;
+
+ has_start = parse_uint (pp, &feature->start);
+
+ if (parse_char (pp, ':')) {
+ parse_uint (pp, &feature->end);
+ } else {
+ if (has_start)
+ feature->end = feature->start + 1;
+ }
+
+ return parse_char (pp, ']');
+}
+
+static hb_bool_t
+parse_feature_value_postfix (char **pp, hb_feature_t *feature)
+{
+ return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
+}
+
+
+static hb_bool_t
+parse_one_feature (char **pp, hb_feature_t *feature)
+{
+ return parse_feature_value_prefix (pp, feature) &&
+ parse_feature_tag (pp, feature) &&
+ parse_feature_indices (pp, feature) &&
+ parse_feature_value_postfix (pp, feature) &&
+ (parse_char (pp, ',') || **pp == '\0');
+}
+
+static void
+skip_one_feature (char **pp)
+{
+ char *e;
+ e = strchr (*pp, ',');
+ if (e)
+ *pp = e + 1;
+ else
+ *pp = *pp + strlen (*pp);
+}
+
+static gboolean
+parse_features (const char *name G_GNUC_UNUSED,
+ const char *arg,
+ gpointer data,
+ GError **error G_GNUC_UNUSED)
+{
+ shape_options_t *shape_opts = (shape_options_t *) data;
+ char *s = (char *) arg;
+ char *p;
+
+ shape_opts->num_features = 0;
+ g_free (shape_opts->features);
+ shape_opts->features = NULL;
+
+ if (!*s)
+ return true;
+
+ /* count the features first, so we can allocate memory */
+ p = s;
+ do {
+ shape_opts->num_features++;
+ p = strchr (p, ',');
+ if (p)
+ p++;
+ } while (p);
+
+ shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
+
+ /* now do the actual parsing */
+ p = s;
+ shape_opts->num_features = 0;
+ while (*p) {
+ if (parse_one_feature (&p, &shape_opts->features[shape_opts->num_features]))
+ shape_opts->num_features++;
+ else
+ skip_one_feature (&p);
+ }
+
+ return true;
+}
+
+
+void
+view_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", NULL},
+ {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: "DEFAULT_BACK")", "red/#rrggbb/#rrggbbaa"},
+ {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: "DEFAULT_FORE")", "red/#rrggbb/#rrggbbaa"},
+ {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"},
+ {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
+ {"font-size", 0, 0, G_OPTION_ARG_DOUBLE, &this->font_size, "Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "view",
+ "View options:",
+ "Options controlling output rendering",
+ this);
+}
+
+void
+shape_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"list-shapers", 0, G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", NULL},
+ {"shaper", 0, G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", NULL},
+ {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Comma-separated list of shapers to try","list"},
+ {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"},
+ {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"},
+ {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"},
+ {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", NULL},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "shape",
+ "Shape options:",
+ "Options controlling the shaping process",
+ this);
+
+ const gchar *features_help = "Comma-separated list of font features\n"
+ "\n"
+ " Features can be enabled or disabled, either globally or limited to\n"
+ " specific character ranges.\n"
+ "\n"
+ " The range indices refer to the positions between Unicode characters,\n"
+ " unless the --utf8-clusters is provided, in which case range indices\n"
+ " refer to UTF-8 byte indices. The position before the first character\n"
+ " is always 0.\n"
+ "\n"
+ " The format is Python-esque. Here is how it all works:\n"
+ "\n"
+ " Syntax: Value: Start: End:\n"
+ "\n"
+ " Setting value:\n"
+ " \"kern\" 1 0 ∞ # Turn feature on\n"
+ " \"+kern\" 1 0 ∞ # Turn feature on\n"
+ " \"-kern\" 0 0 ∞ # Turn feature off\n"
+ " \"kern=0\" 0 0 ∞ # Turn feature off\n"
+ " \"kern=1\" 1 0 ∞ # Turn feature on\n"
+ " \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n"
+ "\n"
+ " Setting index:\n"
+ " \"kern[]\" 1 0 ∞ # Turn feature on\n"
+ " \"kern[:]\" 1 0 ∞ # Turn feature on\n"
+ " \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n"
+ " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n"
+ " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n"
+ " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n"
+ "\n"
+ " Mixing it all:\n"
+ "\n"
+ " \"kern[3:5]=0\" 1 3 5 # Turn feature off for range";
+
+ GOptionEntry entries2[] =
+ {
+ {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"},
+ {NULL}
+ };
+ parser->add_group (entries2,
+ "features",
+ "Features options:",
+ "Options controlling font features used",
+ this);
+}
+
+void
+font_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Font file-name", "filename"},
+ {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Face index (default: 0)", "index"},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "font",
+ "Font options:",
+ "Options controlling the font",
+ this);
+}
+
+void
+text_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"text", 0, 0, G_OPTION_ARG_STRING, &this->text, "Set input text", "string"},
+ {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name\n\n If no text is provided, standard input is used for input.", "filename"},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "text",
+ "Text options:",
+ "Options controlling the input text",
+ this);
+}
+
+void
+output_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"output-file", 0, 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"},
+ {"output-format", 0, 0, G_OPTION_ARG_STRING, &this->output_format, "Set output format", "format"},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "output",
+ "Output options:",
+ "Options controlling the output",
+ this);
+}
+
+
+
+hb_font_t *
+font_options_t::get_font (void) const
+{
+ if (font)
+ return font;
+
+ hb_blob_t *blob = NULL;
+
+ /* Create the blob */
+ {
+ char *font_data;
+ unsigned int len = 0;
+ hb_destroy_func_t destroy;
+ void *user_data;
+ hb_memory_mode_t mm;
+
+ /* This is a hell of a lot of code for just reading a file! */
+ if (!font_file)
+ fail (true, "No font file set");
+
+ if (0 == strcmp (font_file, "-")) {
+ /* read it */
+ GString *gs = g_string_new (NULL);
+ char buf[BUFSIZ];
+#ifdef HAVE__SETMODE
+ _setmode (fileno (stdin), _O_BINARY);
+#endif
+ while (!feof (stdin)) {
+ size_t ret = fread (buf, 1, sizeof (buf), stdin);
+ if (ferror (stdin))
+ fail (false, "Failed reading font from standard input: %s",
+ strerror (errno));
+ g_string_append_len (gs, buf, ret);
+ }
+ len = gs->len;
+ font_data = g_string_free (gs, false);
+ user_data = font_data;
+ destroy = (hb_destroy_func_t) g_free;
+ mm = HB_MEMORY_MODE_WRITABLE;
+ } else {
+ GError *error = NULL;
+ GMappedFile *mf = g_mapped_file_new (font_file, false, &error);
+ if (mf) {
+ font_data = g_mapped_file_get_contents (mf);
+ len = g_mapped_file_get_length (mf);
+ if (len) {
+ destroy = (hb_destroy_func_t) g_mapped_file_unref;
+ user_data = (void *) mf;
+ mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+ } else
+ g_mapped_file_unref (mf);
+ } else {
+ fail (false, "%s", error->message);
+ //g_error_free (error);
+ }
+ if (!len) {
+ /* GMappedFile is buggy, it doesn't fail if file isn't regular.
+ * Try reading.
+ * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */
+ GError *error = NULL;
+ gsize l;
+ if (g_file_get_contents (font_file, &font_data, &l, &error)) {
+ len = l;
+ destroy = (hb_destroy_func_t) g_free;
+ user_data = (void *) font_data;
+ mm = HB_MEMORY_MODE_WRITABLE;
+ } else {
+ fail (false, "%s", error->message);
+ //g_error_free (error);
+ }
+ }
+ }
+
+ blob = hb_blob_create (font_data, len, mm, user_data, destroy);
+ }
+
+ /* Create the face */
+ hb_face_t *face = hb_face_create (blob, face_index);
+ hb_blob_destroy (blob);
+
+
+ font = hb_font_create (face);
+
+ unsigned int upem = hb_face_get_upem (face);
+ hb_font_set_scale (font, upem, upem);
+ hb_face_destroy (face);
+
+#ifdef HAVE_FREETYPE
+ hb_ft_font_set_funcs (font);
+#endif
+
+ return font;
+}
+
+
+const char *
+text_options_t::get_line (unsigned int *len)
+{
+ if (text) {
+ if (text_len == (unsigned int) -1)
+ text_len = strlen (text);
+
+ if (!text_len) {
+ *len = 0;
+ return NULL;
+ }
+
+ const char *ret = text;
+ const char *p = (const char *) memchr (text, '\n', text_len);
+ unsigned int ret_len;
+ if (!p) {
+ ret_len = text_len;
+ text += ret_len;
+ text_len = 0;
+ } else {
+ ret_len = p - ret;
+ text += ret_len + 1;
+ text_len -= ret_len + 1;
+ }
+
+ *len = ret_len;
+ return ret;
+ }
+
+ if (!fp) {
+ if (!text_file)
+ fail (true, "At least one of text or text-file must be set");
+
+ if (0 != strcmp (text_file, "-"))
+ fp = fopen (text_file, "r");
+ else
+ fp = stdin;
+
+ if (!fp)
+ fail (false, "Failed opening text file `%s': %s",
+ text_file, strerror (errno));
+
+ gs = g_string_new (NULL);
+ }
+
+ g_string_set_size (gs, 0);
+ char buf[BUFSIZ];
+ while (fgets (buf, sizeof (buf), fp)) {
+ unsigned int bytes = strlen (buf);
+ if (bytes && buf[bytes - 1] == '\n') {
+ bytes--;
+ g_string_append_len (gs, buf, bytes);
+ break;
+ }
+ g_string_append_len (gs, buf, bytes);
+ }
+ if (ferror (fp))
+ fail (false, "Failed reading text: %s",
+ strerror (errno));
+ *len = gs->len;
+ return !*len && feof (fp) ? NULL : gs->str;
+}
+
+
+FILE *
+output_options_t::get_file_handle (void)
+{
+ if (fp)
+ return fp;
+
+ if (output_file)
+ fp = fopen (output_file, "wb");
+ else {
+#ifdef HAVE__SETMODE
+ _setmode (fileno (stdout), _O_BINARY);
+#endif
+ fp = stdout;
+ }
+ if (!fp)
+ fail (false, "Cannot open output file `%s': %s",
+ g_filename_display_name (output_file), strerror (errno));
+
+ return fp;
+}
+
+static gboolean
+parse_verbose (const char *name G_GNUC_UNUSED,
+ const char *arg G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED,
+ GError **error G_GNUC_UNUSED)
+{
+ format_options_t *format_opts = (format_options_t *) data;
+ format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
+ return true;
+}
+
+void
+format_options_t::add_options (option_parser_t *parser)
+{
+ GOptionEntry entries[] =
+ {
+ {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", NULL},
+ {"no-positions", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_positions, "Do not show glyph positions", NULL},
+ {"no-clusters", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_clusters, "Do not show cluster mapping", NULL},
+ {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Show input text", NULL},
+ {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Show input Unicode codepoints", NULL},
+ {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Show line numbers", NULL},
+ {"verbose", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,(gpointer) &parse_verbose, "Show everything", NULL},
+ {NULL}
+ };
+ parser->add_group (entries,
+ "format",
+ "Format options:",
+ "Options controlling the formatting of buffer contents",
+ this);
+}
+
+void
+format_options_t::serialize_unicode (hb_buffer_t *buffer,
+ GString *gs)
+{
+ unsigned int num_glyphs = hb_buffer_get_length (buffer);
+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+
+ g_string_append_c (gs, '<');
+ for (unsigned int i = 0; i < num_glyphs; i++)
+ {
+ if (i)
+ g_string_append_c (gs, ',');
+ g_string_append_printf (gs, "U+%04X", info->codepoint);
+ info++;
+ }
+ g_string_append_c (gs, '>');
+}
+
+void
+format_options_t::serialize_glyphs (hb_buffer_t *buffer,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs)
+{
+ unsigned int num_glyphs = hb_buffer_get_length (buffer);
+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+ g_string_append_c (gs, '[');
+ for (unsigned int i = 0; i < num_glyphs; i++)
+ {
+ if (i)
+ g_string_append_c (gs, '|');
+
+ char glyph_name[32];
+ if (show_glyph_names) {
+ hb_font_get_glyph_name (font, info->codepoint, glyph_name, sizeof (glyph_name));
+ g_string_append_printf (gs, "%s", glyph_name);
+ } else
+ g_string_append_printf (gs, "%u", info->codepoint);
+
+ if (show_clusters) {
+ g_string_append_printf (gs, "=%u", info->cluster);
+ if (utf8_clusters)
+ g_string_append (gs, "u8");
+ }
+
+ if (show_positions && (pos->x_offset || pos->y_offset)) {
+ g_string_append_c (gs, '@');
+ if (pos->x_offset) g_string_append_printf (gs, "%d", pos->x_offset);
+ if (pos->y_offset) g_string_append_printf (gs, ",%d", pos->y_offset);
+ }
+ if (show_positions && (pos->x_advance || pos->y_advance)) {
+ g_string_append_c (gs, '+');
+ if (pos->x_advance) g_string_append_printf (gs, "%d", pos->x_advance);
+ if (pos->y_advance) g_string_append_printf (gs, ",%d", pos->y_advance);
+ }
+
+ info++;
+ pos++;
+ }
+ g_string_append_c (gs, ']');
+}
+void
+format_options_t::serialize_line_no (unsigned int line_no,
+ GString *gs)
+{
+ if (show_line_num)
+ g_string_append_printf (gs, "%d: ", line_no);
+}
+void
+format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer,
+ unsigned int line_no,
+ const char *text,
+ unsigned int text_len,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs)
+{
+ if (show_text) {
+ serialize_line_no (line_no, gs);
+ g_string_append_c (gs, '(');
+ g_string_append_len (gs, text, text_len);
+ g_string_append_c (gs, ')');
+ g_string_append_c (gs, '\n');
+ }
+
+ if (show_unicode) {
+ serialize_line_no (line_no, gs);
+ serialize_unicode (buffer, gs);
+ g_string_append_c (gs, '\n');
+ }
+}
+void
+format_options_t::serialize_message (unsigned int line_no,
+ const char *msg,
+ GString *gs)
+{
+ serialize_line_no (line_no, gs);
+ g_string_append_printf (gs, "%s", msg);
+ g_string_append_c (gs, '\n');
+}
+void
+format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer,
+ unsigned int line_no,
+ const char *text,
+ unsigned int text_len,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs)
+{
+ serialize_line_no (line_no, gs);
+ serialize_glyphs (buffer, font, utf8_clusters, gs);
+ g_string_append_c (gs, '\n');
+}
--- /dev/null
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef OPTIONS_HH
+#define OPTIONS_HH
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for isatty() */
+#endif
+#ifdef HAVE_IO_H
+#include <io.h> /* for _setmode() under Windows */
+#endif
+
+#include <hb.h>
+#ifdef HAVE_OT
+#include <hb-ot.h>
+#endif
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#undef MIN
+template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
+
+#undef MAX
+template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
+
+
+void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN;
+
+
+extern hb_bool_t debug;
+
+struct option_group_t
+{
+ virtual void add_options (struct option_parser_t *parser) = 0;
+
+ virtual void pre_parse (GError **error G_GNUC_UNUSED) {};
+ virtual void post_parse (GError **error G_GNUC_UNUSED) {};
+};
+
+
+struct option_parser_t
+{
+ option_parser_t (const char *usage) {
+ memset (this, 0, sizeof (*this));
+ usage_str = usage;
+ context = g_option_context_new (usage);
+
+ add_main_options ();
+ }
+ ~option_parser_t (void) {
+ g_option_context_free (context);
+ }
+
+ void add_main_options (void);
+
+ void add_group (GOptionEntry *entries,
+ const gchar *name,
+ const gchar *description,
+ const gchar *help_description,
+ option_group_t *option_group);
+
+ void parse (int *argc, char ***argv);
+
+ G_GNUC_NORETURN void usage (void) {
+ g_printerr ("Usage: %s [OPTION...] %s\n", g_get_prgname (), usage_str);
+ exit (1);
+ }
+
+ const char *usage_str;
+ GOptionContext *context;
+};
+
+
+#define DEFAULT_MARGIN 16
+#define DEFAULT_FORE "#000000"
+#define DEFAULT_BACK "#FFFFFF"
+#define DEFAULT_FONT_SIZE 256
+
+struct view_options_t : option_group_t
+{
+ view_options_t (option_parser_t *parser) {
+ annotate = false;
+ fore = DEFAULT_FORE;
+ back = DEFAULT_BACK;
+ line_space = 0;
+ margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
+ font_size = DEFAULT_FONT_SIZE;
+
+ add_options (parser);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ hb_bool_t annotate;
+ const char *fore;
+ const char *back;
+ double line_space;
+ struct margin_t {
+ double t, r, b, l;
+ } margin;
+ double font_size;
+};
+
+
+struct shape_options_t : option_group_t
+{
+ shape_options_t (option_parser_t *parser)
+ {
+ direction = language = script = NULL;
+ features = NULL;
+ num_features = 0;
+ shapers = NULL;
+ utf8_clusters = false;
+
+ add_options (parser);
+ }
+ ~shape_options_t (void)
+ {
+ free (features);
+ g_free (shapers);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ void setup_buffer (hb_buffer_t *buffer)
+ {
+ hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
+ hb_buffer_set_script (buffer, hb_script_from_string (script, -1));
+ hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
+ }
+
+ void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len)
+ {
+ hb_buffer_reset (buffer);
+ hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
+
+ if (!utf8_clusters) {
+ /* Reset cluster values to refer to Unicode character index
+ * instead of UTF-8 index. */
+ unsigned int num_glyphs = hb_buffer_get_length (buffer);
+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+ for (unsigned int i = 0; i < num_glyphs; i++)
+ {
+ info->cluster = i;
+ info++;
+ }
+ }
+
+ setup_buffer (buffer);
+ }
+
+ hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer)
+ {
+ return hb_shape_full (font, buffer, features, num_features, shapers);
+ }
+
+ void shape_closure (const char *text, int text_len,
+ hb_font_t *font, hb_buffer_t *buffer,
+ hb_set_t *glyphs)
+ {
+ hb_buffer_reset (buffer);
+ hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
+ setup_buffer (buffer);
+ hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs);
+ }
+
+ const char *direction;
+ const char *language;
+ const char *script;
+ hb_feature_t *features;
+ unsigned int num_features;
+ char **shapers;
+ hb_bool_t utf8_clusters;
+};
+
+
+struct font_options_t : option_group_t
+{
+ font_options_t (option_parser_t *parser) {
+ font_file = NULL;
+ face_index = 0;
+
+ font = NULL;
+
+ add_options (parser);
+ }
+ ~font_options_t (void) {
+ hb_font_destroy (font);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ hb_font_t *get_font (void) const;
+
+ const char *font_file;
+ int face_index;
+
+ private:
+ mutable hb_font_t *font;
+};
+
+
+struct text_options_t : option_group_t
+{
+ text_options_t (option_parser_t *parser) {
+ text = NULL;
+ text_file = NULL;
+
+ fp = NULL;
+ gs = NULL;
+ text_len = (unsigned int) -1;
+
+ add_options (parser);
+ }
+ ~text_options_t (void) {
+ if (gs)
+ g_string_free (gs, true);
+ if (fp)
+ fclose (fp);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ void post_parse (GError **error G_GNUC_UNUSED) {
+ if (text && text_file)
+ g_set_error (error,
+ G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "Only one of text and text-file can be set");
+
+ };
+
+ const char *get_line (unsigned int *len);
+
+ const char *text;
+ const char *text_file;
+
+ private:
+ FILE *fp;
+ GString *gs;
+ unsigned int text_len;
+};
+
+struct output_options_t : option_group_t
+{
+ output_options_t (option_parser_t *parser) {
+ output_file = NULL;
+ output_format = NULL;
+
+ fp = NULL;
+
+ add_options (parser);
+ }
+ ~output_options_t (void) {
+ if (fp)
+ fclose (fp);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ void post_parse (GError **error G_GNUC_UNUSED)
+ {
+ if (output_file && !output_format) {
+ output_format = strrchr (output_file, '.');
+ if (output_format)
+ output_format++; /* skip the dot */
+ }
+
+ if (output_file && 0 == strcmp (output_file, "-"))
+ output_file = NULL; /* STDOUT */
+ }
+
+ FILE *get_file_handle (void);
+
+ const char *output_file;
+ const char *output_format;
+
+ mutable FILE *fp;
+};
+
+struct format_options_t : option_group_t
+{
+ format_options_t (option_parser_t *parser) {
+ show_glyph_names = true;
+ show_positions = true;
+ show_clusters = true;
+ show_text = false;
+ show_unicode = false;
+ show_line_num = false;
+
+ add_options (parser);
+ }
+
+ void add_options (option_parser_t *parser);
+
+ void serialize_unicode (hb_buffer_t *buffer,
+ GString *gs);
+ void serialize_glyphs (hb_buffer_t *buffer,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs);
+ void serialize_line_no (unsigned int line_no,
+ GString *gs);
+ void serialize_buffer_of_text (hb_buffer_t *buffer,
+ unsigned int line_no,
+ const char *text,
+ unsigned int text_len,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs);
+ void serialize_message (unsigned int line_no,
+ const char *msg,
+ GString *gs);
+ void serialize_buffer_of_glyphs (hb_buffer_t *buffer,
+ unsigned int line_no,
+ const char *text,
+ unsigned int text_len,
+ hb_font_t *font,
+ hb_bool_t utf8_clusters,
+ GString *gs);
+
+
+ hb_bool_t show_glyph_names;
+ hb_bool_t show_positions;
+ hb_bool_t show_clusters;
+ hb_bool_t show_text;
+ hb_bool_t show_unicode;
+ hb_bool_t show_line_num;
+};
+
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2011,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "options.hh"
+
+#ifndef HB_SHAPE_CONSUMER_HH
+#define HB_SHAPE_CONSUMER_HH
+
+
+template <typename output_t>
+struct shape_consumer_t
+{
+ shape_consumer_t (option_parser_t *parser)
+ : shaper (parser),
+ output (parser) {}
+
+ void init (const font_options_t *font_opts)
+ {
+ font = hb_font_reference (font_opts->get_font ());
+ output.init (font_opts);
+ failed = false;
+ }
+ void consume_line (hb_buffer_t *buffer,
+ const char *text,
+ unsigned int text_len)
+ {
+ output.new_line ();
+
+ shaper.populate_buffer (buffer, text, text_len);
+ output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
+
+ if (!shaper.shape (font, buffer)) {
+ failed = true;
+ hb_buffer_set_length (buffer, 0);
+ output.shape_failed (buffer, text, text_len, shaper.utf8_clusters);
+ return;
+ }
+
+ output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters);
+ }
+ void finish (const font_options_t *font_opts)
+ {
+ output.finish (font_opts);
+ hb_font_destroy (font);
+ font = NULL;
+ }
+
+ public:
+ bool failed;
+
+ protected:
+ shape_options_t shaper;
+ output_t output;
+
+ hb_font_t *font;
+};
+
+
+#endif
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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