diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | ChangeLog.pre-1-10 | 9 | ||||
-rw-r--r-- | ChangeLog.pre-1-2 | 9 | ||||
-rw-r--r-- | ChangeLog.pre-1-4 | 9 | ||||
-rw-r--r-- | ChangeLog.pre-1-6 | 9 | ||||
-rw-r--r-- | ChangeLog.pre-1-8 | 9 | ||||
-rw-r--r-- | configure.in | 3 | ||||
-rw-r--r-- | modules/indic/Makefile.am | 35 | ||||
-rw-r--r-- | modules/indic/indic-fc.c | 444 | ||||
-rw-r--r-- | modules/indic/indic-ot-class-tables.c | 416 | ||||
-rw-r--r-- | modules/indic/indic-ot.c | 411 | ||||
-rw-r--r-- | modules/indic/indic-ot.h | 250 | ||||
-rw-r--r-- | modules/indic/indic-xft.c | 444 | ||||
-rw-r--r-- | pango/opentype/ftxgdef.c | 10 | ||||
-rw-r--r-- | pango/opentype/ftxgpos.c | 33 | ||||
-rw-r--r-- | pango/opentype/pango-ot-ruleset.c | 35 |
16 files changed, 2109 insertions, 26 deletions
@@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/ChangeLog.pre-1-10 b/ChangeLog.pre-1-10 index bef0c4fb..1d25ec61 100644 --- a/ChangeLog.pre-1-10 +++ b/ChangeLog.pre-1-10 @@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/ChangeLog.pre-1-2 b/ChangeLog.pre-1-2 index bef0c4fb..1d25ec61 100644 --- a/ChangeLog.pre-1-2 +++ b/ChangeLog.pre-1-2 @@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/ChangeLog.pre-1-4 b/ChangeLog.pre-1-4 index bef0c4fb..1d25ec61 100644 --- a/ChangeLog.pre-1-4 +++ b/ChangeLog.pre-1-4 @@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/ChangeLog.pre-1-6 b/ChangeLog.pre-1-6 index bef0c4fb..1d25ec61 100644 --- a/ChangeLog.pre-1-6 +++ b/ChangeLog.pre-1-6 @@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/ChangeLog.pre-1-8 b/ChangeLog.pre-1-8 index bef0c4fb..1d25ec61 100644 --- a/ChangeLog.pre-1-8 +++ b/ChangeLog.pre-1-8 @@ -1,3 +1,12 @@ +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 + 2002-05-02 Matthias Clasen <maclas@gmx.de> * docs/Makefile.am (GTKDOC_CFLAGS, GTKDOC_LIBS): Set these. diff --git a/configure.in b/configure.in index 1fa4aac2..1fba2905 100644 --- a/configure.in +++ b/configure.in @@ -344,7 +344,7 @@ arabic_modules="arabic-ft2,arabic-x,arabic-xft" basic_modules="basic-ft2,basic-win32,basic-x,basic-xft" hangul_modules="hangul-x" hebrew_modules="hebrew-ft2,hebrew-x,hebrew-xft" -indic_modules="bengali-x,devanagari-x,gurmukhi-x,gujarati-x,myanmar-x" +indic_modules="bengali-x,devanagari-x,gurmukhi-x,gujarati-x,myanmar-x,indic-xft" tamil_modules="tamil-x,tamil-xft" thai_modules="thai-x" @@ -421,6 +421,7 @@ AM_CONDITIONAL(INCLUDE_TAMIL_XFT,echo $included_modules | grep '\(^\|,\)tamil-xf AM_CONDITIONAL(INCLUDE_THAI_X,echo $included_modules | grep '\(^\|,\)thai-x\($\|,\)' > /dev/null) +AM_CONDITIONAL(INCLUDE_INDIC_XFT,echo $included_modules | grep '\(^\|,\)indic-xft\($\|,\)' > /dev/null) # # We use flockfile to implement pango_getline() - should be moved to GLib 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) +{ +} diff --git a/pango/opentype/ftxgdef.c b/pango/opentype/ftxgdef.c index 6456f18e..e3aaf92b 100644 --- a/pango/opentype/ftxgdef.c +++ b/pango/opentype/ftxgdef.c @@ -230,9 +230,15 @@ /* OpenType 1.2 has introduced the `MarkAttachClassDef' field. We first have to scan the LookupFlag values to find out whether we - must load it or not. Here we only store the current file offset. */ + must load it or not. Here we only store the offset of the table. */ + + new_offset = GET_UShort(); + + if ( new_offset ) + gdef->MarkAttachClassDef_offset = new_offset + base_offset; + else + gdef->MarkAttachClassDef_offset = 0; - gdef->MarkAttachClassDef_offset = FILE_Pos(); gdef->MarkAttachClassDef.loaded = FALSE; gdef->LastGlyph = 0; diff --git a/pango/opentype/ftxgpos.c b/pango/opentype/ftxgpos.c index 012e82d8..f9ba1679 100644 --- a/pango/opentype/ftxgpos.c +++ b/pango/opentype/ftxgpos.c @@ -267,19 +267,6 @@ if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS ) { if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) || - ACCESS_Frame( 2L ) ) - goto Fail1; - - new_offset = GET_UShort(); - - FORGET_Frame(); - - if ( !new_offset ) - return TTO_Err_Invalid_GDEF_SubTable; - - new_offset += base_offset; - - if ( FILE_Seek( new_offset ) || ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef, 256, stream ) ) != TT_Err_Ok ) goto Fail1; @@ -2409,6 +2396,7 @@ &x_mark_value, &y_mark_value ); if ( error ) return error; + error = Get_Anchor( gpi, base_anchor, in->string[j], &x_base_value, &y_base_value ); if ( error ) @@ -6116,7 +6104,7 @@ TTO_GSUB_String* in, TTO_GPOS_Data* out ) { - FT_Error error = TTO_Err_Not_Covered; + FT_Error error, retError = TTO_Err_Not_Covered; TTO_GPOSHeader* gpos = gpi->gpos; FT_UShort* properties = gpos->LookupList.Properties; @@ -6158,9 +6146,11 @@ if ( error == TTO_Err_Not_Covered ) (in->pos)++; + else + retError = error; } - return error; + return retError; } @@ -6253,7 +6243,7 @@ FT_Bool r2l ) { FT_Memory memory = gpos->memory; - FT_Error error = TTO_Err_Not_Covered; + FT_Error error, retError = TTO_Err_Not_Covered; GPOS_Instance gpi; FT_UShort j; @@ -6282,11 +6272,16 @@ if ( !properties || properties[j] ) { error = Do_String_Lookup( &gpi, j, in, *out ); - if ( error && error != TTO_Err_Not_Covered ) - return error; + if ( error ) + { + if ( error != TTO_Err_Not_Covered ) + return error; + } + else + retError = error; } - return error; + return retError; } /* END */ diff --git a/pango/opentype/pango-ot-ruleset.c b/pango/opentype/pango-ot-ruleset.c index 666e6d0b..d392cc84 100644 --- a/pango/opentype/pango-ot-ruleset.c +++ b/pango/opentype/pango-ot-ruleset.c @@ -24,6 +24,9 @@ #include <pango/pango-ot.h> #include "pango-ot-private.h" +#define PANGO_SCALE_26_6 (PANGO_SCALE / (1<<6)) +#define PANGO_UNITS_26_6(d) (PANGO_SCALE_26_6 * (d)) + typedef struct _PangoOTRule PangoOTRule; struct _PangoOTRule @@ -233,7 +236,37 @@ pango_ot_ruleset_shape (PangoOTRuleset *ruleset, } else result_string = in_string; - + + if (gpos) + { + TTO_GPOS_Data *outgpos = NULL; + + if (!TT_GPOS_Apply_String (ruleset->info->face, gpos, 0, result_string, &outgpos, + FALSE /* enable device-dependant values */, + FALSE /* Even though this might be r2l text, RTL is handled elsewhere */)) + { + for (i = 0; i < result_string->length; i++) + { + int j; + + glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6 (outgpos[i].x_pos); + glyphs->glyphs[i].geometry.y_offset += PANGO_UNITS_26_6 (outgpos[i].y_pos); + + for (j = i - outgpos[i].back; j < i; j++) + glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width; + + if (outgpos[i].new_advance) + /* Can't set new x offset for marks, so just make sure not to increase it. + Can do better than this by playing with ->x_offset. */ + glyphs->glyphs[i].geometry.width = 0; + else + glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(outgpos[i].x_advance); + } + + FT_Free(gpos->memory, (void *)outgpos); + } + } + pango_glyph_string_set_size (glyphs, result_string->length); last_cluster = -1; |