summaryrefslogtreecommitdiff
path: root/modules/indic
diff options
context:
space:
mode:
authorEric Mader <mader@jtcsv.com>2002-05-07 20:39:14 +0000
committerEric Mader <emader@src.gnome.org>2002-05-07 20:39:14 +0000
commit75f53e998d6aa5961cfacde2f4a943df5c026c10 (patch)
tree4802d42b003ffb6679f76b9c3ac78be98eda433e /modules/indic
parentd8131cfedbb7903c382b5df44bb00f8ebe5d00b2 (diff)
downloadpango-75f53e998d6aa5961cfacde2f4a943df5c026c10.tar.gz
Add modules modules/indic/indic-xft.c, indic-ot.c, indic-ot.h,
Mon May 06 15:07:39 2002 Eric Mader <mader@jtcsv.com> * Add modules modules/indic/indic-xft.c, indic-ot.c, indic-ot.h, indic-ot-class-tables.c * pango/opentype/ftxgdef.c: Compute full offset for mark attachment class table * pango/opentype/ftxgpos.c: Only return TTO_Err_Not_Covered if nothing matches * pango/opentype/pango-ot-ruleset.c: enable GPOS processing
Diffstat (limited to 'modules/indic')
-rw-r--r--modules/indic/Makefile.am35
-rw-r--r--modules/indic/indic-fc.c444
-rw-r--r--modules/indic/indic-ot-class-tables.c416
-rw-r--r--modules/indic/indic-ot.c411
-rw-r--r--modules/indic/indic-ot.h250
-rw-r--r--modules/indic/indic-xft.c444
6 files changed, 1997 insertions, 3 deletions
diff --git a/modules/indic/Makefile.am b/modules/indic/Makefile.am
index 3fd999bb..08646a14 100644
--- a/modules/indic/Makefile.am
+++ b/modules/indic/Makefile.am
@@ -8,11 +8,34 @@ sources = \
devanagari-x.c \
pango-indic-script.h
+xft_sources = \
+ indic-xft.c \
+ indic-ot-class-tables.c \
+ indic-ot.c \
+ indic-ot.h
+
pangolibs = $(top_builddir)/pango/libpango-$(PANGO_API_VERSION).la $(FRIBIDI_LIBS) $(GLIB_LIBS)
pangoxlibs = $(top_builddir)/pango/libpangox-$(PANGO_API_VERSION).la $(X_LIBS) $(pangolibs)
pangoxftlibs = $(top_builddir)/pango/libpangoxft-$(PANGO_API_VERSION).la $(XFT_LIBS) $(pangolibs)
pangoft2libs = $(top_builddir)/pango/libpangoft2-$(PANGO_API_VERSION).la $(FREETYPE_LIBS) $(pangolibs)
+if HAVE_XFT
+if INCLUDE_INDIC_XFT
+indic_xft_inst=
+indic_xft_noinst=libpango-indic-xft.la
+indic_xft_cflags=-DXFT_MODULE_PREFIX $(FREETYPE_CFLAGS)
+else
+indic_xft_inst=pango-indic-xft.la
+indic_xft_noinst=
+indic_xft_cflags= $(FREETYPE_CFLAGS)
+indic_xft_libadd=$(pangoxftlibs)
+endif
+else
+indic_xft_inst=
+indic_xft_noints=
+indic_xft_cflags=
+endif
+
if HAVE_X
if INCLUDE_MYANMAR_X
@@ -55,14 +78,14 @@ gujarati_x_inst=pango-gujarati-x.la
gujarati_x_libadd=$(pangoxlibs)
endif
-noinst_LTLIBRARIES = $(myanmar_x_noinst) $(gurmukhi_x_noinst) $(bengali_x_noinst) $(devanagari_x_noinst) $(gujarati_x_noinst)
+noinst_LTLIBRARIES = $(myanmar_x_noinst) $(gurmukhi_x_noinst) $(bengali_x_noinst) $(devanagari_x_noinst) $(gujarati_x_noinst) $(indic_xft_noinst)
moduledir = $(libdir)/pango/$(PANGO_MODULE_VERSION)/modules
-module_LTLIBRARIES = $(myanmar_x_inst) $(gurmukhi_x_inst) $(bengali_x_inst) $(devanagari_x_inst) $(gujarati_x_inst)
+module_LTLIBRARIES = $(myanmar_x_inst) $(gurmukhi_x_inst) $(bengali_x_inst) $(devanagari_x_inst) $(gujarati_x_inst) $(indic_xft_inst)
endif
-INCLUDES = -DPANGO_ENABLE_ENGINE -DG_DISABLE_DEPRECATED -I$(top_srcdir) -I$(top_srcdir)/pango/ $(X_CFLAGS) $(myanmar_x_cflags) $(gurmukhi_x_cflags) $(bengali_x_cflags) $(devanagari_x_cflags) $(gujarati_x_cflags)
+INCLUDES = -DPANGO_ENABLE_ENGINE -DG_DISABLE_DEPRECATED -I$(top_srcdir) -I$(top_srcdir)/pango/ $(X_CFLAGS) $(myanmar_x_cflags) $(gurmukhi_x_cflags) $(bengali_x_cflags) $(devanagari_x_cflags) $(gujarati_x_cflags) $(indic_xft_cflags)
EXTRA_DIST = \
pango-indic-script.h
@@ -107,6 +130,12 @@ else
pango_gujarati_x_la_SOURCES = gujarati-x.c
endif
+pango_indic_xft_la_LDFLAGS = -export-dynamic -avoid-version -module
+pango_indic_xft_la_LIBADD = $(indic_xft_libadd)
+pango_indic_xft_la_SOURCES = $(xft_sources)
+
+libpango_indic_xft_la_SOURCES = $(xft_sources)
+
included-modules: $(noinst_LTLIBRARIES)
.PHONY: included-modules
diff --git a/modules/indic/indic-fc.c b/modules/indic/indic-fc.c
new file mode 100644
index 00000000..c3d55f80
--- /dev/null
+++ b/modules/indic/indic-fc.c
@@ -0,0 +1,444 @@
+/* Pango
+ * indic-xft.c:
+ *
+ * Copyright (C) 2001, 2002 IBM Corporation
+ * Author: Eric Mader <mader@jtcsv.com>
+ * Based on arabic-xft.c by Owen Taylor <otaylor@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "indic-ot.h"
+
+#include "pangoxft.h"
+#include "pango-engine.h"
+#include "pango-utils.h"
+
+typedef struct _PangoEngineShapeIndic PangoEngineShapeIndic;
+typedef struct _PangoIndicInfo PangoIndicInfo;
+
+struct _PangoEngineShapeIndic
+{
+ PangoEngineShape shapeEngine;
+ PangoIndicInfo *indicInfo;
+};
+
+struct _PangoIndicInfo
+{
+ PangoOTTag scriptTag;
+ IndicOTClassTable *classTable;
+ gchar *gsubQuarkName;
+ gchar *gposQuarkName;
+};
+
+#define INDIC_ENGINE_INFO(script) {#script "ScriptEngineXft", PANGO_ENGINE_TYPE_SHAPE, PANGO_RENDER_TYPE_XFT, script##_ranges, G_N_ELEMENTS(script##_ranges)}
+
+#define PANGO_INDIC_INFO(script) {OT_TAG_##script, &script##_class_table, "pango-indic-" #script "-GSUB-ruleset", "pango-indic-" #script "-GPOS-rulsest"}
+
+#define INDIC_SCRIPT_RANGE(script) {SCRIPT_RANGE_##script, "*"}
+
+#define OT_TAG_deva FT_MAKE_TAG('d','e','v','a')
+#define OT_TAG_beng FT_MAKE_TAG('b','e','n','g')
+#define OT_TAG_punj FT_MAKE_TAG('p','u','n','j')
+#define OT_TAG_gujr FT_MAKE_TAG('g','u','j','r')
+#define OT_TAG_orya FT_MAKE_TAG('o','r','y','a')
+#define OT_TAG_taml FT_MAKE_TAG('t','a','m','l')
+#define OT_TAG_telu FT_MAKE_TAG('t','e','l','u')
+#define OT_TAG_knda FT_MAKE_TAG('k','n','d','a')
+#define OT_TAG_mlym FT_MAKE_TAG('m','l','y','m')
+
+static PangoEngineRange deva_ranges[] = {
+ INDIC_SCRIPT_RANGE(deva), /* Devanagari */
+};
+
+static PangoEngineRange beng_ranges[] = {
+ INDIC_SCRIPT_RANGE(beng), /* Bengali */
+};
+
+static PangoEngineRange punj_ranges[] = {
+ INDIC_SCRIPT_RANGE(punj), /* Punjabi */
+};
+
+static PangoEngineRange gujr_ranges[] = {
+ INDIC_SCRIPT_RANGE(gujr), /* Gujarati */
+};
+
+static PangoEngineRange orya_ranges[] = {
+ INDIC_SCRIPT_RANGE(orya), /* Oriya */
+};
+
+static PangoEngineRange taml_ranges[] = {
+ INDIC_SCRIPT_RANGE(taml), /* Tamil */
+};
+
+static PangoEngineRange telu_ranges[] = {
+ INDIC_SCRIPT_RANGE(telu), /* Telugu */
+};
+
+static PangoEngineRange knda_ranges[] = {
+ INDIC_SCRIPT_RANGE(knda), /* Kannada */
+};
+
+static PangoEngineRange mlym_ranges[] = {
+ INDIC_SCRIPT_RANGE(mlym), /* Malayalam */
+};
+
+static PangoEngineInfo script_engines[] = {
+ INDIC_ENGINE_INFO(deva), INDIC_ENGINE_INFO(beng), INDIC_ENGINE_INFO(punj),
+ INDIC_ENGINE_INFO(gujr), INDIC_ENGINE_INFO(orya), INDIC_ENGINE_INFO(taml),
+ INDIC_ENGINE_INFO(telu), INDIC_ENGINE_INFO(knda), INDIC_ENGINE_INFO(mlym)
+};
+
+/*
+ * WARNING: These entries need to be in the same order as the entries
+ * in script_engines[].
+ *
+ * FIXME: remove this requirement, either by encapsulating the order
+ * in a macro that calls a body macro that can be redefined, or by
+ * putting the pointers to the PangoEngineInfo in PangoIndicInfo...
+ */
+static PangoIndicInfo indic_info[] = {
+ PANGO_INDIC_INFO(deva), PANGO_INDIC_INFO(beng), PANGO_INDIC_INFO(punj),
+ PANGO_INDIC_INFO(gujr), PANGO_INDIC_INFO(orya), PANGO_INDIC_INFO(taml),
+ PANGO_INDIC_INFO(telu), PANGO_INDIC_INFO(knda), PANGO_INDIC_INFO(mlym)
+};
+
+void
+maybe_add_GSUB_feature (PangoOTRuleset *ruleset,
+ PangoOTInfo *info,
+ guint script_index,
+ PangoOTTag feature_tag,
+ gulong property_bit)
+{
+ guint feature_index;
+
+ /* 0xffff == default language system */
+ if (pango_ot_info_find_feature (info, PANGO_OT_TABLE_GSUB,
+ feature_tag, script_index, 0xffff, &feature_index))
+ {
+ /*
+ printf("Added GSUB feature '%c%c%c%c' = %8.8X\n", feature_tag>>24, feature_tag>>16&0xFF, feature_tag>>8&0xFF, feature_tag&0xFF, property_bit);
+ */
+
+ pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GSUB, feature_index,
+ property_bit);
+ }
+}
+
+void maybe_add_GPOS_feature (PangoOTRuleset *ruleset,
+ PangoOTInfo *info,
+ guint script_index,
+ PangoOTTag feature_tag,
+ gulong property_bit)
+{
+ guint feature_index;
+
+ if (pango_ot_info_find_feature (info, PANGO_OT_TABLE_GPOS,
+ feature_tag,script_index, 0xffff, &feature_index))
+ {
+ /*
+ printf("Added GPOS feature '%c%c%c%c' = %8.8X\n", feature_tag>>24, feature_tag>>16&0xFF, feature_tag>>8&0xFF, feature_tag&0xFF, property_bit);
+ */
+
+ pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GPOS, feature_index,
+ property_bit);
+ }
+}
+
+static PangoOTRuleset *
+get_gsub_ruleset (PangoFont *font, PangoIndicInfo *indic_info)
+{
+ PangoOTInfo *info = pango_xft_font_get_ot_info (font);
+ GQuark ruleset_quark = g_quark_from_string (indic_info->gsubQuarkName);
+ PangoOTRuleset *ruleset;
+
+ if (!info)
+ return NULL;
+
+ ruleset = g_object_get_qdata (G_OBJECT (font), ruleset_quark);
+
+ if (!ruleset)
+ {
+ guint script_index;
+
+ ruleset = pango_ot_ruleset_new (info);
+
+ if (pango_ot_info_find_script (info, PANGO_OT_TABLE_GSUB,
+ indic_info->scriptTag, &script_index))
+ {
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('n','u','k','t'), nukt);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','k','h','n'), akhn);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('r','p','h','f'), rphf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','f'), blwf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('h','a','l','f'), half);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','s','t','f'), pstf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('v','a','t','u'), vatu);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','r','e','s'), pres);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','s'), blws);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','b','v','s'), abvs);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','s','t','s'), psts);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('h','a','l','n'), haln);
+ }
+
+ g_object_set_qdata_full (G_OBJECT (font), ruleset_quark, ruleset,
+ (GDestroyNotify)g_object_unref);
+ }
+
+ return ruleset;
+}
+
+
+static PangoOTRuleset *
+get_gpos_ruleset (PangoFont *font, PangoIndicInfo *indic_info)
+{
+ PangoOTInfo *info = pango_xft_font_get_ot_info (font);
+ GQuark ruleset_quark = g_quark_from_string (indic_info->gposQuarkName);
+ PangoOTRuleset *ruleset;
+
+ if (!info)
+ return NULL;
+
+ ruleset = g_object_get_qdata (G_OBJECT (font), ruleset_quark);
+
+ if (!ruleset)
+ {
+ guint script_index;
+
+ ruleset = pango_ot_ruleset_new (info);
+
+ if (1 && pango_ot_info_find_script (info, PANGO_OT_TABLE_GPOS,
+ indic_info->scriptTag, &script_index))
+ {
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','m'), blwm);
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','b','v','m'), abvm);
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('d','i','s','t'), dist);
+ }
+
+ g_object_set_qdata_full (G_OBJECT (font), ruleset_quark, ruleset,
+ (GDestroyNotify)g_object_unref);
+ }
+
+ return ruleset;
+}
+static void
+set_glyphs (PangoFont *font, const gunichar *wcs, const glong *indices, glong n_glyphs, PangoGlyphString *glyphs)
+{
+ FT_Face face = pango_xft_font_get_face (font);
+ gint i;
+
+ g_assert (face);
+
+ pango_glyph_string_set_size (glyphs, n_glyphs);
+
+ for (i = 0; i < n_glyphs; i += 1)
+ {
+ PangoGlyph glyph = FT_Get_Char_Index (face, wcs[i]);
+
+ glyphs->glyphs[i].glyph = glyph;
+ glyphs->log_clusters[i] = indices[i];
+ }
+}
+
+/*
+ * FIXME: should this check for null pointers, etc.?
+ */
+static gunichar *
+expand_text(const gchar *text, glong length, glong **offsets, glong *n_chars)
+{
+ const gchar *p;
+ gunichar *wcs, *wco;
+ glong i, *oo;
+
+ *n_chars = g_utf8_strlen (text, length);
+ wcs = g_new (gunichar, *n_chars);
+ *offsets = g_new (glong, *n_chars + 1);
+
+ p = text;
+ wco = wcs;
+ oo = *offsets;
+ for (i = 0; i < *n_chars; i += 1)
+ {
+ *wco++ = g_utf8_get_char (p);
+ *oo++ = p - text;
+
+ p = g_utf8_next_char (p);
+ }
+
+ *oo = p - text;
+
+ return wcs;
+}
+
+
+/* analysis->shape_engine has the PangoEngine... */
+static void
+indic_engine_shape (PangoFont *font,
+ const char *text,
+ gint length,
+ PangoAnalysis *analysis,
+ PangoGlyphString *glyphs)
+{
+ glong i, n_chars, n_glyphs;
+ gulong *tags = NULL;
+ gunichar *wc_in = NULL, *wc_out = NULL;
+ glong *utf8_offsets = NULL;
+ glong *indices = NULL;
+ FT_Face face;
+ PangoOTRuleset *gsub_ruleset = NULL, *gpos_ruleset = NULL;
+ PangoEngineShapeIndic *indic_shape_engine = NULL;
+ PangoIndicInfo *indic_info = NULL;
+
+ g_return_if_fail (font != NULL);
+ g_return_if_fail (text != NULL);
+ g_return_if_fail (length >= 0);
+ g_return_if_fail (analysis != NULL);
+
+ face = pango_xft_font_get_face (font);
+ g_assert (face != NULL);
+
+ indic_shape_engine = (PangoEngineShapeIndic *) analysis->shape_engine;
+
+#if 1
+ g_assert (indic_shape_engine->shapeEngine.engine.length == sizeof (PangoEngineShapeIndic));
+#endif
+
+ indic_info = indic_shape_engine->indicInfo;
+
+ wc_in = expand_text (text, length, &utf8_offsets, &n_chars);
+ n_glyphs = indic_ot_reorder (wc_in, utf8_offsets, n_chars, indic_info->classTable, NULL, NULL, NULL);
+
+ wc_out = g_new (gunichar, n_glyphs);
+ indices = g_new (glong, n_glyphs);
+ tags = g_new (gulong, n_glyphs);
+
+ n_glyphs = indic_ot_reorder (wc_in, utf8_offsets, n_chars, indic_info->classTable, wc_out, indices, tags);
+
+ pango_glyph_string_set_size (glyphs, n_glyphs);
+ set_glyphs(font, wc_out, indices, n_glyphs, glyphs);
+
+ /* do gsub processing */
+ gsub_ruleset = get_gsub_ruleset (font, indic_info);
+ if (gsub_ruleset != NULL)
+ {
+ pango_ot_ruleset_shape (gsub_ruleset, glyphs, tags);
+ }
+
+ /* apply default positioning */
+ for (i = 0; i < glyphs->num_glyphs; i += 1)
+ {
+ if (glyphs->glyphs[i].glyph != 0)
+ {
+ PangoRectangle logical_rect;
+
+ pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
+ glyphs->glyphs[i].geometry.width = logical_rect.width;
+ }
+ else
+ {
+ glyphs->glyphs[i].geometry.width = 0;
+ }
+
+ glyphs->glyphs[i].geometry.x_offset = 0;
+ glyphs->glyphs[i].geometry.y_offset = 0;
+ }
+
+#if 1
+ /* do gpos processing */
+ gpos_ruleset = get_gpos_ruleset (font, indic_info);
+ if (gpos_ruleset != NULL)
+ {
+ pango_ot_ruleset_shape (gpos_ruleset, glyphs, tags);
+ }
+#endif
+
+ g_free (tags);
+ g_free (indices);
+ g_free (wc_out);
+ g_free (wc_in);
+ g_free (utf8_offsets);
+}
+
+static PangoCoverage *
+indic_engine_get_coverage (PangoFont *font,
+ PangoLanguage *lang)
+{
+ return pango_font_get_coverage (font, lang);
+}
+
+static PangoEngine *
+indic_engine_xft_new (gint index)
+{
+ PangoEngineShapeIndic *result;
+
+ result = g_new (PangoEngineShapeIndic, 1);
+
+ result->shapeEngine.engine.id = script_engines[index].id;
+ result->shapeEngine.engine.type = PANGO_ENGINE_TYPE_SHAPE;
+ result->shapeEngine.engine.length = sizeof (*result);
+ result->shapeEngine.script_shape = indic_engine_shape;
+ result->shapeEngine.get_coverage = indic_engine_get_coverage;
+
+ result->indicInfo = &indic_info[index];
+
+ return (PangoEngine *)result;
+}
+
+/* The following three functions provide the public module API for
+ * Pango. If we are compiling it as a module, then we name the
+ * entry points script_engine_list, etc. But if we are compiling
+ * it for inclusion directly in Pango, then we need them to
+ * to have distinct names for this module, so we prepend
+ * _pango_indic_xft_
+ */
+#ifdef XFT_MODULE_PREFIX
+#define MODULE_ENTRY(func) _pango_indic_xft_##func
+#else
+#define MODULE_ENTRY(func) func
+#endif
+
+/* List the engines contained within this module
+ */
+void
+MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, gint *n_engines)
+{
+ *engines = script_engines;
+ *n_engines = G_N_ELEMENTS (script_engines);
+}
+
+/* Load a particular engine given the ID for the engine
+ */
+PangoEngine *
+MODULE_ENTRY(script_engine_load) (const char *id)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(script_engines); i += 1)
+ {
+ if (!strcmp(id, script_engines[i].id))
+ {
+ return indic_engine_xft_new(i);
+ }
+ }
+
+ return NULL;
+}
+
+void
+MODULE_ENTRY(script_engine_unload) (PangoEngine *engine)
+{
+}
diff --git a/modules/indic/indic-ot-class-tables.c b/modules/indic/indic-ot-class-tables.c
new file mode 100644
index 00000000..644b40ee
--- /dev/null
+++ b/modules/indic/indic-ot-class-tables.c
@@ -0,0 +1,416 @@
+/* Pango
+ * indic-ot-class-tables.c:
+ *
+ * Copyright (C) 2001, 2002 IBM Corporation. All Rights Reserved.
+ * Author: Eric Mader <mader@jtcsv.com>
+ *
+ */
+
+#include "indic-ot.h"
+
+enum
+{
+ /*
+ * Split matra table indices
+ */
+ _x1 = 1 << CF_INDEX_SHIFT,
+ _x2 = 2 << CF_INDEX_SHIFT,
+ _x3 = 3 << CF_INDEX_SHIFT,
+ _x4 = 4 << CF_INDEX_SHIFT,
+ _x5 = 5 << CF_INDEX_SHIFT,
+ _x6 = 6 << CF_INDEX_SHIFT,
+ _x7 = 7 << CF_INDEX_SHIFT,
+ _x8 = 8 << CF_INDEX_SHIFT,
+ _x9 = 9 << CF_INDEX_SHIFT,
+
+ /*
+ * Simple classes
+ */
+ _xx = CC_RESERVED,
+ _ma = CC_MODIFYING_MARK_ABOVE,
+ _mp = CC_MODIFYING_MARK_POST,
+ _iv = CC_INDEPENDENT_VOWEL,
+ _ct = CC_CONSONANT | CF_CONSONANT,
+ _cn = CC_CONSONANT_WITH_NUKTA | CF_CONSONANT,
+ _nu = CC_NUKTA,
+ _dv = CC_DEPENDENT_VOWEL,
+ _dl = _dv | CF_MATRA_PRE,
+ _db = _dv | CF_MATRA_BELOW,
+ _da = _dv | CF_MATRA_ABOVE,
+ _dr = _dv | CF_MATRA_POST,
+ _lm = _dv | CF_LENGTH_MARK,
+ _vr = CC_VIRAMA,
+
+ /*
+ * Split matras
+ */
+ _s1 = _dv | _x1,
+ _s2 = _dv | _x2,
+ _s3 = _dv | _x3,
+ _s4 = _dv | _x4,
+ _s5 = _dv | _x5,
+ _s6 = _dv | _x6,
+ _s7 = _dv | _x7,
+ _s8 = _dv | _x8,
+ _s9 = _dv | _x9,
+
+ /*
+ * consonants with special forms
+ * NOTE: this assumes that no consonants with nukta have
+ * special forms... (Bengali RA?)
+ */
+ _bb = _ct | CF_BELOW_BASE,
+ _pb = _ct | CF_POST_BASE,
+ _vt = _bb | CF_VATTU,
+ _rv = _vt | CF_REPH,
+ _rp = _pb | CF_REPH,
+ _rb = _bb | CF_REPH
+};
+
+/*
+ * Character class tables
+ */
+static IndicOTCharClass devaCharClasses[] =
+{
+ _xx, _ma, _ma, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, /* 0900 - 090F */
+ _iv, _iv, _iv, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, /* 0910 - 091F */
+ _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _cn, _ct, _ct, _ct, _ct, _ct, _ct, /* 0920 - 092F */
+ _rv, _cn, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _xx, _xx, _nu, _xx, _dr, _dl, /* 0930 - 093F */
+ _dr, _db, _db, _db, _db, _da, _da, _da, _da, _dr, _dr, _dr, _dr, _vr, _xx, _xx, /* 0940 - 094F */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _cn, _cn, _cn, _cn, _cn, _cn, _cn, _cn, /* 0950 - 095F */
+ _iv, _iv, _db, _db, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0960 - 096F */
+ _xx /* 0970 */
+};
+
+static IndicOTCharClass bengCharClasses[] =
+{
+ _xx, _ma, _mp, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _xx, _iv, /* 0980 - 098F */
+ _iv, _xx, _xx, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, /* 0990 - 099F */
+ _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _xx, _ct, _ct, _bb, _ct, _ct, _pb, /* 09A0 - 09AF */
+ _rv, _xx, _ct, _xx, _xx, _xx, _ct, _ct, _ct, _ct, _xx, _xx, _nu, _xx, _dr, _dl, /* 09B0 - 09BF */
+ _dr, _db, _db, _db, _db, _xx, _xx, _dl, _dl, _xx, _xx, _s1, _s2, _vr, _xx, _xx, /* 09C0 - 09CF */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _dr, _xx, _xx, _xx, _xx, _cn, _cn, _xx, _cn, /* 09D0 - 09DF */
+ _iv, _iv, _dv, _dv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 09E0 - 09EF */
+ _ct, _ct, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx /* 09F0 - 09FA */
+};
+
+static IndicOTCharClass punjCharClasses[] =
+{
+ _xx, _xx, _ma, _xx, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _xx, _xx, _xx, _iv, /* 0A00 - 0A0F */
+ _iv, _xx, _xx, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, /* 0A10 - 0A1F */
+ _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _xx, _ct, _ct, _ct, _ct, _ct, _bb, /* 0A20 - 0A2F */
+ _vt, _xx, _ct, _ct, _xx, _bb, _ct, _xx, _ct, _bb, _xx, _xx, _nu, _xx, _dr, _dl, /* 0A30 - 0A3F */
+ _dr, _db, _db, _xx, _xx, _xx, _xx, _da, _da, _xx, _xx, _da, _da, _vr, _xx, _xx, /* 0A40 - 0A4F */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _cn, _cn, _cn, _cn, _xx, _cn, _xx, /* 0A50 - 0A5F */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0A60 - 0A6F */
+ _ma, _ma, _ct, _ct, _xx /* 0A70 - 0A74 */
+};
+
+static IndicOTCharClass gujrCharClasses[] =
+{
+ _xx, _ma, _ma, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _iv, _xx, _iv, /* 0A80 - 0A8F */
+ _iv, _iv, _xx, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, /* 0A90 - 0A9F */
+ _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _xx, _ct, _ct, _ct, _ct, _ct, _ct, /* 0AA0 - 0AAF */
+ _rv, _xx, _ct, _ct, _xx, _ct, _ct, _ct, _ct, _ct, _xx, _xx, _nu, _xx, _dr, _dl, /* 0AB0 - 0ABF */
+ _dr, _db, _db, _db, _db, _da, _xx, _da, _da, _dr, _xx, _dr, _dr, _vr, _xx, _xx, /* 0AC0 - 0ACF */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0AD0 - 0ADF */
+ _iv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx /* 0AE0 - 0AEF */
+};
+
+static IndicOTCharClass oryaCharClasses[] =
+{
+ _xx, _ma, _ma, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _xx, _iv, /* 0B00 - 0B0F */
+ _iv, _xx, _xx, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, /* 0B10 - 0B1F */
+ _ct, _ct, _ct, _ct, _bb, _ct, _ct, _ct, _bb, _xx, _ct, _ct, _bb, _bb, _bb, _pb, /* 0B20 - 0B2F */
+ _rv, _xx, _bb, _bb, _xx, _xx, _ct, _ct, _ct, _ct, _xx, _xx, _nu, _xx, _dr, _da, /* 0B30 - 0B3F */
+ _dr, _db, _db, _db, _xx, _xx, _xx, _dl, _s1, _xx, _xx, _s2, _s3, _vr, _xx, _xx, /* 0B40 - 0B4F */
+ _xx, _xx, _xx, _xx, _xx, _xx, _da, _dr, _xx, _xx, _xx, _xx, _cn, _cn, _xx, _cn, /* 0B50 - 0B5F */
+ _iv, _iv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0B60 - 0B6F */
+ _xx /* 0B70 */
+};
+
+static IndicOTCharClass tamlCharClasses[] =
+{
+ _xx, _xx, _ma, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _xx, _xx, _iv, _iv, /* 0B80 - 0B8F */
+ _iv, _xx, _iv, _iv, _iv, _ct, _xx, _xx, _xx, _ct, _ct, _xx, _ct, _xx, _ct, _ct, /* 0B90 - 0B9F */
+ _xx, _xx, _xx, _ct, _ct, _xx, _xx, _xx, _ct, _ct, _ct, _xx, _xx, _xx, _ct, _ct, /* 0BA0 - 0BAF */
+ _ct, _ct, _ct, _ct, _ct, _ct, _xx, _ct, _ct, _ct, _xx, _xx, _xx, _xx, _dr, _dr, /* 0BB0 - 0BBF */
+ _da, _dr, _dr, _xx, _xx, _xx, _dl, _dl, _dl, _xx, _s1, _s2, _s3, _vr, _xx, _xx, /* 0BC0 - 0BCF */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _dr, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0BD0 - 0BDF */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0BE0 - 0BEF */
+ _xx, _xx, _xx /* 0BF0 - 0BF2 */
+};
+
+/* FIXME: Should some of the bb's be pb's? (KA, NA, MA, YA, VA, etc. (approx 13)) */
+static IndicOTCharClass teluCharClasses[] =
+{
+ _xx, _mp, _mp, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _iv, _iv, /* 0C00 - 0C0F */
+ _iv, _xx, _iv, _iv, _iv, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, /* 0C10 - 0C1F */
+ _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _xx, _bb, _bb, _bb, _bb, _bb, _bb, /* 0C20 - 0C2F */
+ _bb, _ct, _bb, _bb, _xx, _bb, _bb, _bb, _bb, _bb, _xx, _xx, _xx, _xx, _da, _da, /* 0C30 - 0C3F */
+ _da, _dr, _dr, _dr, _dr, _xx, _da, _da, _s1, _xx, _da, _da, _da, _vr, _xx, _xx, /* 0C40 - 0C4F */
+ _xx, _xx, _xx, _xx, _xx, _da, _db, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0C50 - 0C5F */
+ _iv, _iv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx /* 0C60 - 0C6F */
+};
+
+/* FIXME: is 0CD5 a dr or an lm?? */
+static IndicOTCharClass kndaCharClasses[] =
+{
+ _xx, _xx, _mp, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _iv, /* 0C80 - 0C8F */
+ _iv, _xx, _iv, _iv, _iv, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, /* 0C90 - 0C9F */
+ _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _bb, _xx, _bb, _bb, _bb, _bb, _bb, _bb, /* 0CA0 - 0CAF */
+ _rb, _ct, _bb, _bb, _xx, _bb, _bb, _bb, _bb, _bb, _xx, _xx, _xx, _xx, _dr, _da, /* 0CB0 - 0CBF */
+ _s1, _dr, _dr, _dr, _dr, _xx, _da, _s2, _s3, _xx, _s4, _s5, _da, _vr, _xx, _xx, /* 0CC0 - 0CCF */
+ _xx, _xx, _xx, _xx, _xx, _lm, _dr, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _ct, _xx, /* 0CD0 - 0CDF */
+ _iv, _iv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx /* 0CE0 - 0CEF */
+};
+
+/*
+ * FIXME: this is correct for old-style Malayalam (MAL) but not for reformed Malayalam (MLR)
+ * FIXME: should there be a REPH for old-style Malayalam?
+ */
+static IndicOTCharClass mlymCharClasses[] =
+{
+ _xx, _xx, _mp, _mp, _xx, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _iv, _xx, _iv, _iv, /* 0D00 - 0D0F */
+ _iv, _xx, _iv, _iv, _iv, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _ct, _bb, /* 0D10 - 0D1F */
+ _ct, _ct, _ct, _bb, _ct, _bb, _bb, _ct, _ct, _xx, _ct, _ct, _ct, _ct, _ct, _ct, /* 0D20 - 0D2F */
+ _pb, _cn, _bb, _ct, _ct, _pb, _ct, _ct, _ct, _ct, _xx, _xx, _xx, _xx, _dr, _dr, /* 0D30 - 0D3F */
+ _dr, _db, _db, _db, _xx, _xx, _dl, _dl, _dl, _xx, _s1, _s2, _s3, _vr, _xx, _xx, /* 0D40 - 0D4F */
+ _xx, _xx, _xx, _xx, _xx, _xx, _xx, _dr, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, /* 0D50 - 0D5F */
+ _iv, _iv, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx /* 0D60 - 0D6F */
+};
+
+/*
+ * Split matra tables
+ */
+static const IndicOTSplitMatra bengSplitTable[] = {{0x09C7, 0x09BE}, {0x09C7, 0x09D7}};
+
+static const IndicOTSplitMatra oryaSplitTable[] = {{0x0B47, 0x0B56}, {0x0B47, 0x0B3E}, {0x0B47, 0x0B57}};
+
+static const IndicOTSplitMatra tamlSplitTable[] = {{0x0BC6, 0x0BBE}, {0x0BC7, 0x0BBE}, {0x0BC6, 0x0BD7}};
+
+static const IndicOTSplitMatra teluSplitTable[] = {{0x0C46, 0x0C56}};
+
+static const IndicOTSplitMatra kndaSplitTable[] = {{0x0CBF, 0x0CD5}, {0x0CC6, 0x0CD5}, {0x0CC6, 0x0CD6}, {0x0CC6, 0x0CC2},
+ {0x0CC6, 0x0CC2, 0x0CD5}};
+
+static const IndicOTSplitMatra mlymSplitTable[] = {{0x0D46, 0x0D3E}, {0x0D47, 0x0D3E}, {0x0D46, 0x0D57}};
+
+/*
+ * Script Flags
+ */
+
+/*
+ * FIXME: post 'GSUB' reordering of MATRA_PRE's for Malayalam and Tamil
+ * FIXME: reformed Malayalam needs to reorder VATTU to before base glyph...
+ * FIXME: eyelash RA only for Devanagari??
+ */
+#define DEVA_SCRIPT_FLAGS (SF_EYELASH_RA | SF_NO_POST_BASE_LIMIT)
+#define BENG_SCRIPT_FLAGS (SF_REPH_AFTER_BELOW | SF_NO_POST_BASE_LIMIT)
+#define PUNJ_SCRIPT_FLAGS (SF_NO_POST_BASE_LIMIT)
+#define GUJR_SCRIPT_FLAGS (SF_NO_POST_BASE_LIMIT)
+#define ORYA_SCRIPT_FLAGS (SF_REPH_AFTER_BELOW | SF_NO_POST_BASE_LIMIT)
+#define TAML_SCRIPT_FLAGS (SF_MPRE_FIXUP | SF_NO_POST_BASE_LIMIT)
+#define TELU_SCRIPT_FLAGS (SF_MATRAS_AFTER_BASE | 3)
+#define KNDA_SCRIPT_FLAGS (SF_MATRAS_AFTER_BASE | 3)
+#define MLYM_SCRIPT_FLAGS (SF_NO_POST_BASE_LIMIT)
+
+/*
+ * Indic Class Tables
+ */
+IndicOTClassTable deva_class_table = {0x0900, 0x0970, 2, DEVA_SCRIPT_FLAGS, devaCharClasses, NULL};
+
+IndicOTClassTable beng_class_table = {0x0980, 0x09FA, 3, BENG_SCRIPT_FLAGS, bengCharClasses, bengSplitTable};
+
+IndicOTClassTable punj_class_table = {0x0A00, 0x0A74, 2, PUNJ_SCRIPT_FLAGS, punjCharClasses, NULL};
+
+IndicOTClassTable gujr_class_table = {0x0A80, 0x0AEF, 2, GUJR_SCRIPT_FLAGS, gujrCharClasses, NULL};
+
+IndicOTClassTable orya_class_table = {0x0B00, 0x0B70, 3, ORYA_SCRIPT_FLAGS, oryaCharClasses, oryaSplitTable};
+
+IndicOTClassTable taml_class_table = {0x0B80, 0x0BF2, 3, TAML_SCRIPT_FLAGS, tamlCharClasses, tamlSplitTable};
+
+IndicOTClassTable telu_class_table = {0x0C00, 0x0C6F, 3, TELU_SCRIPT_FLAGS, teluCharClasses, teluSplitTable};
+
+IndicOTClassTable knda_class_table = {0x0C80, 0x0CEF, 4, KNDA_SCRIPT_FLAGS, kndaCharClasses, kndaSplitTable};
+
+IndicOTClassTable mlym_class_table = {0x0D00, 0x0D6F, 3, MLYM_SCRIPT_FLAGS, mlymCharClasses, mlymSplitTable};
+
+const IndicOTSplitMatra *indic_ot_get_split_matra(const IndicOTClassTable *class_table, IndicOTCharClass char_class)
+{
+ gint32 index = (char_class & CF_INDEX_MASK) >> CF_INDEX_SHIFT;
+
+ return &class_table->splitMatraTable[index - 1];
+}
+
+gboolean indic_ot_is_vm_above(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_VM_ABOVE(char_class);
+}
+
+gboolean indic_ot_is_vm_post(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_VM_POST(char_class);
+}
+
+gboolean indic_ot_is_consonant(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_CONSONANT(char_class);
+}
+
+gboolean indic_ot_is_reph(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_REPH(char_class);
+}
+
+gboolean indic_ot_is_virama(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_VIRAMA(char_class);
+}
+
+gboolean indic_ot_is_nukta(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_NUKTA(char_class);
+}
+
+gboolean indic_ot_is_vattu(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_VATTU(char_class);
+}
+
+gboolean indic_ot_is_matra(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_MATRA(char_class);
+}
+
+gboolean indic_ot_is_split_matra(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_SPLIT_MATRA(char_class);
+}
+
+gboolean indic_ot_is_m_pre(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_M_PRE(char_class);
+}
+
+gboolean indic_ot_is_m_below(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_M_BELOW(char_class);
+}
+
+gboolean indic_ot_is_m_above(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_M_ABOVE(char_class);
+}
+
+gboolean indic_ot_is_m_post(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_M_POST(char_class);
+}
+
+gboolean indic_ot_is_length_mark(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return IS_LENGTH_MARK(char_class);
+}
+
+gboolean indic_ot_has_post_or_below_base_form(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return HAS_POST_OR_BELOW_BASE_FORM(char_class);
+}
+
+gboolean indic_ot_has_post_base_form(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return HAS_POST_BASE_FORM(char_class);
+}
+
+gboolean indic_ot_has_below_base_form(const IndicOTClassTable *class_table, gunichar ch)
+{
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, ch);
+
+ return HAS_BELOW_BASE_FORM(char_class);
+}
+
+IndicOTCharClass indic_ot_get_char_class(const IndicOTClassTable *class_table, gunichar ch)
+{
+ if (ch == C_SIGN_ZWJ) {
+ return CF_CONSONANT | CC_ZERO_WIDTH_MARK;
+ }
+
+ if (ch == C_SIGN_ZWNJ) {
+ return CC_ZERO_WIDTH_MARK;
+ }
+
+ if (ch < class_table->firstChar || ch > class_table->lastChar) {
+ return CC_RESERVED;
+ }
+
+ return class_table->charClasses[ch - class_table->firstChar];
+}
+
+static const gint8 stateTable[][CC_COUNT] =
+{
+/* xx ma mp iv ct cn nu dv vr zw */
+ { 1, 1, 1, 5, 3, 2, 1, 1, 1, 1},
+ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, 6, 1, -1, -1, -1, -1, 5, 4, -1},
+ {-1, 6, 1, -1, -1, -1, 2, 5, 4, -1},
+ {-1, -1, -1, -1, 3, 2, -1, -1, -1, 8},
+ {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, 7, 1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, -1, 1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, 3, 2, -1, -1, -1, -1}
+
+};
+
+glong indic_ot_find_syllable(const IndicOTClassTable *class_table, const gunichar *chars, glong prev, glong char_count)
+{
+ glong cursor = prev;
+ gint8 state = 0;
+
+ while (cursor < char_count) {
+ IndicOTCharClass char_class = indic_ot_get_char_class(class_table, chars[cursor]);
+
+ state = stateTable[state][char_class & CF_CLASS_MASK];
+
+ if (state < 0) {
+ break;
+ }
+
+ cursor += 1;
+ }
+
+ return cursor;
+}
+
diff --git a/modules/indic/indic-ot.c b/modules/indic/indic-ot.c
new file mode 100644
index 00000000..faa47e4a
--- /dev/null
+++ b/modules/indic/indic-ot.c
@@ -0,0 +1,411 @@
+/* Pango
+ * indic-ot.c:
+ *
+ * Copyright (C) 2001, 2002 IBM Corporation. All Rights Reserved.
+ * Author: Eric Mader <mader@jtcsv.com>
+ *
+ */
+
+#include "indic-ot.h"
+
+/*
+ * FIXME: should the IndicOutput stuff be moved
+ * to a spereate .h and .c file just to keep the
+ * clutter down here? (it's not really usefull
+ * anyplace else, is it?)
+ */
+struct _Output
+{
+ glong fOutIndex;
+
+ const glong *fOriginalOffsets;
+
+ gunichar *fOutChars;
+ glong *fCharIndices;
+ gulong *fCharTags;
+
+ gunichar fMpre;
+ gunichar fMbelow;
+ gunichar fMabove;
+ gunichar fMpost;
+ gunichar fLengthMark;
+ glong fMatraIndex;
+ gulong fMatraTags;
+};
+
+typedef struct _Output Output;
+
+static void initOutput(Output *output, const glong *originalOffsets, gunichar *outChars, glong *charIndices, gulong *charTags)
+{
+ output->fOriginalOffsets = originalOffsets;
+
+ output->fOutChars = outChars;
+ output->fCharIndices = charIndices;
+ output->fCharTags = charTags;
+
+ output->fOutIndex = 0;
+ output->fMatraTags = 0;
+
+ output->fMpre = output->fMbelow = output->fMabove = output->fMpost = output->fLengthMark = 0;
+}
+
+static void saveMatra(Output *output, gunichar matra, IndicOTCharClass matraClass)
+{
+ /* FIXME: check if already set, or if not a matra... */
+ if (IS_M_PRE(matraClass)) {
+ output->fMpre = matra;
+ } else if (IS_M_BELOW(matraClass)) {
+ output->fMbelow = matra;
+ } else if (IS_M_ABOVE(matraClass)) {
+ output->fMabove = matra;
+ } else if (IS_M_POST(matraClass)) {
+ output->fMpost = matra;
+ } else if (IS_LENGTH_MARK(matraClass)) {
+ output->fLengthMark = matra;
+ }
+}
+
+static void noteMatra(Output *output, const IndicOTClassTable *classTable, gunichar matra, guint32 matraIndex, gulong matraTags)
+{
+ IndicOTCharClass matraClass = indic_ot_get_char_class(classTable, matra);
+
+ output->fMpre = output->fMbelow = output->fMabove = output->fMpost = output->fLengthMark = 0;
+ output->fMatraIndex = matraIndex;
+ output->fMatraTags = matraTags;
+
+ if (IS_MATRA(matraClass)) {
+ if (IS_SPLIT_MATRA(matraClass)) {
+ const IndicOTSplitMatra *splitMatra = indic_ot_get_split_matra(classTable, matraClass);
+ int i;
+
+ for (i = 0; i < 3 && (*splitMatra)[i] != 0; i += 1) {
+ gunichar piece = (*splitMatra)[i];
+ IndicOTCharClass pieceClass = indic_ot_get_char_class(classTable, piece);
+
+ saveMatra(output, piece, pieceClass);
+ }
+ } else {
+ saveMatra(output, matra, matraClass);
+ }
+ }
+}
+
+static void writeChar(Output *output, gunichar ch, guint32 charIndex, gulong charTags)
+{
+ if (output->fOutChars != NULL) {
+ output->fOutChars[output->fOutIndex] = ch;
+ output->fCharIndices[output->fOutIndex] = output->fOriginalOffsets[charIndex];
+ output->fCharTags[output->fOutIndex] = charTags;
+ }
+
+ output->fOutIndex += 1;
+}
+
+static void writeMpre(Output *output)
+{
+ if (output->fMpre != 0) {
+ writeChar(output, output->fMpre, output->fMatraIndex, output->fMatraTags);
+ }
+}
+
+static void writeMbelow(Output *output)
+{
+ if (output->fMbelow != 0) {
+ writeChar(output, output->fMbelow, output->fMatraIndex, output->fMatraTags);
+ }
+}
+
+static void writeMabove(Output *output)
+{
+ if (output->fMabove != 0) {
+ writeChar(output, output->fMabove, output->fMatraIndex, output->fMatraTags);
+ }
+}
+
+static void writeMpost(Output *output)
+{
+ if (output->fMpost != 0) {
+ writeChar(output, output->fMpost, output->fMatraIndex, output->fMatraTags);
+ }
+}
+
+static void writeLengthMark(Output *output)
+{
+ if (output->fLengthMark != 0) {
+ writeChar(output, output->fLengthMark, output->fMatraIndex, output->fMatraTags);
+ }
+}
+
+glong getOutputIndex(Output *output)
+{
+ return output->fOutIndex;
+}
+
+#define false 0
+#define true 1
+
+glong indic_ot_reorder(const gunichar *chars, const glong *utf8_offsets, glong char_count, const IndicOTClassTable *class_table, gunichar *out_chars, glong *char_indices, gulong *char_tags)
+{
+ Output output;
+ glong i, prev = 0;
+
+ initOutput(&output, utf8_offsets, out_chars, char_indices, char_tags);
+
+ while (prev < char_count) {
+ glong syllable = indic_ot_find_syllable(class_table, chars, prev, char_count);
+ glong matra, vmabove, vmpost = syllable;
+
+ while (vmpost > prev && indic_ot_is_vm_post(class_table, chars[vmpost - 1])) {
+ vmpost -= 1;
+ }
+
+ vmabove = vmpost;
+ while (vmabove > prev && indic_ot_is_vm_above(class_table, chars[vmabove - 1])) {
+ vmabove -= 1;
+ }
+
+ matra = vmabove - 1;
+ noteMatra(&output, class_table, chars[matra], /*matra*/ prev, blwf_p);
+
+ switch (indic_ot_get_char_class(class_table, chars[prev]) & CF_CLASS_MASK) {
+ case CC_RESERVED:
+ case CC_INDEPENDENT_VOWEL:
+ case CC_ZERO_WIDTH_MARK:
+ for (i = prev; i < syllable; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, blwf_p);
+ }
+
+ break;
+
+ case CC_MODIFYING_MARK_ABOVE:
+ case CC_MODIFYING_MARK_POST:
+ case CC_NUKTA:
+ case CC_VIRAMA:
+ writeChar(&output, C_DOTTED_CIRCLE, prev, blwf_p);
+ writeChar(&output, chars[prev], prev, blwf_p);
+ break;
+
+ case CC_DEPENDENT_VOWEL:
+ writeMpre(&output);
+ writeChar(&output, C_DOTTED_CIRCLE, prev, blwf_p);
+ writeMbelow(&output);
+ writeMabove(&output);
+ writeMpost(&output);
+ writeLengthMark(&output);
+ break;
+
+ case CC_CONSONANT:
+ case CC_CONSONANT_WITH_NUKTA:
+ {
+ guint32 length = vmabove - prev;
+ glong lastConsonant = vmabove - 1;
+ glong baseLimit = prev;
+ glong baseConsonant, postBase;
+
+ /* Check for REPH at front of syllable */
+ if (length > 2 && indic_ot_is_reph(class_table, chars[prev]) && indic_ot_is_virama(class_table, chars[prev + 1])) {
+ baseLimit = prev + 2;
+
+ /* Check for eyelash RA, if the script supports it */
+ if ((class_table->scriptFlags & SF_EYELASH_RA) != 0 &&
+ chars[prev + 2] == C_SIGN_ZWJ) {
+ if (length > 3) {
+ baseLimit += 1;
+ } else {
+ baseLimit = prev;
+ }
+ }
+ }
+
+ while (lastConsonant >= baseLimit && !indic_ot_is_consonant(class_table, chars[lastConsonant])) {
+ lastConsonant -= 1;
+ }
+
+ baseConsonant = lastConsonant;
+ postBase = lastConsonant + 1;
+
+ if (lastConsonant >= prev) {
+ glong postBaseLimit = class_table->scriptFlags & SF_POST_BASE_LIMIT_MASK;
+ gboolean seenVattu = false;
+ gboolean seenBelowBaseForm = false;
+ gboolean supressVattu = true;
+ glong bcSpan;
+
+ while (baseConsonant >= baseLimit) {
+ IndicOTCharClass charClass = indic_ot_get_char_class(class_table, chars[baseConsonant]);
+
+ if (IS_CONSONANT(charClass)) {
+ if (postBaseLimit == 0 || seenVattu ||
+ (baseConsonant > baseLimit && !indic_ot_is_virama(class_table, chars[baseConsonant - 1])) ||
+ !HAS_POST_OR_BELOW_BASE_FORM(charClass)) {
+ break;
+ }
+
+ seenVattu = IS_VATTU(charClass);
+
+ if (HAS_POST_BASE_FORM(charClass)) {
+ if (seenBelowBaseForm) {
+ break;
+ }
+
+ postBase = baseConsonant;
+ } else if (HAS_BELOW_BASE_FORM(charClass)) {
+ seenBelowBaseForm = true;
+ }
+
+ postBaseLimit -= 1;
+ }
+
+ baseConsonant -= 1;
+ }
+
+ if (baseConsonant < baseLimit) {
+ baseConsonant = baseLimit;
+ }
+
+ /* Write Mpre */
+ writeMpre(&output);
+
+ /* Write eyelash RA */
+ /* NOTE: baseLimit == prev + 3 iff eyelash RA present... */
+ if (baseLimit == prev + 3) {
+ writeChar(&output, chars[prev], prev, half_p);
+ writeChar(&output, chars[prev + 1], prev /*+ 1*/, half_p);
+ writeChar(&output, chars[prev + 2], prev /*+ 2*/, half_p);
+ }
+
+ /* write any pre-base consonants */
+ supressVattu = true;
+
+ for (i = baseLimit; i < baseConsonant; i += 1) {
+ gunichar ch = chars[i];
+ gulong tag = blwf_p;
+ IndicOTCharClass charClass = indic_ot_get_char_class(class_table, ch);
+
+ if (IS_CONSONANT(charClass)) {
+ if (IS_VATTU(charClass) && supressVattu) {
+ tag = nukt_p;
+ }
+
+ supressVattu = IS_VATTU(charClass);
+ } else if (IS_VIRAMA(charClass) && chars[i + 1] == C_SIGN_ZWNJ)
+ {
+ tag = nukt_p;
+ }
+
+ writeChar(&output, ch, /*i*/ prev, tag);
+ }
+
+ bcSpan = baseConsonant + 1;
+
+ if (bcSpan < vmabove && indic_ot_is_nukta(class_table, chars[bcSpan])) {
+ bcSpan += 1;
+ }
+
+ if (baseConsonant == lastConsonant && bcSpan < vmabove && indic_ot_is_virama(class_table, chars[bcSpan])) {
+ bcSpan += 1;
+
+ if (bcSpan < vmabove && chars[bcSpan] == C_SIGN_ZWNJ) {
+ bcSpan += 1;
+ }
+ }
+
+ /* write base consonant */
+ for (i = baseConsonant; i < bcSpan; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, nukt_p);
+ }
+
+ if ((class_table->scriptFlags & SF_MATRAS_AFTER_BASE) != 0) {
+ writeMbelow(&output);
+ writeMabove(&output);
+ writeMpost(&output);
+ }
+
+ /* write below-base consonants */
+ if (baseConsonant != lastConsonant) {
+ for (i = bcSpan + 1; i < postBase; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, blwf_p);
+ }
+
+ if (postBase > lastConsonant) {
+ /* write halant that was after base consonant */
+ writeChar(&output, chars[bcSpan], /*bcSpan*/ prev, blwf_p);
+ }
+ }
+
+ /* write Mbelow, Mabove */
+ if ((class_table->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) {
+ writeMbelow(&output);
+ writeMabove(&output);
+ }
+
+ if ((class_table->scriptFlags & SF_REPH_AFTER_BELOW) != 0) {
+ if (baseLimit == prev + 2) {
+ writeChar(&output, chars[prev], prev, rphf_p);
+ writeChar(&output, chars[prev + 1], prev /*+ 1*/, rphf_p);
+ }
+
+ /* write VMabove */
+ for (i = vmabove; i < vmpost; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, blwf_p);
+ }
+ }
+
+ /* write post-base consonants */
+ /* FIXME: does this put the right tags on post-base consonants? */
+ if (baseConsonant != lastConsonant) {
+ if (postBase <= lastConsonant) {
+ for (i = postBase; i <= lastConsonant; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, nukt_p);
+ }
+
+ /* write halant that was after base consonant */
+ writeChar(&output, chars[bcSpan], /*bcSpan*/ prev, blwf_p);
+ }
+
+ /* write the training halant, if there is one */
+ if (lastConsonant < matra && indic_ot_is_virama(class_table, chars[matra])) {
+ writeChar(&output, chars[matra], /*matra*/ prev, nukt_p);
+ }
+ }
+
+ /* write Mpost */
+ if ((class_table->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) {
+ writeMpost(&output);
+ }
+
+ writeLengthMark(&output);
+
+ /* write reph */
+ if ((class_table->scriptFlags & SF_REPH_AFTER_BELOW) == 0) {
+ if (baseLimit == prev + 2) {
+ writeChar(&output, chars[prev], prev, rphf_p);
+ writeChar(&output, chars[prev + 1], prev /*+ 1*/, rphf_p);
+ }
+
+ /* write VMabove */
+ for (i = vmabove; i < vmpost; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, blwf_p);
+ }
+ }
+
+ /* write VMpost */
+ for (i = vmpost; i < syllable; i += 1) {
+ writeChar(&output, chars[i], /*i*/ prev, blwf_p);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+
+ prev = syllable;
+ }
+
+ return getOutputIndex(&output);
+}
+
diff --git a/modules/indic/indic-ot.h b/modules/indic/indic-ot.h
new file mode 100644
index 00000000..57e2bbac
--- /dev/null
+++ b/modules/indic/indic-ot.h
@@ -0,0 +1,250 @@
+/* Pango
+ * indic-ot.h:
+ *
+ * Copyright (C) 2001, 2002 IBM Corporation. All Rights Reserved.
+ * Author: Eric Mader <mader@jtcsv.com>
+ *
+ */
+
+#ifndef __INDIC_OT_H__
+#define __INDIC_OT_H__
+
+#include <freetype/freetype.h>
+#include <pango/pango-glyph.h>
+#include <pango/pango-types.h>
+
+G_BEGIN_DECLS
+
+#ifdef PANGO_ENABLE_ENGINE
+
+/* Characters that get refered to by name... */
+enum
+{
+ C_SIGN_ZWNJ = 0x200C,
+ C_SIGN_ZWJ = 0x200D,
+ C_DOTTED_CIRCLE = 0x25CC
+};
+
+/*
+ * The characters that a split matra splits into.
+ * Unused characters will be zero.
+ */
+typedef gunichar IndicOTSplitMatra[3];
+
+/*
+ * Character class values
+ */
+/* FIXME: does this need to be a typedef? */
+typedef enum
+{
+ CC_RESERVED = 0,
+ CC_MODIFYING_MARK_ABOVE = 1,
+ CC_MODIFYING_MARK_POST = 2,
+ CC_INDEPENDENT_VOWEL = 3,
+ CC_CONSONANT = 4,
+ CC_CONSONANT_WITH_NUKTA = 5,
+ CC_NUKTA = 6,
+ CC_DEPENDENT_VOWEL = 7,
+ CC_VIRAMA = 8,
+ CC_ZERO_WIDTH_MARK = 9,
+ CC_COUNT = 10
+} IndicOTCharClassValues;
+
+/*
+ * Character class flags
+ */
+/* FIXME: does this need to be a typedef? */
+typedef enum
+{
+ CF_CLASS_MASK = 0x0000FFFF,
+
+ CF_CONSONANT = 0x80000000,
+
+ CF_REPH = 0x40000000,
+ CF_VATTU = 0x20000000,
+ CF_BELOW_BASE = 0x10000000,
+ CF_POST_BASE = 0x08000000,
+
+ CF_MATRA_PRE = 0x04000000,
+ CF_MATRA_BELOW = 0x02000000,
+ CF_MATRA_ABOVE = 0x01000000,
+ CF_MATRA_POST = 0x00800000,
+ CF_LENGTH_MARK = 0x00400000,
+ CF_INDEX_MASK = 0x000F0000,
+ CF_INDEX_SHIFT = 16
+} IndicOTCharClassFlags;
+
+/*
+ * Character class: a character class value
+ * ORed with character class flags.
+ */
+typedef glong IndicOTCharClass;
+
+/*
+ * Script flags
+ */
+typedef enum
+{
+ SF_MATRAS_AFTER_BASE = 0x80000000,
+ SF_REPH_AFTER_BELOW = 0x40000000,
+ SF_EYELASH_RA = 0x20000000,
+ SF_MPRE_FIXUP = 0x10000000,
+
+ SF_POST_BASE_LIMIT_MASK = 0x0000FFFF,
+ SF_NO_POST_BASE_LIMIT = 0x00007FFF
+} IndicOTScriptFlags;
+
+/*
+ * Bit flags for the indic feature tags
+ */
+enum indic_glyph_feature_
+{
+ nukt = 0x0001,
+ akhn = 0x0002,
+ rphf = 0x0004,
+ blwf = 0x0008,
+ half = 0x0010,
+ pstf = 0x0020,
+ vatu = 0x0040,
+ pres = 0x0080,
+ blws = 0x0100,
+ abvs = 0x0200,
+ psts = 0x0400,
+ haln = 0x0800,
+ blwm = 0x1000,
+ abvm = 0x2000,
+ dist = 0x4000,
+ junk = 0x8000
+};
+
+/*
+ * Complement of the feature flags that
+ * will be assigned to specific glyphs.
+ *
+ * The names come from the ICU implementation,
+ * which listed the actual tags in an order
+ * such that tags could be assigned using the
+ * address of the first one: &tags[0], &tags[1],
+ * &tags[2], &tags[3]. The name of each set here
+ * is the name of the first tag in the ICU list.
+ */
+enum indic_glyph_property_
+{
+ rphf_p = (junk | dist),
+ blwf_p = (junk | dist | rphf),
+ half_p = (junk | dist | rphf | blwf),
+ nukt_p = (junk | dist | rphf | blwf | half)
+};
+
+/*
+ * Per-script character ranges
+ */
+#define SCRIPT_RANGE_deva 0x0900, 0x0970
+
+#define SCRIPT_RANGE_beng 0x0980, 0x09FA
+
+#define SCRIPT_RANGE_punj 0x0A00, 0x0A74
+
+#define SCRIPT_RANGE_gujr 0x0A80, 0x0AEF
+
+#define SCRIPT_RANGE_orya 0x0B00, 0x0B70
+
+#define SCRIPT_RANGE_taml 0x0B80, 0x0BF2
+
+#define SCRIPT_RANGE_telu 0x0C00, 0x0C6F
+
+#define SCRIPT_RANGE_knda 0x0C80, 0x0CEF
+
+#define SCRIPT_RANGE_mlym 0x0D00, 0x0D6F
+
+/*
+ * Macros to test the charClass flags for various things.
+ */
+#define IS_VM_ABOVE(charClass) ((charClass & CF_CLASS_MASK) == CC_MODIFYING_MARK_ABOVE)
+
+#define IS_VM_POST(charClass) ((charClass & CF_CLASS_MASK) == CC_MODIFYING_MARK_POST)
+
+#define IS_CONSONANT(charClass) ((charClass & CF_CONSONANT) != 0)
+
+#define IS_REPH(charClass) ((charClass & CF_REPH) != 0)
+
+#define IS_NUKTA(charClass) ((charClass & CF_CLASS_MASK) == CC_NUKTA)
+
+#define IS_VIRAMA(charClass) ((charClass & CF_CLASS_MASK) == CC_VIRAMA)
+
+#define IS_VATTU(charClass) ((charClass & CF_VATTU) != 0)
+
+#define IS_MATRA(charClass) ((charClass & CF_CLASS_MASK) == CC_DEPENDENT_VOWEL)
+
+#define IS_SPLIT_MATRA(charClass) ((charClass & CF_INDEX_MASK) != 0)
+
+#define IS_M_PRE(charClass) ((charClass & CF_MATRA_PRE) != 0)
+
+#define IS_M_BELOW(charClass) ((charClass & CF_MATRA_BELOW) != 0)
+
+#define IS_M_ABOVE(charClass) ((charClass & CF_MATRA_ABOVE) != 0)
+
+#define IS_M_POST(charClass) ((charClass & CF_MATRA_POST) != 0)
+
+#define IS_LENGTH_MARK(charClass) ((charClass & CF_LENGTH_MARK) != 0)
+
+#define HAS_POST_OR_BELOW_BASE_FORM(charClass) ((charClass & (CF_POST_BASE | CF_BELOW_BASE)) != 0)
+
+#define HAS_POST_BASE_FORM(charClass) ((charClass & CF_POST_BASE) != 0)
+
+#define HAS_BELOW_BASE_FORM(charClass) ((charClass & CF_BELOW_BASE) != 0)
+
+struct _IndicOTClassTable
+{
+ gunichar firstChar;
+ gunichar lastChar;
+ glong worstCaseExpansion;
+ IndicOTScriptFlags scriptFlags;
+ IndicOTCharClass *charClasses;
+
+ const IndicOTSplitMatra *splitMatraTable;
+};
+
+typedef struct _IndicOTClassTable IndicOTClassTable;
+
+extern IndicOTClassTable deva_class_table;
+extern IndicOTClassTable beng_class_table;
+extern IndicOTClassTable punj_class_table;
+extern IndicOTClassTable gujr_class_table;
+extern IndicOTClassTable orya_class_table;
+extern IndicOTClassTable taml_class_table;
+extern IndicOTClassTable telu_class_table;
+extern IndicOTClassTable knda_class_table;
+extern IndicOTClassTable mlym_class_table;
+
+const IndicOTSplitMatra *indic_ot_get_split_matra(const IndicOTClassTable *class_table, IndicOTCharClass char_class);
+
+IndicOTCharClass indic_ot_get_char_class(const IndicOTClassTable *class_table, gunichar ch);
+
+gboolean indic_ot_is_vm_above(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_vm_post(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_consonant(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_reph(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_virama(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_nukta(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_vattu(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_matra(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_split_matra(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_m_pre(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_m_below(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_m_above(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_m_post(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_is_length_mark(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_has_post_or_below_base_form(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_has_post_base_form(const IndicOTClassTable *class_table, gunichar ch);
+gboolean indic_ot_has_below_base_form(const IndicOTClassTable *class_table, gunichar ch);
+
+glong indic_ot_find_syllable(const IndicOTClassTable *class_table, const gunichar *chars, glong prev, glong char_count);
+
+glong indic_ot_reorder(const gunichar *chars, const glong *utf8_offsets, glong char_count, const IndicOTClassTable *class_table, gunichar *out_chars, glong *char_indices, gulong *char_tags);
+
+#endif /* PANGO_ENABLE_ENGINE */
+
+G_END_DECLS
+
+#endif /* __INDIC_OT_H__ */
diff --git a/modules/indic/indic-xft.c b/modules/indic/indic-xft.c
new file mode 100644
index 00000000..c3d55f80
--- /dev/null
+++ b/modules/indic/indic-xft.c
@@ -0,0 +1,444 @@
+/* Pango
+ * indic-xft.c:
+ *
+ * Copyright (C) 2001, 2002 IBM Corporation
+ * Author: Eric Mader <mader@jtcsv.com>
+ * Based on arabic-xft.c by Owen Taylor <otaylor@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "indic-ot.h"
+
+#include "pangoxft.h"
+#include "pango-engine.h"
+#include "pango-utils.h"
+
+typedef struct _PangoEngineShapeIndic PangoEngineShapeIndic;
+typedef struct _PangoIndicInfo PangoIndicInfo;
+
+struct _PangoEngineShapeIndic
+{
+ PangoEngineShape shapeEngine;
+ PangoIndicInfo *indicInfo;
+};
+
+struct _PangoIndicInfo
+{
+ PangoOTTag scriptTag;
+ IndicOTClassTable *classTable;
+ gchar *gsubQuarkName;
+ gchar *gposQuarkName;
+};
+
+#define INDIC_ENGINE_INFO(script) {#script "ScriptEngineXft", PANGO_ENGINE_TYPE_SHAPE, PANGO_RENDER_TYPE_XFT, script##_ranges, G_N_ELEMENTS(script##_ranges)}
+
+#define PANGO_INDIC_INFO(script) {OT_TAG_##script, &script##_class_table, "pango-indic-" #script "-GSUB-ruleset", "pango-indic-" #script "-GPOS-rulsest"}
+
+#define INDIC_SCRIPT_RANGE(script) {SCRIPT_RANGE_##script, "*"}
+
+#define OT_TAG_deva FT_MAKE_TAG('d','e','v','a')
+#define OT_TAG_beng FT_MAKE_TAG('b','e','n','g')
+#define OT_TAG_punj FT_MAKE_TAG('p','u','n','j')
+#define OT_TAG_gujr FT_MAKE_TAG('g','u','j','r')
+#define OT_TAG_orya FT_MAKE_TAG('o','r','y','a')
+#define OT_TAG_taml FT_MAKE_TAG('t','a','m','l')
+#define OT_TAG_telu FT_MAKE_TAG('t','e','l','u')
+#define OT_TAG_knda FT_MAKE_TAG('k','n','d','a')
+#define OT_TAG_mlym FT_MAKE_TAG('m','l','y','m')
+
+static PangoEngineRange deva_ranges[] = {
+ INDIC_SCRIPT_RANGE(deva), /* Devanagari */
+};
+
+static PangoEngineRange beng_ranges[] = {
+ INDIC_SCRIPT_RANGE(beng), /* Bengali */
+};
+
+static PangoEngineRange punj_ranges[] = {
+ INDIC_SCRIPT_RANGE(punj), /* Punjabi */
+};
+
+static PangoEngineRange gujr_ranges[] = {
+ INDIC_SCRIPT_RANGE(gujr), /* Gujarati */
+};
+
+static PangoEngineRange orya_ranges[] = {
+ INDIC_SCRIPT_RANGE(orya), /* Oriya */
+};
+
+static PangoEngineRange taml_ranges[] = {
+ INDIC_SCRIPT_RANGE(taml), /* Tamil */
+};
+
+static PangoEngineRange telu_ranges[] = {
+ INDIC_SCRIPT_RANGE(telu), /* Telugu */
+};
+
+static PangoEngineRange knda_ranges[] = {
+ INDIC_SCRIPT_RANGE(knda), /* Kannada */
+};
+
+static PangoEngineRange mlym_ranges[] = {
+ INDIC_SCRIPT_RANGE(mlym), /* Malayalam */
+};
+
+static PangoEngineInfo script_engines[] = {
+ INDIC_ENGINE_INFO(deva), INDIC_ENGINE_INFO(beng), INDIC_ENGINE_INFO(punj),
+ INDIC_ENGINE_INFO(gujr), INDIC_ENGINE_INFO(orya), INDIC_ENGINE_INFO(taml),
+ INDIC_ENGINE_INFO(telu), INDIC_ENGINE_INFO(knda), INDIC_ENGINE_INFO(mlym)
+};
+
+/*
+ * WARNING: These entries need to be in the same order as the entries
+ * in script_engines[].
+ *
+ * FIXME: remove this requirement, either by encapsulating the order
+ * in a macro that calls a body macro that can be redefined, or by
+ * putting the pointers to the PangoEngineInfo in PangoIndicInfo...
+ */
+static PangoIndicInfo indic_info[] = {
+ PANGO_INDIC_INFO(deva), PANGO_INDIC_INFO(beng), PANGO_INDIC_INFO(punj),
+ PANGO_INDIC_INFO(gujr), PANGO_INDIC_INFO(orya), PANGO_INDIC_INFO(taml),
+ PANGO_INDIC_INFO(telu), PANGO_INDIC_INFO(knda), PANGO_INDIC_INFO(mlym)
+};
+
+void
+maybe_add_GSUB_feature (PangoOTRuleset *ruleset,
+ PangoOTInfo *info,
+ guint script_index,
+ PangoOTTag feature_tag,
+ gulong property_bit)
+{
+ guint feature_index;
+
+ /* 0xffff == default language system */
+ if (pango_ot_info_find_feature (info, PANGO_OT_TABLE_GSUB,
+ feature_tag, script_index, 0xffff, &feature_index))
+ {
+ /*
+ printf("Added GSUB feature '%c%c%c%c' = %8.8X\n", feature_tag>>24, feature_tag>>16&0xFF, feature_tag>>8&0xFF, feature_tag&0xFF, property_bit);
+ */
+
+ pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GSUB, feature_index,
+ property_bit);
+ }
+}
+
+void maybe_add_GPOS_feature (PangoOTRuleset *ruleset,
+ PangoOTInfo *info,
+ guint script_index,
+ PangoOTTag feature_tag,
+ gulong property_bit)
+{
+ guint feature_index;
+
+ if (pango_ot_info_find_feature (info, PANGO_OT_TABLE_GPOS,
+ feature_tag,script_index, 0xffff, &feature_index))
+ {
+ /*
+ printf("Added GPOS feature '%c%c%c%c' = %8.8X\n", feature_tag>>24, feature_tag>>16&0xFF, feature_tag>>8&0xFF, feature_tag&0xFF, property_bit);
+ */
+
+ pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GPOS, feature_index,
+ property_bit);
+ }
+}
+
+static PangoOTRuleset *
+get_gsub_ruleset (PangoFont *font, PangoIndicInfo *indic_info)
+{
+ PangoOTInfo *info = pango_xft_font_get_ot_info (font);
+ GQuark ruleset_quark = g_quark_from_string (indic_info->gsubQuarkName);
+ PangoOTRuleset *ruleset;
+
+ if (!info)
+ return NULL;
+
+ ruleset = g_object_get_qdata (G_OBJECT (font), ruleset_quark);
+
+ if (!ruleset)
+ {
+ guint script_index;
+
+ ruleset = pango_ot_ruleset_new (info);
+
+ if (pango_ot_info_find_script (info, PANGO_OT_TABLE_GSUB,
+ indic_info->scriptTag, &script_index))
+ {
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('n','u','k','t'), nukt);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','k','h','n'), akhn);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('r','p','h','f'), rphf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','f'), blwf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('h','a','l','f'), half);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','s','t','f'), pstf);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('v','a','t','u'), vatu);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','r','e','s'), pres);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','s'), blws);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','b','v','s'), abvs);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('p','s','t','s'), psts);
+ maybe_add_GSUB_feature (ruleset, info, script_index, FT_MAKE_TAG ('h','a','l','n'), haln);
+ }
+
+ g_object_set_qdata_full (G_OBJECT (font), ruleset_quark, ruleset,
+ (GDestroyNotify)g_object_unref);
+ }
+
+ return ruleset;
+}
+
+
+static PangoOTRuleset *
+get_gpos_ruleset (PangoFont *font, PangoIndicInfo *indic_info)
+{
+ PangoOTInfo *info = pango_xft_font_get_ot_info (font);
+ GQuark ruleset_quark = g_quark_from_string (indic_info->gposQuarkName);
+ PangoOTRuleset *ruleset;
+
+ if (!info)
+ return NULL;
+
+ ruleset = g_object_get_qdata (G_OBJECT (font), ruleset_quark);
+
+ if (!ruleset)
+ {
+ guint script_index;
+
+ ruleset = pango_ot_ruleset_new (info);
+
+ if (1 && pango_ot_info_find_script (info, PANGO_OT_TABLE_GPOS,
+ indic_info->scriptTag, &script_index))
+ {
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('b','l','w','m'), blwm);
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('a','b','v','m'), abvm);
+ maybe_add_GPOS_feature (ruleset, info, script_index, FT_MAKE_TAG ('d','i','s','t'), dist);
+ }
+
+ g_object_set_qdata_full (G_OBJECT (font), ruleset_quark, ruleset,
+ (GDestroyNotify)g_object_unref);
+ }
+
+ return ruleset;
+}
+static void
+set_glyphs (PangoFont *font, const gunichar *wcs, const glong *indices, glong n_glyphs, PangoGlyphString *glyphs)
+{
+ FT_Face face = pango_xft_font_get_face (font);
+ gint i;
+
+ g_assert (face);
+
+ pango_glyph_string_set_size (glyphs, n_glyphs);
+
+ for (i = 0; i < n_glyphs; i += 1)
+ {
+ PangoGlyph glyph = FT_Get_Char_Index (face, wcs[i]);
+
+ glyphs->glyphs[i].glyph = glyph;
+ glyphs->log_clusters[i] = indices[i];
+ }
+}
+
+/*
+ * FIXME: should this check for null pointers, etc.?
+ */
+static gunichar *
+expand_text(const gchar *text, glong length, glong **offsets, glong *n_chars)
+{
+ const gchar *p;
+ gunichar *wcs, *wco;
+ glong i, *oo;
+
+ *n_chars = g_utf8_strlen (text, length);
+ wcs = g_new (gunichar, *n_chars);
+ *offsets = g_new (glong, *n_chars + 1);
+
+ p = text;
+ wco = wcs;
+ oo = *offsets;
+ for (i = 0; i < *n_chars; i += 1)
+ {
+ *wco++ = g_utf8_get_char (p);
+ *oo++ = p - text;
+
+ p = g_utf8_next_char (p);
+ }
+
+ *oo = p - text;
+
+ return wcs;
+}
+
+
+/* analysis->shape_engine has the PangoEngine... */
+static void
+indic_engine_shape (PangoFont *font,
+ const char *text,
+ gint length,
+ PangoAnalysis *analysis,
+ PangoGlyphString *glyphs)
+{
+ glong i, n_chars, n_glyphs;
+ gulong *tags = NULL;
+ gunichar *wc_in = NULL, *wc_out = NULL;
+ glong *utf8_offsets = NULL;
+ glong *indices = NULL;
+ FT_Face face;
+ PangoOTRuleset *gsub_ruleset = NULL, *gpos_ruleset = NULL;
+ PangoEngineShapeIndic *indic_shape_engine = NULL;
+ PangoIndicInfo *indic_info = NULL;
+
+ g_return_if_fail (font != NULL);
+ g_return_if_fail (text != NULL);
+ g_return_if_fail (length >= 0);
+ g_return_if_fail (analysis != NULL);
+
+ face = pango_xft_font_get_face (font);
+ g_assert (face != NULL);
+
+ indic_shape_engine = (PangoEngineShapeIndic *) analysis->shape_engine;
+
+#if 1
+ g_assert (indic_shape_engine->shapeEngine.engine.length == sizeof (PangoEngineShapeIndic));
+#endif
+
+ indic_info = indic_shape_engine->indicInfo;
+
+ wc_in = expand_text (text, length, &utf8_offsets, &n_chars);
+ n_glyphs = indic_ot_reorder (wc_in, utf8_offsets, n_chars, indic_info->classTable, NULL, NULL, NULL);
+
+ wc_out = g_new (gunichar, n_glyphs);
+ indices = g_new (glong, n_glyphs);
+ tags = g_new (gulong, n_glyphs);
+
+ n_glyphs = indic_ot_reorder (wc_in, utf8_offsets, n_chars, indic_info->classTable, wc_out, indices, tags);
+
+ pango_glyph_string_set_size (glyphs, n_glyphs);
+ set_glyphs(font, wc_out, indices, n_glyphs, glyphs);
+
+ /* do gsub processing */
+ gsub_ruleset = get_gsub_ruleset (font, indic_info);
+ if (gsub_ruleset != NULL)
+ {
+ pango_ot_ruleset_shape (gsub_ruleset, glyphs, tags);
+ }
+
+ /* apply default positioning */
+ for (i = 0; i < glyphs->num_glyphs; i += 1)
+ {
+ if (glyphs->glyphs[i].glyph != 0)
+ {
+ PangoRectangle logical_rect;
+
+ pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
+ glyphs->glyphs[i].geometry.width = logical_rect.width;
+ }
+ else
+ {
+ glyphs->glyphs[i].geometry.width = 0;
+ }
+
+ glyphs->glyphs[i].geometry.x_offset = 0;
+ glyphs->glyphs[i].geometry.y_offset = 0;
+ }
+
+#if 1
+ /* do gpos processing */
+ gpos_ruleset = get_gpos_ruleset (font, indic_info);
+ if (gpos_ruleset != NULL)
+ {
+ pango_ot_ruleset_shape (gpos_ruleset, glyphs, tags);
+ }
+#endif
+
+ g_free (tags);
+ g_free (indices);
+ g_free (wc_out);
+ g_free (wc_in);
+ g_free (utf8_offsets);
+}
+
+static PangoCoverage *
+indic_engine_get_coverage (PangoFont *font,
+ PangoLanguage *lang)
+{
+ return pango_font_get_coverage (font, lang);
+}
+
+static PangoEngine *
+indic_engine_xft_new (gint index)
+{
+ PangoEngineShapeIndic *result;
+
+ result = g_new (PangoEngineShapeIndic, 1);
+
+ result->shapeEngine.engine.id = script_engines[index].id;
+ result->shapeEngine.engine.type = PANGO_ENGINE_TYPE_SHAPE;
+ result->shapeEngine.engine.length = sizeof (*result);
+ result->shapeEngine.script_shape = indic_engine_shape;
+ result->shapeEngine.get_coverage = indic_engine_get_coverage;
+
+ result->indicInfo = &indic_info[index];
+
+ return (PangoEngine *)result;
+}
+
+/* The following three functions provide the public module API for
+ * Pango. If we are compiling it as a module, then we name the
+ * entry points script_engine_list, etc. But if we are compiling
+ * it for inclusion directly in Pango, then we need them to
+ * to have distinct names for this module, so we prepend
+ * _pango_indic_xft_
+ */
+#ifdef XFT_MODULE_PREFIX
+#define MODULE_ENTRY(func) _pango_indic_xft_##func
+#else
+#define MODULE_ENTRY(func) func
+#endif
+
+/* List the engines contained within this module
+ */
+void
+MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, gint *n_engines)
+{
+ *engines = script_engines;
+ *n_engines = G_N_ELEMENTS (script_engines);
+}
+
+/* Load a particular engine given the ID for the engine
+ */
+PangoEngine *
+MODULE_ENTRY(script_engine_load) (const char *id)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(script_engines); i += 1)
+ {
+ if (!strcmp(id, script_engines[i].id))
+ {
+ return indic_engine_xft_new(i);
+ }
+ }
+
+ return NULL;
+}
+
+void
+MODULE_ENTRY(script_engine_unload) (PangoEngine *engine)
+{
+}