Thai Pango Engine with Wtt cell-clustering and Thai MAC/WIN font layout



Hi Owen, Robert, Pablo, K.Theppitak,

K.Theppitak and I worked together to have this wtt cell-clustering and
supporting Thai MAC/WIN font (tis620-1, tis620-2).

I did tested it with only "gtklabel" for tis620-2 (Thai WIN) on Solaris
and attached are the snapshot and updated thai.c file (the change is 
in -DWTT_CLUSTERING).

I haven't tested it with xtis620.2529-1 yet.

I'll keep testing it with all the gtk1.3 samples and other platforms/fonts.

Please let us know any more comment and anything else we need to do so that
we can have this one integrated into the workspace.

Thanks,

Chookij V.
/* pANGO
 * thai.c:
 *
 * Copyright (C) 1999 Red Hat Software
 *
 * 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 <iconv.h>

#include <glib.h>
#include "pango.h"
#include "pangox.h"
#include <fribidi/fribidi.h>

#ifdef	WTT_CLUSTERING
#define ucs2tis(wc)             (unsigned int)((unsigned int)(wc) - 0x0E00 + 0xA0)
#define tis2uni(c)              ((gunichar)(c) - 0xA0 + 0x0E00)

#define	MAX_CLUSTER_CHRS	256
#define	MAX_GLYPHS		256

/* Define TACTIS character classes */
#define	CTRL			0
#define	NON			1
#define	CONS			2
#define	LV			3
#define	FV1			4
#define	FV2			5
#define	FV3			6
#define	BV1			7
#define	BV2			8
#define	BD			9
#define	TONE			10
#define	AD1			11
#define	AD2			12
#define	AD3			13
#define	AV1			14
#define	AV2			15
#define	AV3			16

#define	_ND			0
#define	_NC			1
#define	_UC			(1<<1)
#define	_BC			(1<<2)
#define	_SC			(1<<3)
#define	_AV			(1<<4)
#define	_BV			(1<<5)
#define	_TN			(1<<6)
#define	_AD			(1<<7)
#define	_BD			(1<<8)
#define	_AM			(1<<9)

#define	NoTailCons		_NC
#define	UpTailCons		_UC
#define	BotTailCons		_BC
#define	SpltTailCons		_SC
#define	Cons			(NoTailCons|UpTailCons|BotTailCons|SpltTailCons)
#define AboveVowel		_AV
#define BelowVowel		_BV
#define	Tone			_TN
#define AboveDiac		_AD
#define BelowDiac		_BD
#define	SaraAm			_AM

#define ChrType(InpChr)		_TACchclass[(unsigned int)(InpChr)]
#define	IsChrType(wc, mask)	(_ChrTypeTbl[ucs2tis((wc))] & (mask))
#endif


/* We handle the range U+0e01 to U+0e5b exactly
 */
static PangoEngineRange thai_ranges[] = {
  { 0x0e01, 0x0e5b, "*" },  /* Thai */
};

static PangoEngineInfo script_engines[] = {
  {
    "ThaiScriptEngineLang",
    PANGO_ENGINE_TYPE_LANG,
    PANGO_RENDER_TYPE_NONE,
    thai_ranges, G_N_ELEMENTS(thai_ranges)
  },
  {
    "ThaiScriptEngineX",
    PANGO_ENGINE_TYPE_SHAPE,
    PANGO_RENDER_TYPE_X,
    thai_ranges, G_N_ELEMENTS(thai_ranges)
  }
};

/*
 * Language script engine
 */

static void 
thai_engine_break (const char     *text,
		    gint            len,
		    PangoAnalysis  *analysis,
		    PangoLogAttr   *attrs)
{
}

static PangoEngine *
thai_engine_lang_new ()
{
  PangoEngineLang *result;
  
  result = g_new (PangoEngineLang, 1);

  result->engine.id = "ThaiScriptEngine";
  result->engine.type = PANGO_ENGINE_TYPE_LANG;
  result->engine.length = sizeof (result);
  result->script_break = thai_engine_break;

  return (PangoEngine *)result;
}

/*
 * X window system script engine portion
 */

typedef struct _ThaiFontInfo ThaiFontInfo;

/* The type of encoding that we will use
 */
typedef enum {
  THAI_FONT_NONE,
  THAI_FONT_XTIS,
  THAI_FONT_TIS,
#ifdef	WTT_CLUSTERING
  THAI_FONT_TIS_MAC,
  THAI_FONT_TIS_WIN,
#endif
  THAI_FONT_ISO10646
} ThaiFontType;

struct _ThaiFontInfo
{
  PangoFont   *font;
  ThaiFontType type;
  PangoXSubfont subfont;
};

/* All combining marks for Thai fall in the range U+0E30-U+0E50,
 * so we confine our data tables to that range, and use
 * default values for characters outside those ranges.
 */

/* Map from code point to group used for rendering with XTIS fonts
 * (0 == base character)
 */
static const char groups[32] = {
  0, 1, 0, 0, 1, 1, 1, 1,
  1, 1, 1, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 2,
  2, 2, 2, 2, 2, 2, 1, 0
};

/* Map from code point to index within group 1
 * (0 == no combining mark from group 1)
 */   
static const char group1_map[32] = {
  0, 1, 0, 0, 2, 3, 4, 5,
  6, 7, 8, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0
};

/* Map from code point to index within group 2
 * (0 == no combining mark from group 2)
 */   
static const char group2_map[32] = {
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 1,
  2, 3, 4, 5, 6, 7, 1, 0
};

#ifdef	WTT_CLUSTERING

static int _ChrTypeTbl[256] = {
	/*	   0,   1,   2,   3,   4,   5,   6,   7,
		   8,   9,   A,   B,   C,   D,   E,   F  */

	/*00*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*10*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*20*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*30*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*40*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*50*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*60*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*70*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*80*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
	/*90*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		
	/*A0*/	 _ND, _NC, _NC, _NC, _NC, _NC, _NC, _NC,
		 _NC, _NC, _NC, _NC, _NC, _SC, _BC, _BC,
	/*B0*/	 _SC, _NC, _NC, _NC, _NC, _NC, _NC, _NC,
		 _NC, _NC, _NC, _UC, _NC, _UC, _NC, _UC,
	/*C0*/	 _NC, _NC, _NC, _NC, _ND, _NC, _ND, _NC,
		 _NC, _NC, _NC, _NC, _UC, _NC, _NC, _ND,
	/*D0*/	 _ND, _AV, _ND, _AM, _AV, _AV, _AV, _AV,
		 _BV, _BV, _BD, _ND, _ND, _ND, _ND, _ND,
	/*E0*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _AD,
		 _TN, _TN, _TN, _TN, _AD, _AD, _AD, _ND,
	/*F0*/	 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND,
		 _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND
};

static int _TACchclass[256] = {
	/*	   0,   1,   2,   3,   4,   5,   6,   7,
		   8,   9,   A,   B,   C,   D,   E,   F  */

	/*00*/	CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
		CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
	/*10*/	CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
		CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
	/*20*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON, NON,
	/*30*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON, NON,
	/*40*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON, NON,
	/*50*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON, NON,
	/*60*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON, NON,
	/*70*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON,CTRL,
	/*80*/	CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
		CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
	/*90*/	CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
		CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,CTRL,
	/*A0*/	 NON,CONS,CONS,CONS,CONS,CONS,CONS,CONS,
		CONS,CONS,CONS,CONS,CONS,CONS,CONS,CONS,
	/*B0*/	CONS,CONS,CONS,CONS,CONS,CONS,CONS,CONS,
		CONS,CONS,CONS,CONS,CONS,CONS,CONS,CONS,
	/*C0*/	CONS,CONS,CONS,CONS, FV3,CONS, FV3,CONS,
		CONS,CONS,CONS,CONS,CONS,CONS,CONS, NON,
	/*D0*/	 FV1, AV2, FV1, FV1, AV1, AV3, AV2, AV3,
		 BV1, BV2,  BD, NON, NON, NON, NON, NON,
	/*E0*/	  LV,  LV,  LV,  LV,  LV, FV2, NON, AD2,
		TONE,TONE,TONE,TONE, AD1, AD1, AD3, NON,
	/*F0*/	 NON, NON, NON, NON, NON, NON, NON, NON,
		 NON, NON, NON, NON, NON, NON, NON,CTRL
};

/* Table for Thai Cell Manipulation */
static char _TAC_celltype_inputcheck[17][17] = {
	/* Cn */ /*	 0,  1,  2,  3,  4,  5,  6,  7,
			 8,  9,  A,  B,  C,  D,  E,  F	    */
	/* Cn-1 00 */	'X', 'A', 'A', 'A', 'A', 'A', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 10 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 20 */	'X', 'A', 'A', 'A', 'A', 'S', 'A', 'C',
			'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C',
	     /*	30 */	'X', 'S', 'A', 'S', 'S', 'S', 'S', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 40 */	'X', 'S', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 50 */	'X', 'A', 'A', 'A', 'A', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 60 */	'X', 'A', 'A', 'A', 'S', 'A', 'S', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 70 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'C', 'C', 'R', 'R', 'R', 'R', 'R',
	     /* 80 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'C', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* 90 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* A0 */	'X', 'A', 'A', 'A', 'A', 'A', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* B0 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* C0 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* D0 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
	     /* E0 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'C', 'C', 'R', 'R', 'R', 'R', 'R',
	     /* F0 */	'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'C', 'R', 'R', 'R', 'R', 'R', 'R',
			'X', 'A', 'A', 'A', 'S', 'S', 'A', 'R',
			'R', 'R', 'C', 'R', 'C', 'R', 'R', 'R', 'R'
};

typedef struct {
    gint ShiftDown_TONE_AD[8];
    gint ShiftDownLeft_TONE_AD[8];
    gint ShiftLeft_TONE_AD[8];
    gint ShiftLeft_AV[7];
    gint ShiftDown_BV_BD[3];
    gint TailCutCons[4];
} ThaiShapeTable;

#define shiftdown_tone_ad(c,tbl)      ((tbl)->ShiftDown_TONE_AD[(c)-0xE7])
#define shiftdownleft_tone_ad(c,tbl)  ((tbl)->ShiftDownLeft_TONE_AD[(c)-0xE7])
#define shiftleft_tone_ad(c,tbl)      ((tbl)->ShiftLeft_TONE_AD[(c)-0xE7])
#define shiftleft_av(c,tbl)           ((tbl)->ShiftLeft_AV[(c)-0xD1])
#define shiftdown_bv_bd(c,tbl)        ((tbl)->ShiftDown_BV_BD[(c)-0xD8])
#define tailcutcons(c,tbl)            ((tbl)->TailCutCons[(c)-0xAD])

/* MAC */
static const ThaiShapeTable MacShapeTable = {
    { 0xE7, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0xED, 0xEE },
    { 0xE7, 0x83, 0x84, 0x85, 0x86, 0x87, 0x8F, 0xEE },
    { 0x93, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x8F, 0xEE },
    { 0x92, 0x00, 0x00, 0x94, 0x95, 0x96, 0x97 },
    { 0xD8, 0xD9, 0xDA },
    { 0xAD, 0x00, 0x00, 0xB0 }
};

/* WIN */
static const ThaiShapeTable WinShapeTable = {
    { 0xE7, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0xED, 0xEE },
    { 0xE7, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x99, 0xEE },
    { 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0x99, 0xEE },
    { 0x98, 0x00, 0x00, 0x81, 0x82, 0x83, 0x84 },
    { 0xFC, 0xFD, 0xFE },
    { 0x90, 0x00, 0x00, 0x80 }
};

#endif

/* Returns a structure with information we will use to rendering given the
 * #PangoFont. This is computed once per font and cached for later retrieval.
 */
static ThaiFontInfo *
get_font_info (PangoFont *font)
{
  static const char *charsets[] = {
    "xtis620.2529-1",
    "xtis-0",
    "tis620.2533-1",
    "tis620.2529-1",
    "iso8859-11",
#ifdef	WTT_CLUSTERING
    "tis620-0",
    "tis620-1",
    "tis620-2",
#endif
    "iso10646-1",
  };

  static const int charset_types[] = {
    THAI_FONT_XTIS,
    THAI_FONT_XTIS,
    THAI_FONT_TIS,
    THAI_FONT_TIS,
    THAI_FONT_TIS,
#ifdef	WTT_CLUSTERING
    THAI_FONT_TIS,
    THAI_FONT_TIS_MAC,
    THAI_FONT_TIS_WIN,
#endif
    THAI_FONT_ISO10646
  };
  
  ThaiFontInfo *font_info;
  GQuark info_id = g_quark_from_string ("thai-font-info");
  
  font_info = g_object_get_qdata (G_OBJECT (font), info_id);

  if (!font_info)
    {
      /* No cached information not found, so we need to compute it
       * from scratch
       */
      PangoXSubfont *subfont_ids;
      int *subfont_charsets;
      int n_subfonts, i;

      font_info = g_new (ThaiFontInfo, 1);
      font_info->font = font;
      font_info->type = THAI_FONT_NONE;
      
      g_object_set_qdata_full (G_OBJECT (font), info_id, font_info, (GDestroyNotify)g_free);
      
      n_subfonts = pango_x_list_subfonts (font, (char **)charsets, G_N_ELEMENTS (charsets),
					  &subfont_ids, &subfont_charsets);

      for (i=0; i < n_subfonts; i++)
	{
	  ThaiFontType font_type = charset_types[subfont_charsets[i]];
	  
	  if (font_type != THAI_FONT_ISO10646 ||
	      pango_x_has_glyph (font, PANGO_X_MAKE_GLYPH (subfont_ids[i], 0xe01)))
	    {
	      font_info->type = font_type;
	      font_info->subfont = subfont_ids[i];
	      
	      break;
	    }
	}

      g_free (subfont_ids);
      g_free (subfont_charsets);
    }

  return font_info;
}

static void
add_glyph (ThaiFontInfo     *font_info, 
	   PangoGlyphString *glyphs, 
	   int               cluster_start, 
	   PangoGlyph        glyph,
	   gboolean          combining)
{
  PangoRectangle ink_rect, logical_rect;
  int index = glyphs->num_glyphs;

  pango_glyph_string_set_size (glyphs, index + 1);
  
  glyphs->glyphs[index].glyph = glyph;
  glyphs->glyphs[index].attr.is_cluster_start = combining ? 0 : 1;
  
  glyphs->log_clusters[index] = cluster_start;

  pango_font_get_glyph_extents (font_info->font,
				glyphs->glyphs[index].glyph, &ink_rect, &logical_rect);

  if (combining)
    {
#ifdef	WTT_CLUSTERING
      if ((font_info->type == THAI_FONT_TIS) ||
	  (font_info->type == THAI_FONT_TIS_MAC) ||
	  (font_info->type == THAI_FONT_TIS_WIN)) {
	  glyphs->glyphs[index].geometry.width =
		logical_rect.width + glyphs->glyphs[index - 1].geometry.width;
	  if (logical_rect.width > 0)
	      glyphs->glyphs[index].geometry.x_offset = glyphs->glyphs[index - 1].geometry.width;
	  else
	      glyphs->glyphs[index].geometry.x_offset = glyphs->glyphs[index].geometry.width;
	  glyphs->glyphs[index - 1].geometry.width = 0;
      } else {
	  glyphs->glyphs[index].geometry.width =
		MAX (logical_rect.width, glyphs->glyphs[index - 1].geometry.width);
	  glyphs->glyphs[index - 1].geometry.width = 0;
	  glyphs->glyphs[index].geometry.x_offset = 0;
      }
#else
      glyphs->glyphs[index].geometry.width = 
	MAX (logical_rect.width, glyphs->glyphs[index - 1].geometry.width);
      glyphs->glyphs[index - 1].geometry.width = 0;
      glyphs->glyphs[index].geometry.x_offset = 0;
#endif
    }
  else
    {
      glyphs->glyphs[index].geometry.x_offset = 0;
      glyphs->glyphs[index].geometry.width = logical_rect.width;
    }
  
  glyphs->glyphs[index].geometry.y_offset = 0;
}

#ifdef	WTT_CLUSTERING
static gint
get_win_mac_adjusted_glyphs_list (ThaiFontInfo *font_info,
		                  gunichar *cluster,
		                  gint num_chrs,
		                  gint **glyph_lists,
				  ThaiShapeTable *shaping_table)
{
      switch (num_chrs) {
        case 1:
          if (IsChrType(cluster[0], _BV|_BD|_AV|_AD|_TN)) {
              glyph_lists[0] =
              		PANGO_X_MAKE_GLYPH (font_info->subfont, 0x7F);
              glyph_lists[1] =
              		PANGO_X_MAKE_GLYPH (font_info->subfont,
					    ucs2tis(cluster[0]));
              return 2;
          } else {
              glyph_lists[0] =
              		PANGO_X_MAKE_GLYPH (font_info->subfont,
					    ucs2tis(cluster[0]));
              return 1;
          }
          break;
        
        case 2:
          if (IsChrType(cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
              IsChrType(cluster[1], SaraAm)) {
             glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						     ucs2tis(cluster[0]));
             glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont, 0xED);
             glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont, 0xD2);
             return 3;
          } else if (IsChrType(cluster[0], UpTailCons) &&
          	     IsChrType(cluster[1], SaraAm)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
                    glyph_lists[1] =
			    PANGO_X_MAKE_GLYPH (font_info->subfont,
				shiftleft_tone_ad(0xED, shaping_table));
                    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
							    0xD2);
                    return 3;
          } else if (IsChrType(cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
          	     IsChrType(cluster[1], _AV)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[1]));
		    return 2;
          } else if (IsChrType(cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
          	     IsChrType(cluster[1], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_tone_ad(ucs2tis(cluster[1]),
						          shaping_table));
		    return 2;
	  } else if (IsChrType(cluster[0], UpTailCons) &&
          	     IsChrType(cluster[1], _AV)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftleft_av(ucs2tis(cluster[1]),
						     shaping_table));
		    return 2;
          } else if (IsChrType(cluster[0], UpTailCons) &&
          	     IsChrType(cluster[1], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdownleft_tone_ad(ucs2tis(cluster[1]),
						              shaping_table));
		    return 2;
	  } else if (IsChrType(cluster[0], NoTailCons|UpTailCons) &&
          	     IsChrType(cluster[1], _BV|_BD)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[1]));
		    return 2;
          } else if (IsChrType(cluster[0], BotTailCons) &&
          	     IsChrType(cluster[1], _BV|_BD)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    					ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_bv_bd(ucs2tis(cluster[1]),
						        shaping_table));
		    return 2;
	  } else if (IsChrType(cluster[0], SpltTailCons) &&
          	     IsChrType(cluster[1], _BV|_BD)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			tailcutcons(ucs2tis(cluster[0]),
						    shaping_table));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    				ucs2tis(cluster[1]));
		    return 2;
	  } else {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
							    0x7F);
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
							ucs2tis(cluster[1]));
 		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
							ucs2tis(cluster[2]));
		    return 3;
	  }
          break;
          
        case 3:
          if (IsChrType(cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
              IsChrType(cluster[1], _TN) &&
              IsChrType(cluster[2], SaraAm)) {
             glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						     ucs2tis(cluster[0]));
             glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont, 0xED);
             glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						     ucs2tis(cluster[1]));
             glyph_lists[3] = PANGO_X_MAKE_GLYPH (font_info->subfont, 0xD2);
             return 4;
          } else if (IsChrType(cluster[0], UpTailCons) &&
		     IsChrType(cluster[1], _TN) &&
		     IsChrType(cluster[2], SaraAm)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
					shiftleft_tone_ad(0xED, shaping_table));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftleft_tone_ad(ucs2tis(cluster[1]),
						          shaping_table));
		    glyph_lists[3] = PANGO_X_MAKE_GLYPH (font_info->subfont,
							    0xD2);
		    return 4;
	  } else if (IsChrType(cluster[0], UpTailCons) &&
		     IsChrType(cluster[1], _AV) &&
		     IsChrType(cluster[2], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
					shiftleft_av(ucs2tis(cluster[1]),
						     shaping_table));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftleft_tone_ad(ucs2tis(cluster[2]),
						          shaping_table));
		    return 3;
	  } else if (IsChrType(cluster[0], UpTailCons) &&
		     IsChrType(cluster[1], _BV) &&
		     IsChrType(cluster[2], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[1]));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdownleft_tone_ad(ucs2tis(cluster[2]),
						              shaping_table));
		    return 3;
	  } else if (IsChrType(cluster[0], NoTailCons) &&
		     IsChrType(cluster[1], _BV) &&
		     IsChrType(cluster[2], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[1]));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_tone_ad(ucs2tis(cluster[2]),
						          shaping_table));
		    return 3;
	  } else if (IsChrType(cluster[0], SpltTailCons) &&
		     IsChrType(cluster[1], _BV) &&
		     IsChrType(cluster[2], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			tailcutcons(ucs2tis(cluster[0]),
						    shaping_table));
		    glyph_lists[1] =
		        PANGO_X_MAKE_GLYPH (font_info->subfont,
					    ucs2tis(cluster[1]));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_tone_ad(ucs2tis(cluster[2]),
						          shaping_table));
		    return 3;
	  } else if (IsChrType(cluster[0], BotTailCons) &&
		     IsChrType(cluster[1], _BV) &&
		     IsChrType(cluster[2], _AD|_TN)) {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_bv_bd(ucs2tis(cluster[1]),
						        shaping_table));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
		    			shiftdown_tone_ad(ucs2tis(cluster[2]),
						          shaping_table));
		    return 3;
	  } else {
		    glyph_lists[0] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[0]));
		    glyph_lists[1] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[1]));
		    glyph_lists[2] = PANGO_X_MAKE_GLYPH (font_info->subfont,
						    ucs2tis(cluster[2]));
		    return 3;
	  }
          break;
      }
      return 0;
}

static gint
get_glyphs_list (ThaiFontInfo *font_info,
		 gunichar *cluster,
		 gint num_chrs,
		 gint **glyph_lists)
{
  int i;
  PangoGlyph glyph;
  int xtis_index;

  if ((cluster == NULL) || (num_chrs == 0))
      return 0;

  switch (font_info->type)
    {
    case THAI_FONT_NONE:
      for (i=0; i < num_chrs; i++)
	  glyph_lists[i] = pango_x_get_unknown_glyph (font_info->font);
      return num_chrs;

    case THAI_FONT_XTIS:
      /* If we are rendering with an XTIS font, we try to find a precomposed
       * glyph for the cluster.
       */
      xtis_index = 0x100 * (cluster[0] - 0xe00 + 0x20) + 0x30;
      if (cluster[1])
	  xtis_index +=8 * group1_map[cluster[1] - 0xe30];
      if (cluster[2])
	  xtis_index += group2_map[cluster[2] - 0xe30];
      glyph = PANGO_X_MAKE_GLYPH (font_info->subfont, xtis_index);
      if (pango_x_has_glyph (font_info->font, glyph)) {
          glyph_lists[0] = glyph;
          return 1;
      }
      for (i=0; i < num_chrs; i++)
	  glyph_lists[i] =
	      PANGO_X_MAKE_GLYPH (font_info->subfont, 0x100 * (cluster[i] - 0xe00 + 0x20) + 0x30);
      return num_chrs;
      
    case THAI_FONT_TIS:
      for (i=0; i < num_chrs; i++)
          glyph_lists[i] =
              PANGO_X_MAKE_GLYPH (font_info->subfont, ucs2tis(cluster[i]));
      return num_chrs;
      
    case THAI_FONT_TIS_MAC: /* MacIntosh Extension */
      return get_win_mac_adjusted_glyphs_list (font_info,
		                               cluster,
		                               num_chrs,
		                               glyph_lists,
					       &MacShapeTable);
      break;
      
    case THAI_FONT_TIS_WIN: /* Microsoft Extension */
      return get_win_mac_adjusted_glyphs_list (font_info,
		                               cluster,
		                               num_chrs,
		                               glyph_lists,
					       &WinShapeTable);
      break;
      
    case THAI_FONT_ISO10646:
      for (i=0; i < num_chrs; i++)
          glyph_lists[i] = PANGO_X_MAKE_GLYPH (font_info->subfont, cluster[i]);
      return num_chrs;
    }
  return 0;			/* Quiet GCC */
}
#endif


/* Return the glyph code within the font for the given Unicode Thai 
 * code pointer
 */
get_glyph (ThaiFontInfo *font_info, gunichar wc)
{
  switch (font_info->type)
    {
    case THAI_FONT_NONE:
      return pango_x_get_unknown_glyph (font_info->font);
    case THAI_FONT_XTIS:
      return PANGO_X_MAKE_GLYPH (font_info->subfont, 0x100 * (wc - 0xe00 + 0x20) + 0x30);
    case THAI_FONT_TIS:
      return PANGO_X_MAKE_GLYPH (font_info->subfont, wc - 0xe00 + 0xA0);
    case THAI_FONT_ISO10646:
      return PANGO_X_MAKE_GLYPH (font_info->subfont, wc);
    }
  return 0;			/* Quiet GCC */
}

#ifdef	WTT_CLUSTERING
static void
add_cluster (ThaiFontInfo *font_info,
	     PangoGlyphString *glyphs,
	     int cluster_start,
	     gunichar *cluster,
	     gint num_chrs)
	     
{
  int i;
  gint glyphs_list[MAX_GLYPHS];
  gint num_glyphs;
  
  num_glyphs = get_glyphs_list(font_info, cluster, num_chrs, &glyphs_list);
  for (i=0; i<num_glyphs; i++)
       add_glyph (font_info, glyphs, cluster_start, glyphs_list[i],
	    		i == 0 ? FALSE : TRUE);
}
#else
static void
add_cluster (ThaiFontInfo *font_info,
	     PangoGlyphString *glyphs,
	     int cluster_start,
	     gunichar base, 
	     gunichar group1,
	     gunichar group2)
{
  /* If we are rendering with an XTIS font, we try to find a precomposed
   * glyph for the cluster.
   */
  if (font_info->type == THAI_FONT_XTIS)
    {
      PangoGlyph glyph;
      int xtis_index = 0x100 * (base - 0xe00 + 0x20) + 0x30;
      if (group1)
	xtis_index +=8 * group1_map[group1 - 0xe30];
      if (group2)
	xtis_index += group2_map[group2 - 0xe30];
      
      glyph = PANGO_X_MAKE_GLYPH (font_info->subfont, xtis_index);

      if (pango_x_has_glyph (font_info->font, glyph))
	{
	  add_glyph (font_info, glyphs, cluster_start, glyph, FALSE);
	  return;
	}
    }

  /* If that failed, then we add compose the cluster out of three 
   * individual glyphs
   */
  add_glyph (font_info, glyphs, cluster_start, get_glyph (font_info, base), FALSE);
  if (group1)
    add_glyph (font_info, glyphs, cluster_start, get_glyph (font_info, group1), TRUE);
  if (group2)
    add_glyph (font_info, glyphs, cluster_start, get_glyph (font_info, group2), TRUE);
}
#endif


#ifdef	WTT_CLUSTERING
gboolean
IsWttComposible(gint cur_wc, gint nxt_wc)
{
  switch (_TAC_celltype_inputcheck[ChrType(ucs2tis(cur_wc))]
				  [ChrType(ucs2tis(nxt_wc))])
  {
     case 'A':
     case 'S':
     case 'R':
     case 'X':
       return FALSE;

     case 'C':
       return TRUE;
  }
}

static char *
g_utf8_get_next_cluster(const char	*text,
			gint		length,
			gunichar	**cluster,
			gint		*num_chrs)
{  
  int nChrs = 1;
  gboolean ClusterNotFound = TRUE;
  const char *p;

  if ((text == NULL) || (length == 0) ||
      ((text) && (*text == '\0')) ) {
      if (*num_chrs)
          *num_chrs = 0;
      if (cluster)
	  cluster[0] = 0;
      return (char *)NULL;
  }
  
  p = text;
  if (cluster)
      cluster[0] = g_utf8_get_char(p);
  p = g_utf8_next_char(p);
  do {
    if (p >= text + length)
	break;
    if (cluster)
        cluster[nChrs] = g_utf8_get_char(p);
    if (IsWttComposible(cluster[nChrs - 1], cluster[nChrs])) {
        nChrs++;
        if (nChrs == 3)
	    ClusterNotFound = FALSE;
        p = g_utf8_next_char(p);
    } else {
	if ((nChrs == 1) &&
	    IsChrType(cluster[nChrs - 1], Cons) &&
	    IsChrType(cluster[nChrs], SaraAm) ) {
	    nChrs = 2;
	    p = g_utf8_next_char(p);
	} else if ((nChrs == 2) &&
		   IsChrType(cluster[nChrs - 2], Cons) &&
		   IsChrType(cluster[nChrs - 1], Tone) &&
		   IsChrType(cluster[nChrs], SaraAm) ) {
		   nChrs = 3;
		   p = g_utf8_next_char(p);
	}
	ClusterNotFound = FALSE;
    }
  } while (ClusterNotFound);

  if (*num_chrs)
      *num_chrs = nChrs;
  if (cluster)
      cluster[nChrs] = 0;
  return p;
}
#endif


static void 
thai_engine_shape (PangoFont        *font,
		   const char       *text,
		   gint              length,
		   PangoAnalysis    *analysis,
		   PangoGlyphString *glyphs)
{
  ThaiFontInfo *font_info;
  const char *p;
  const char *log_cluster;

  gunichar base = 0;
  gunichar group1 = 0;
  gunichar group2 = 0;
  int cluster_start = 0;

  pango_glyph_string_set_size (glyphs, 0);

  font_info = get_font_info (font);

#ifdef	WTT_CLUSTERING
  p = text;
  while (p < text + length)
    {
      gunichar cluster[MAX_CLUSTER_CHRS];
      gint num_chrs;

	log_cluster = p;
	p = g_utf8_get_next_cluster(p, text + length - p, &cluster, &num_chrs);
	add_cluster(font_info, glyphs, log_cluster - text, cluster, num_chrs);
    }
#else
  p = text;
  while (p < text + length)
    {
      int group;
      gunichar wc;

      wc = g_utf8_get_char (p);

      if (wc >= 0xe30 && wc < 0xe50)
	group = groups[wc - 0xe30];
      else
	group = 0;

      switch (group)
	{
	case 0:
	  if (base)
	    {
	      add_cluster (font_info, glyphs, cluster_start, base, group1, group2);
	      group1 = 0;
	      group2 = 0;
	    }
	  cluster_start = p - text;
	  base = wc;
	  break;
	case 1:
	  group1 = wc;
	  break;
	case 2:
	  group2 = wc;
	  break;
	}
      
      p = g_utf8_next_char (p);
    }

  if (base)
    add_cluster (font_info, glyphs, cluster_start, base, group1, group2);
#endif

}

static PangoCoverage *
thai_engine_get_coverage (PangoFont  *font,
			   const char *lang)
{
  PangoCoverage *result = pango_coverage_new ();
  
  ThaiFontInfo *font_info = get_font_info (font);
  
  if (font_info->type != THAI_FONT_NONE)
    {
      gunichar wc;
      
      for (wc = 0xe01; wc <= 0xe3a; wc++)
	pango_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
      for (wc = 0xe3f; wc <= 0xe5b; wc++)
	pango_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    }

  return result;
}

static PangoEngine *
thai_engine_x_new ()
{
  PangoEngineShape *result;
  
  result = g_new (PangoEngineShape, 1);

  result->engine.id = "ThaiScriptEngine";
#ifdef	WTT_CLUSTERING
  result->engine.type = PANGO_ENGINE_TYPE_SHAPE;
#else
  result->engine.type = PANGO_ENGINE_TYPE_LANG;
#endif
  result->engine.length = sizeof (result);
  result->script_shape = thai_engine_shape;
  result->get_coverage = thai_engine_get_coverage;

  return (PangoEngine *)result;
}

/* The following three functions provide the public module API for
 * Pango. If we are compiling it is 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_thai_
 */
#ifdef MODULE_PREFIX
#define MODULE_ENTRY(func) _pango_thai_##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)
{
  if (!strcmp (id, "ThaiScriptEngineLang")) {
    return thai_engine_lang_new ();
  } else if (!strcmp (id, "ThaiScriptEngineX")) {
    return thai_engine_x_new ();
  } else {
    return NULL;
  }
}

void 
MODULE_ENTRY(script_engine_unload) (PangoEngine *engine)
{
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]