/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * Pangolite
 * dvng-x.c:
 * 
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Sun Microsystems, Inc.
 * Portions created by the Initial Developer are Copyright (C) 2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Prabhat Hegde (prabhat.hegde@sun.com)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <glib.h>
#include <string.h>

#include "pango-engine.h"
#include "pango-coverage.h"

#define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
#define MAX_CLUSTER_CHRS 256
#define MAX_GLYPHS       256
#define GLYPH_COMBINING  256

/*************************************************************************
 *         CHARACTER TYPE CONSTANTS - What they represent                *
 *         ----------------------------------------------                *
 *                                                                       *
 * _NP : Vowel-Modifier Visarg(U+0903) (Displayed to the right).         *
 * _UP : Vowel-Modifer Chandrabindu(U+0901) and Anuswar (U+0902).        *
 *       Displayed above the Consonant/Vowel                             *
 * _IV : Independant Vowels                                              *
 * _CN : All consonants except _CK and _RC below                         *
 * _CK : Consonants that can be followed by a Nukta(U+093C). Characters  *
 *       (U+0958-U+095F). No clue why Unicode imposed it on Devanagari.  *
 * _RC : Consonant Ra - Needs to handle 'Reph'                           *
 * _NM : Handle special case of Matras when followed by a Chandrabindu/  *
 *       Anuswar.                                                        *
 * _IM : Choti I Matra (U+093F). Needs to handle re-ordering.            *
 * _HL : Halant (U+094D)                                                 *
 * _NK : Nukta (U+093C)                                                  *
 * _VD : For Vedic extension characters (U+0951 - U+0954)                *
 * _HD : Hindu Numerals written in Devanagari                            *
 * _MS : For Matras such U+0941, U+0942 which need to be attached to the *
 *       holding consonant at the Stem.                                  *
 * _RM : A cluster of Ra+Halant at the begining would be represented by  *
 *       a Reph (placed in sun.unicode.india-0 at 0xF812). Reph is       *
 *       typically attached on top of the last consonant.                *
 * _II_M, _EY_M, _AI_M, _OW1_M, _OW2_M, _AYE_M, _EE_M, _AWE_M, _O_M,     * 
 *     : Separate glyphs are provided which combine matra with a reph.   *
 *       Matras which need to use this are represented by the above.     *
 *************************************************************************/
 
/*************************************************************************
 *                          CLUSTERING LOGIC                             *
 *                          ----------------                             *
 *                                                                       *
 * Notations used to describe Devanagari Script Generic Info:            *
 * D : Vowel-Modifiers (U+0901 - U+0903)                                 *
 * V : Vowels (U+0905 - U+0913) & (U+0960, U+0961)                       *
 * C : Consonants (U+0915 - U+0939)                                      *
 * M : Matras (U+093E - U+094C) & (U+0962, U+0963)                       *
 * H : Halant (U+094D)                                                   *
 * N : Nukta (U+093C)                                                    *
 *                                                                       *
 * RULES:-                                                               *
 * -------                                                               *
 * Syllable/Cluster types                                                *
 *                                                                       *
 * 1] Vowel Syllable ::- V[D]                                            *
 * 2] Cons - Vowel Syllable ::- [Cons-Syllable] Full- Cons [M] [D]       *
 * 3] Cons-Syllable  ::- [Pure-Cons][Pure-Cons] Pure-Cons                *
 * 4] Pure-Cons  ::- Full-Cons H                                         *
 * 5] Full-Cons  ::- C[N]                                                *
 *                                                                       *
 * Notes:                                                                *
 * 1] Nukta (N) can come after only those consonants with which it can   *
 *    combine, ie U+0915-U+0917, U+091C, U+0921, U+0922, U+092B & U+092F *
 * 2] Worst case Vowel cluster - V D                                     *
 * 3] A worst case Consonant cluster: C N H C N H C N H C N M D          *
 *************************************************************************/

/*
 * Devanagari character classes
 */
#define _NP    1L
#define _UP    (1L<<1)
#define _IV    (1L<<2)
#define _CN    (1L<<3)
#define _CK    (1L<<4)
#define _RC    (1L<<5)
#define _NM    (1L<<6)
#define _IM    (1L<<7)
#define _HL    (1L<<8)
#define _NK    (1L<<9)
#define _VD    (1L<<10)
#define _HD    (1L<<11)
#define _II_M  (1L<<12)
#define _EY_M  (1L<<13)
#define _AI_M  (1L<<14)
#define _OW1_M (1L<<15)
#define _OW2_M (1L<<16)
#define _MS    (1L<<17)
#define _AYE_M (1L<<18)
#define _EE_M  (1L<<19)
#define _AWE_M (1L<<20)
#define _O_M   (1L<<21)
#define _RM    (_II_M|_EY_M|_AI_M|_OW1_M|_OW2_M|_AYE_M|_EE_M|_AWE_M|_O_M)

/* Common Non-defined type */
#define __ND 0

/*
 * Devanagari character types
 */
#define __UP     1
#define __NP     2
#define __IV     3
#define __CN     4
#define __CK     5
#define __RC     6
#define __NM     7
#define __RM     8
#define __IM     9
#define __HL    10
#define __NK    11
#define __VD    12
#define __HD    13

/*
 * Devanagari Glyph Type State
 */
#define St0      0
#define St1      1
#define St2      2
#define St3      3
#define St4      4
#define St5      5
#define St6      6
#define St7      7
#define St8      8
#define St9      9
#define St10    10
#define St11    11
#define St12    12
#define St13    13
#define St14    14
#define St15    15
#define St16    16
#define St17    17
#define St18    18
#define St19    19
#define St20    20

#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 MAP_SIZE     243
#define MAX_STATE     21
#define MAX_DEVA_TYPE 14
#define MAX_CORE_CONS  6

#define SCRIPT_ENGINE_NAME  "DvngScriptEngineX"
#define PANGO_RENDER_TYPE_X "PangoliteRenderX"

#define ucs2dvng(ch) (gunichar2)((gunichar2)(ch) - 0x0900)

typedef guint16 PangoliteXSubfont;
#define PANGO_MOZ_MAKE_GLYPH(index) ((guint32)0 | (index))

/* We handle the following ranges between U+0901 to U+0970 exactly
 */
static PangoliteEngineRange dvng_ranges[] = {
  { 0x0901, 0x0903, "*" },
  { 0x0905, 0x0939, "*" },
  { 0x093c, 0x094d, "*" },
  { 0x0950, 0x0954, "*" },
  { 0x0958, 0x0970, "*" }, /* Hindi Ranges */
};

static PangoliteEngineInfo script_engines[] = {
  {
    SCRIPT_ENGINE_NAME,
    PANGO_ENGINE_TYPE_SHAPE,
    PANGO_RENDER_TYPE_X,
    dvng_ranges, 
    G_N_ELEMENTS(dvng_ranges)
  }
};

/* X window system script engine portion
 */
typedef struct _DvngFontInfo DvngFontInfo;

/* The type of encoding that we will use
 */
typedef enum {
  DVNG_FONT_NONE,
  DVNG_FONT_SUN
} DvngFontType;

/*
 */
typedef struct {
  const gunichar       ISCII[MAX_CORE_CONS];
  const unsigned short ISFOC[MAX_CORE_CONS];
} DvngGlyphEntry;

/*
 */
struct _DvngFontInfo
{
  DvngFontType  type;
  PangoliteXSubfont subfont;
};

typedef long DvngCls;
typedef int  StateType;

/*
 * Devanagari character class table
 */
static const DvngCls DvngChrClsTbl[128] = {
/*            0,       1,      2,        3,      4,       5,    6,   7,
              8,       9,      A,        B,      C,       D,    E,   F, */
/* 0 */     _ND,     _UP,     _UP,     _NP,    _ND,     _IV,   _IV, _IV,
            _IV,     _IV,     _IV,     _IV,    _IV,     _IV,   _IV, _IV,
/* 1 */     _IV,     _IV,     _IV,     _IV,    _IV, _CK|_MS,   _CK, _CK,
            _CN,     _CN,     _CN,     _CN,    _CK,     _CN,   _CN,_CN|_MS,
/* 2 */ _CN|_MS, _CK|_MS, _CK|_MS,     _CN,    _CN,     _CN,   _CN, _CN,
            _CN,     _CN,     _CN, _CK|_MS,    _CN,     _CN,   _CN, _CN,
/* 3 */     _RC,     _CN,     _CN,     _CN,    _CN,     _CN,   _CN, _CN,
            _CN, _CN|_MS,     _ND,     _ND,    _NK,     _VD,   _NM, _IM,
/* 4 */   _II_M,     _NM,     _NM,     _NM,    _NM,  _AYE_M, _EE_M, _EY_M,
          _AI_M,  _AWE_M,    _O_M,  _OW1_M, _OW2_M,     _HL,   _ND, _ND,
/* 5 */     _ND,     _VD,     _VD,     _VD,    _VD,     _ND,   _ND, _ND,
            _CN,     _CN,     _CN,     _CN,    _CN,     _CN,   _CN, _CN,
/* 6 */     _IV,     _IV,     _NM,     _NM,    _ND,     _ND,   _HD, _HD,
            _HD,     _HD,     _HD,     _HD,    _HD,     _HD,   _HD, _HD,
/* 7 */     _ND,     _ND,     _ND,     _ND,    _ND,     _ND,   _ND, _ND,
            _ND,     _ND,     _ND,     _ND,    _ND,     _ND,   _ND, _ND,
};

/*
 * Devanagari character type table
 */
static const gint DvngChrTypeTbl[128] = {
/*         0,    1,    2,    3,    4,    5,    6,    7,
           8,    9,    A,    B,    C,    D,    E,    F, */
/* 0 */ __ND, __UP, __UP, __NP, __ND, __IV, __IV, __IV,
        __IV, __IV, __IV, __IV, __IV, __IV, __IV, __IV,
/* 1 */ __IV, __IV, __IV, __IV, __IV, __CK, __CK, __CK,
        __CN, __CN, __CN, __CN, __CK, __CN, __CN, __CN,
/* 2 */ __CN, __CK, __CK, __CN, __CN, __CN, __CN, __CN,
        __CN, __CN, __CN, __CK, __CN, __CN, __CN, __CN,
/* 3 */ __RC, __CN, __CN, __CN, __CN, __CN, __CN, __CN,
        __CN, __CN, __ND, __ND, __NK, __VD, __NM, __IM,
/* 4 */ __RM, __NM, __NM, __NM, __NM, __RM, __RM, __RM,
        __RM, __RM, __RM, __RM, __RM, __HL, __ND, __ND,
/* 5 */ __ND, __VD, __VD, __VD, __VD, __ND, __ND, __ND,
        __CN, __CN, __CN, __CN, __CN, __CN, __CN, __CN,
/* 6 */ __IV, __IV, __NM, __NM, __ND, __ND, __HD, __HD,
        __HD, __HD, __HD, __HD, __HD, __HD, __HD, __HD,
/* 7 */ __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND,
        __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND,
};

/*
 * Devanagari character composible table
 */
static const gint DvngComposeTbl[14][14] = {
  /*        ND, UP, NP, IV, CN, CK, RC, NM, RM, IM, HL, NK, VD, HD,  */
  /* 0  */ { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* ND */
  /* 1  */ { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* UP */
  /* 2  */ { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* NP */
  /* 3  */ { 0,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* IV */
  /* 4  */ { 0,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,}, /* CN */
  /* 5  */ { 0,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  1,  0,  0,}, /* CK */
  /* 6  */ { 0,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,}, /* RC */
  /* 7  */ { 0,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* NM */
  /* 8  */ { 0,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* RM */
  /* 9  */ { 0,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* IM */
  /* 10 */ { 0,  0,  0,  0,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0,}, /* HL */
  /* 11 */ { 0,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,}, /* NK */
  /* 12 */ { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* VD */
  /* 13 */ { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,}, /* HD */
};

StateType DvngStateTbl[MAX_STATE][MAX_DEVA_TYPE] = {

  /* ND,   UP,   NP,   IV,   CN,   CK,   RC,   NM,   RM,
     IM,   HL,   NK,   VD,   HD */
                                           /* State 0 */
 {  St11, St1, St1,  St2,  St4,  St4, St12,  St1,  St1,
    St1,  St1, St1,  St1, St11, },
                                           /* State 1 */
 {  St1,  St1,  St1,  St1,  St1,  St1,  St1,  St1,  St1,
    St1,  St1,  St1,  St1,  St1, },
                                           /* State 2 */
 {  St2,  St3,  St3,  St2,  St2,  St2,  St2,  St2,  St2,
    St2,  St2,  St2,  St2,  St2, },
                                           /* State 3 */
 {  St3,  St3,  St3,  St3,  St3,  St3,  St3,  St3,  St3,
    St3,  St3,  St3,  St3,  St3, },
                                           /* State 4 */
 {  St4,  St8,  St8,  St4,  St4,  St4,  St4,  St6,  St6,
    St9,  St5,  St4,  St4,  St4, },
                                           /* State 5 */
 {  St5,  St5,  St5,  St5,  St4,  St4,  St4,  St5,  St5,
    St5,  St5,  St5,  St5,  St5, },
                                           /* State 6 */
 {  St6,  St7,  St7,  St6,  St6,  St6,  St6,  St6,  St6,
    St6,  St6,  St6,  St6,  St6, },
                                           /* State 7 */
 {  St7,  St7,  St7,  St7,  St7,  St7,  St7,  St7,  St7,
    St7,  St7,  St7,  St7,  St7, },
                                           /* State 8 */
 {  St8,  St8,  St8,  St8,  St8,  St8,  St8,  St8,  St8,
    St8,  St8,  St8,  St8,  St8, },
                                           /* State 9 */
 {  St9, St10, St10,  St9,  St9,  St9,  St9,  St9,  St9,
    St9,  St9,  St9,  St9,  St9, },
                                          /* State 10 */
 { St10, St10, St10, St10, St10, St10, St10, St10, St10,
   St10, St10, St10, St10, St10, },
                                          /* State 11 */
 { St11, St11, St11, St11, St11, St11, St11, St11, St11,
   St11, St11, St11, St11, St11, },
                                          /* State 12 */
 { St12,  St8,  St8, St12, St12, St12, St12,  St6,  St6,
    St9, St13, St12, St12, St12, },
                                          /* State 13 */
 { St13, St13, St13, St13, St14, St14, St14, St13, St13,
   St13, St13, St13, St13, St13, },
                                           /* State 14 */
 { St14, St18, St18, St14, St14, St14, St14, St16, St16,
   St19, St15, St14, St14, St14, },
                                           /* State 15 */
 { St15, St15, St15, St15, St14, St14, St14, St15, St15,
   St15, St15, St15, St15, St15, },
                                           /* State 16 */
 { St16, St17, St17, St16, St16, St16, St16, St16, St16,
   St16, St16, St16, St16, St16, },
                                           /* State 17 */
 { St17, St17, St17, St17, St17, St17, St17, St17, St17,
   St17, St17, St17, St17, St17, },
                                           /* State 18 */
 { St18, St18, St18, St18, St18, St18, St18, St18, St18,
   St18, St18, St18, St18, St18, },
                                           /* State 19 */
 { St19, St20, St20, St19, St19, St19, St19, St19, St19,
   St19, St19, St19, St19, St19, },
                                           /* State 20 */
 { St20, St20, St20, St20, St20, St20, St20, St20, St20,
   St20, St20, St20, St20, St20, },
};

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

/* 0 */  0,  1,  1,  1,  0,  1,  1,  1,
         1,  1,  1,  1,  1,  1,  1,  1,
/* 1 */  1,  1,  1,  1,  1,  4,  4,  4,
         4,  2,  4,  1,  4,  4,  3,  3,
/* 2 */  3,  3,  3,  2,  4,  4,  4,  4,
         4,  1,  4,  4,  4,  4,  4,  4,
/* 3 */  2,  2,  2,  2,  1,  4,  4,  3,
         5,  4,  0,  0,  2,  1,  1,  1,
/* 4 */  1,  1,  1,  1,  1,  1,  1,  1,
         1,  1,  1,  1,  1,  3,  0,  0,
/* 5 */  1,  1,  1,  1,  1,  0,  0,  0,
         1,  1,  1,  1,  1,  1,  1,  1,
/* 6 */  1,  1,  1,  1,  1,  1,  1,  1,
         1,  1,  1,  1,  1,  1,  1,  1,
/* 7 */  1,  0,  0,  0,  0,  0,  0,  0,
         0,  0,  0,  0,  0,  0,  0,  0,
};

#define IsDvngCharCls(ch, mask) (DvngChrClsTbl[ucs2dvng((ch))] & (mask))

#define IsComposible(ch1, ch2) (DvngComposeTbl[DvngChrTypeTbl[ucs2dvng((ch1))]][DvngChrTypeTbl[ucs2dvng((ch2))]])

#define GetDvngRuleCt(ch) (DvngRuleTbl[ucs2dvng((ch))])

#define IsKern(gid) ((gid >= 0xF830 && gid <= 0xF83E) ? TRUE : FALSE)

#define IsMatraAtStem(gid) (((gid >= 0xF811 && gid <= 0xF813) || \
                             (gid >= 0xF81E && gid <= 0xF82F) || \
                             (gid >= 0x0962 && gid <= 0x0963) || \
                             (gid >= 0x0941 && gid <= 0x0948) || \
                             (gid == 0x093C) || (gid == 0x094D)) ? TRUE : FALSE)

#define SetClusterState(st, ch) DvngStateTbl[(st)][DvngChrTypeTbl[ucs2dvng((ch))]]

#define MAP_SIZE 243

/*
 * Devanagari Glyph Tables
 */
static const DvngGlyphEntry sunGlyphTbl[MAP_SIZE] = {
  /* Vowel Modifiers - Ct 3 */
  { {0x0901,0x0},                       {0x0901,0x0} },
  { {0x0902,0x0},                       {0x0902,0x0} },
  { {0x0903,0x0},                       {0x0903,0x0} },

  /* vowels 13 - Ct 18 */
  { {0x0905,0x0},                       {0x0905,0x0} },
  { {0x0906,0x0},                       {0x0906,0x0} },
  { {0x0907,0x0},                       {0x0907,0x0} },
  { {0x0908,0x0},                       {0x0908,0x0} },
  { {0x0909,0x0},                       {0x0909,0x0} },
  { {0x090a,0x0},                       {0x090a,0xF830,0x0} },
  { {0x090b,0x0},                       {0x090b,0xF831,0x0} },
  { {0x0960,0x0},                       {0x0960,0xF831,0x0} },
  { {0x090c,0x0},                       {0x090c,0xF83D,0x0} },
  { {0x0961,0x0},                       {0x0961,0xF83D,0x0} },
  { {0x090d,0x0},                       {0x090d,0x0} },
  { {0x090e,0x0},                       {0x090e,0x0} },
  { {0x090f,0x0},                       {0x090f,0x0} },
  { {0x0910,0x0},                       {0x0910,0x0} },
  { {0x0911,0x0},                       {0x0911,0x0} },
  { {0x0912,0x0},                       {0x0912,0x0} },
  { {0x0913,0x0},                       {0x0913,0x0} },
  { {0x0914,0x0},                       {0x0914,0x0} },

  /* Vowel signs - Ct 17 */
  { {0x093e,0x0},                       {0x093e,0x0} },
  { {0x093f,0x0},                       {0x093f,0x0} },
  { {0x0940,0x0},                       {0x0940,0x0} },
  { {0x0941,0x0},                       {0x0941,0x0} },
  { {0x0942,0x0},                       {0x0942,0x0} },
  { {0x0943,0x0},                       {0x0943,0x0} },
  { {0x0944,0x0},                       {0x0944,0x0} },
  { {0x0945,0x0},                       {0x0945,0x0} },
  { {0x0946,0x0},                       {0x0946,0x0} },
  { {0x0947,0x0},                       {0x0947,0x0} },
  { {0x0948,0x0},                       {0x0948,0x0} },
  { {0x0949,0x0},                       {0x0949,0x0} },
  { {0x094a,0x0},                       {0x094a,0x0} },
  { {0x094b,0x0},                       {0x094b,0x0} },
  { {0x094c,0x0},                       {0x094c,0x0} },
  { {0x0962,0x0},                       {0x0962,0x0} },
  { {0x0963,0x0},                       {0x0963,0x0} },

  /* Consonants */
  /* ka -> ka + kern space */
  { {0x0915,0x0},                       {0x0915,0xF832,0x0} },
  /* ka nukta  -> ka nukta + kern space */
  { {0x0915,0x093c,0x0},                {0x0958,0xF832,0x0} },
  /* ka + halant  -> half ka */
  { {0x0915,0x094d,0x0},                {0xF7C1,0x0} },
  /* ka nukta + halant-> half ka nukta */
  { {0x0915,0x093c,0x094d,0x0},         {0xF7C2,0x0} },

  { {0x0915,0x094d,0x0915,0x0},         {0xF845,0xF832,0x0} },

  /* ka + halant + ta -> kta + kern space */
  { {0x0915,0x094d,0x0924,0x0},         {0xF7C4,0xF832,0x0} },
  /* ka + halant + ra -> kra + kern space */
  { {0x0915,0x094d,0x0930,0x0},         {0xF7C3,0xF832,0x0} },
  { {0x0915,0x094d,0x0930,0x094d,0x0},  {0xF7C3,0x094d,0xF832,0x0} },
  /* ka + halant + SHa -> kSHa */
  { {0x0915,0x094d,0x0937,0x0},         {0xF7C5,0x093E,0x0} }, 
  /* ka + halant + SHa + halant -> half kSHa */
  { {0x0915,0x094d,0x0937,0x094d,0x0},  {0xF7C5,0x0} },

  /* kha 6 */
  { {0x0916,0x0},                       {0x0916,0x0} },
  { {0x0916,0x094d,0x0},                {0xF7C6,0x0} },
  { {0x0916,0x093c,0x0},                {0x0959,0x0} },
  { {0x0916,0x093c,0x094d,0x0},         {0xF7C7,0x0} },
  { {0x0916,0x094d,0x0930,0x0},         {0xF7C8,0x093E,0x0} },
  { {0x0916,0x094d,0x0930,0x094d,0x0},  {0xF7C8,0x0} },

  /* ga 6 */
  { {0x0917,0x0},                       {0x0917,0x0} },
  { {0x0917,0x094d,0x0},                {0xF7C9,0x0} },
  { {0x0917,0x093c,0x0},                {0x095a,0x0} },
  { {0x0917,0x093c,0x094d,0x0},         {0xF7CA,0x0} },
  { {0x0917,0x094d,0x0930,0x0},         {0xF7CB,0x093E,0x0} },
  { {0x0917,0x094d,0x0930,0x094d,0x0},  {0xF7CB,0x0} },

  /* gha 4 */
  { {0x0918,0x0},                       {0x0918,0x0} },
  { {0x0918,0x094d,0x0},                {0xF7CC,0x0} },
  { {0x0918,0x094d,0x0930,0x0},         {0xF7CD,0x093E,0x0} },
  { {0x0918,0x094d,0x0930,0x094d,0x0},  {0xF7CD,0x0} },

  /* nga 1 */
  { {0x0919,0x0},                       {0x0919,0xF833,0x0} },

  /* { {0x0919,0x094d,0x0},             {0x0919,0x094d,0xF833,0x0} }, */

  /* cha 4 */
  { {0x091a,0x0},                       {0x091a,0x0} },
  /* cha  + halant -> half cha   */
  { {0x091a,0x094d,0x0},                {0xF7CE,0x0} },
  /* cha + halant ra -> chra  */
  { {0x091a,0x094d,0x0930,0x0},         {0xF7CF,0x093E,0x0} },
  /* cha  + halant ra + halant  */
  { {0x091a,0x094d,0x0930,0x094d,0x0},  {0xF7CF,0x0} },

  /* chha 1 */
  { {0x091b,0x0},                       {0x091b,0xF834,0x0} },

  /* chha + halant -> chha+ halant */
  /*  { {0x091b,0x094d,0x0 },           {0x091b,0x094d,0xF834,0x0} }, */

  /* ja 6 */
  /* ja  */
  { {0x091c,0x0},                       {0x091c,0x0} },
  /* ja + halant -> half ja  */
  { {0x091c,0x094d,0x0},                {0xF7D0,0x0} },
  /* ja + nukta -> za */
  { {0x091c,0x093c,0x0},                {0xF7D1,0x093E,0x0} },
  /* ja + nukta + halant -> half za */
  { {0x091c,0x093c,0x094d,0x0 },        {0xF7D1,0x0} },
  /* ja + halant + ra -> jra  */
  { {0x091c,0x094d,0x0930,0x0},         {0xF7D2,0x093E,0x0} },
  /* ja + halant + ra + halant -> */
  { {0x091c,0x094d,0x0930,0x094d,0x0},  {0xF7D2,0x0} },

  /* dna 2 */
  /* ja + halant + jna -> dna  */
  { {0x091c,0x094d,0x091e,0x0},         {0xF7D3,0x093E,0x0} },
  /* ja + halant + jna -> half dna  */
  { {0x091c,0x094d,0x091e,0x094d,0x0},  {0xF7D3,0x0} },

  /* jha 4 */
  /* jha  */
  { {0x091d,0x0},                       {0x091d,0x0} },
  /* jha + halant -> half jha  */
  { {0x091d,0x094d,0x0},                {0xF7D4,0x0} },
  /* jha + halant -> jhra */
  { {0x091d,0x094d,0x0930,0x0},         {0xF7D5,0x093E,0x0} },
  /*  jha + halant -> half jhra */
  { {0x091d,0x094d,0x0930,0x094d,0x0},  {0xF7D5,0x0} },

  /* nya 2 */
  /* nya */
  { {0x091e,0x0},                       {0x091e,0x0} },
  /* nya + halant -> half nya */
  { {0x091e,0x094d,0x0},                {0xF7D6,0x0} },

  { {0x091e,0x094d,0x091c,0x0},         {0xF846,0x0} },

  /* Ta 3 */
  /* Ta -> Ta + kern space */
  { {0x091f,0x0},                       {0x091F,0xF835,0x0} },

  { {0x091f,0x094d,0x0},                {0x091f,0x094d,0xF835,0x0} },

  /* Ta + halant + Ta -> TaTa + kern space */
  { {0x091f,0x094d,0x091f,0x0},         {0xF7D7,0xF835,0x0} },
  /* Ta + halant + Tha -> TaTha + kern space */
  { {0x091f,0x094d,0x0920,0x0},         {0xF7D8,0xF835,0x0} },

  /* Tha 2 */
  /* Tha -> Tha + kern space */
  { {0x0920,0x0},                       {0x0920,0xF836,0x0} },

  { {0x0920,0x094d,0x0},                {0x0920,0x094d,0xF836,0x0} },

  /* Tha + halant + Tha -> + kern space */
  { {0x0920,0x094d,0x0920,0x0},         {0xF7D9,0xF836,0x0} },

  /* Da 1 */
  /* Da  -> Da  + kern space */
  { {0x0921,0x0},                       {0x0921,0xF837,0x0} },

  { {0x0921,0x094d,0x0},                {0x0921,0x094d,0xF837,0x0} },
 
  /* Da nukta 1 */
  /* Da + nukta -> + kern space */
  { {0x0921,0x093c,0x0},                {0x095c,0xF837,0x0} },

  /* Da + nukta+ halant */
  { {0x0921,0x093c,0x094d,0x0},         {0x095c,0x094d,0xF837,0x0} },

  { {0x0921,0x094d,0x0917,0x0},         {0xF847,0xF837,0x0} },

  /* Da halant Da 1 */
  /* Da + halant + Da - > + kern space */
  { {0x0921,0x094d,0x0921,0x0},         {0xF7DA,0xF837,0x0} },

  /* Da halant Dha 1 */
  /* Da + halant + Dha -> + kern space */
  { {0x0921,0x094d,0x0922,0x0},         {0xF7DB,0xF837,0x0} },

  /* Dha 1 */
  /* Dha  + kern space */
  { {0x0922,0x0},                       {0x0922,0xF838,0x0} },

  /* Dha nukta 1 */
  /* Dha + nukta -> + kern space */
  { {0x0922,0x093c,0x0},                {0x095d,0xF838,0x0} },
  { {0x0922,0x093c,0x094d,0x0},         {0x095d,0x094d,0xF838,0x0} },

  { {0x0922,0x094d,0x0},                {0x0922,0x094d,0xF838,0x0} },

  /* Nna 2 */
  /* Nna */
  { {0x0923,0x0},                       {0x0923,0x0} },
  /* Nna + halant -> half Nna */
  { {0x0923,0x094d,0x0},                {0xF7DC,0x0} },

  /* ta 6 */
  /* ta */
  { {0x0924,0x0},                       {0x0924,0x0} },
  /* ta + halant -> half ta */
  { {0x0924,0x094d,0x0},                {0xF7DD,0x0} },
  /* ta + halant + ra -> half tra */
  { {0x0924,0x094d,0x0930,0x0},         {0xF7DE,0x093E,0x0} },
  /* ta + halant + ra + halant -> half tra  */
  { {0x0924,0x094d,0x0930,0x094d,0x0},  {0xF7DE,0x0} },
  /* ta + halant + ta -> */
  { {0x0924,0x094d,0x0924,0x0},         {0xF7DF,0x093E,0x0} },
  /* ta + halant + ta + halant -> */
  { {0x0924,0x094d,0x0924,0x094d,0x0},  {0xF7DF,0x0} },

  /* tha 4 */
  /* tha */
  { {0x0925,0x0},                       {0x0925,0x0} },
  /* tha + halant -> half tha */
  { {0x0925,0x094d,0x0},                {0xF7E0,0x0} },
  /* tha + halant + ra ->  */
  { {0x0925,0x094d,0x0930,0x0},         {0xF7E1,0x093E,0x0} },
  /* tha + halant + ra + halant -> */
  { {0x0925,0x094d,0x0930,0x094d,0x0},  {0xF7E1,0x0} },

  /* da 1 */
  /* da -> da + kern space */
  { {0x0926,0x0},                       {0x0926,0xF839,0x0} },
  /* da + halant -> half da + kern space */
  { {0x0926,0x094d,0x0},                {0x0926,0x094d,0xF839,0x0} },

  /* da ri 1 */
  /* da + ru -> da + ru + kern space */
  { {0x0926,0x0943,0x0},                {0xF7E2,0xF839,0x0} },

  /* da halant ra 1 */
  /* da + halant + ra -> dra + kern space  */
  { {0x0926,0x094d,0x0930,0x0},         {0xF7E3,0xF839,0x0} },
  { {0x0926,0x094d,0x0930,0x094d,0x0},  {0xF7E3,0x094d,0xF839,0x0} },

  { {0x0926,0x094d,0x0918,0x0},         {0xF848,0xF839,0x0} },

  /* da halant da 1 */
  /* da + halant + da  -> + kern space */
  { {0x0926,0x094d,0x0926,0x0},         {0xF7E4,0xF839,0x0} },

  /* da halant dha 1 */
  /* da + halant + dha -> + kern space  */
  { {0x0926,0x094d,0x0927,0x0},         {0xF7E5,0xF839,0x0} },

  { {0x0926,0x094d,0x092c,0x0},         {0xF849,0xF839,0x0} },

  { {0x0926,0x094d,0x092d,0x0},         {0xF844,0xF839,0x0} },

  /* da halant ma 1 */
  /* da + halant + ma -> + kern space */
  { {0x0926,0x094d,0x092e,0x0},         {0xF7E6,0xF839,0x0} },

  /* da halant ya 1 */
  /* da + halant + ya -> + kern space */
  { {0x0926,0x094d,0x092f,0x0},         {0xF7E7,0xF839,0x0} },

  /* da halant va 1 */
  /* da + halant + va -> + kern space  */
  { {0x0926,0x094d,0x0935,0x0},         {0xF7E8,0xF839,0x0} },

  /* dha 4 */
  /* Dha  */
  { {0x0927,0x0},                       {0x0927,0x0} },
  /* Dha + halant - > half Dha */
  { {0x0927,0x094d,0x0},                {0xF7E9,0x0} },
  /* Dha + halant + ra -> half Dhra */
  { {0x0927,0x094d,0x0930,0x0},         {0xF7EA,0x093E,0x0} },
  /* Dha + halant + ra + halant ->  */
  { {0x0927,0x094d,0x0930,0x094d,0x0},  {0xF7EA,0x0} },

  /* na 6 */
  /* na */
  { {0x0928,0x0},                       {0x0928,0x0} },
  /* na + halant  ->  half na */
  { {0x0928,0x094d,0x0},                {0xF7EB,0x0} },
  /* na + halant + ra  -> */
  { {0x0928,0x094d,0x0930,0x0},         {0xF7EC,0x093E,0x0} },
  /* na + halant + ra + halant -> */
  { {0x0928,0x094d,0x0930,0x094d,0x0},  {0xF7EC,0x0} },
  /* na + halant + na -> */
  { {0x0928,0x094d,0x0928,0x0},         {0xF7ED,0x093E,0x0} },
  /* na + halant + na + halant -> */
  { {0x0928,0x094d,0x0928,0x094d,0x0},  {0xF7ED,0x0} },

  { {0x0929,0x0},                       {0x0929,0x0} },

  /* pa 4 */
  /* pa */
  { {0x092a,0x0},                       {0x092a,0x0} },
  /* pa + halant -> half pa */
  { {0x092a,0x094d,0x0},                {0xF7EE,0x0} },
  /* pa + halant +ra -> pra */
  { {0x092a,0x094d,0x0930,0x0},         {0xF7EF,0x093E,0x0} },
  /* pa + halant + ra + halant -> half pra */
  { {0x092a,0x094d,0x0930,0x094d,0x0},  {0xF7EF,0x0} },

  /* pha 5 */
  /* pha -> pha + kern space */
  { {0x092b,0x0 },                      {0x092b,0xF832,0x0} },
  /* pha + halant -> half pha */
  { {0x092b,0x094d,0x0},                {0xF7F0,0x0} },
  /* pha nukta -> pha nukta + kern space */
  { {0x092b,0x093c,0x0},                {0x095e,0xF832,0x0} },
  /* pha nukta + halant-> half pha nukta */
  { {0x092b,0x093c,0x094d,0x0},         {0xF7F1,0x0} },
  /* pha + halant + ra -> fra + kern space */
  { {0x092b,0x094d,0x0930,0x0},         {0xF7F5,0xF832,0x0} },
  /* pha + halant + ra -> fra + kern space */
  { {0x092b,0x094d,0x0930,0x094d,0x0},  {0xF7F5,0xF832,0x094d,0x0} },

  /* ba 4 */
  /* ba */
  { {0x092c,0x0},                       {0x092c,0x0} },
  /* ba + halant -> half ba */
  { {0x092c,0x094d,0x0},                {0xF7F6,0x0} },
  /* ba + halant + ra -> */
  { {0x092c,0x094d,0x0930,0x0},         {0xF7F7,0x093E,0x0} },
  /* ba + halant ra + halant -> */
  { {0x092c,0x094d,0x0930,0x094d,0x0},  {0xF7F7,0x0} },

  /* bha 4 */
  /* bha  */
  { {0x092d,0x0},                       {0x092d,0x0} },
  /* bha + halant -> half halant  */
  { {0x092d,0x094d,0x0},                {0xF7F8,0x0} },
  /* bha + halant + ra ->  */
  { {0x092d,0x094d,0x0930,0x0},         {0xF7F9,0x093E,0x0} },
  /* bha + halant + ra + halant ->  */
  { {0x092d,0x094d,0x0930,0x094d,0x0},  {0xF7F9,0x0} },
  /* ma 4 */
  /* ma  */
  { {0x092e,0x0},                       {0x092e,0x0} },
  /* ma + halant -> half ma */
  { {0x092e,0x094d,0x0},                {0xF7FA,0x0} },
  /* ma + halant + ra -> */
  { {0x092e,0x094d,0x0930,0x0},         {0xF7FB,0x093E,0x0} },
  /* ma + halant + ra + halant ->  */
  { {0x092e,0x094d,0x0930,0x094d,0x0},  {0xF7FB,0x0} },

  /* ya 4 */
  /* ya */
  { {0x092f,0x0},                       {0x092f,0x0} },
  /* ya + halant -> half ya */
  { {0x092f,0x094d,0x0},                {0xF7FC,0x0} },
  /* ya + halant + ra -> */
  { {0x092f,0x094d,0x0930,0x0},         {0xF7FD,0x093E,0x0} },
  /* ya + halant + ra + halant -> */
  { {0x092f,0x094d,0x0930,0x094d,0x0},  {0xF7FD,0x0} },

  /* ra 3 */
  /* ra  */
  { {0x0930,0x0 },                      {0x0930,0xF83A,0x0} },
  /*  { {0x0930,0x094d,0x0},                 {0xF812,0x0} }, */
  /* ra + u -> Ru + kern space */
  { {0x0930,0x0941,0x0},                {0xF800,0xF83B,0x0} },
  /* ra + U -> RU + kern space */
  { {0x0930,0x0942,0x0},                {0xF801,0xF83C,0x0} },

  { {0x0931,0x0},                       {0x0931,0x0} },
  { {0x0931,0x094d,0x0},                {0xF7FF,0x0} },

  /* la 2 */
  /* la  */
  { {0x0932,0x0},                       {0x0932,0x0} },
  /* la + halant -> */
  { {0x0932,0x094d,0x0},                {0xF802,0x0} },
  /* La 2 */
  /* La */
  { {0x0933,0x0},                       {0x0933,0x0} },
  /* La + halant -> half La */
  { {0x0933,0x094d,0x0},                {0xF803,0x0} },

  { {0x0934,0x0} ,                      {0x0934,0x0} },
  /* va 4 */
  /* va */
  { {0x0935,0x0},                       {0x0935,0x0} },
  /* va + halant -> half va */
  { {0x0935,0x094d,0x0},                {0xF804,0x0} },
  /* va + halant + ra ->  */
  { {0x0935,0x094d,0x0930,0x0},         {0xF805,0x093E,0x0} },
  /* va + halant + ra + halant ->   */
  { {0x0935,0x094d,0x0930,0x094d,0x0},  {0xF805,0x0} },

  /* sha 6 */
  /* sha */
  { {0x0936,0x0},                       {0x0936,0x0} },
  /* sha + halant -> half sha */
  { {0x0936,0x094d,0x0},                {0xF806,0x0} },

  { {0x0936,0x094d,0x091a,0x0},         {0xF83F,0x0} },
  { {0x0936,0x094d,0x0928,0x0},         {0xF840,0x0} },

  /* sha + halant + va -> shwa */
  { {0x0936,0x094d,0x0935,0x0},         {0xF807,0x093E,0x0} },
  /* sha + halant + va + halant -> half shwa */
  { {0x0936,0x094d,0x0935,0x094d,0x0},  {0xF807,0x0} },
  /* sha + halant + ra -> shra */
  { {0x0936,0x094d,0x0930,0x0},         {0xF808,0x093E,0x0} },
  /* sha + halant + ra + halant -> half shra */
  { {0x0936,0x094d,0x0930,0x094d,0x0},  {0xF808,0x0} },
  /* SHa 2 */
  /* SHa */
  { {0x0937,0x0},                       {0x0937,0x0} },
  /* SHa + halant -> half SHa  */
  { {0x0937,0x094d,0x0},                {0xF809,0x0} },

  { {0x0937,0x094d,0x091f,0x0},         {0xF841,0xF835,0x0} },
  { {0x0937,0x094d,0x0920,0x0},         {0xF842,0xF836,0x0} },

  /* sa 4 */
  /* sa */
  { {0x0938,0x0},                       {0x0938,0x0} },
  /* sa + halant -> half sa */
  { {0x0938,0x094d,0x0},                {0xF80A,0x0} },
  /* sa + halant + ra ->  */
  { {0x0938,0x094d,0x0930,0x0},         {0xF80B,0x093E,0x0} },
  /* sa + halan + ra + halant -> */
  { {0x0938,0x094d,0x0930,0x094d,0x0},  {0xF80B,0x0} },

  { {0x0938,0x094d,0x0924,0x094d,0x0930,0x0}, {0xF843,0x0} },

  /* ha 2 */
  /* ha + kern space  */
  { {0x0939,0x0},                       {0x0939,0xF83E,0x0} },
  /* ha + halant  -> half ha */
  { {0x0939,0x094d,0x0},                {0xF80C,0xF83E,0x0} },
  /* ha + Rii + matra -> */
  { {0x0939,0x0943,0x0},                {0xF80D,0xF83E,0x0} },
  /* ha + halant + ra -> */
  { {0x0939,0x094d,0x0930,0x0},         {0xF80E,0xF83E,0x0} },
  /* ha + halant + ra + halant -> */
  { {0x0939,0x094d,0x0930,0x094d,0x0},  {0xF80E,0x094d,0xF83E,0x0} },

  { {0x0939,0x094d,0x0923,0x0},         {0xF84D,0xF83E,0x0} },
  { {0x0939,0x094d,0x0928,0x0},         {0xF84C,0xF83E,0x0} },

  /* ha + halant + ma -> */
  { {0x0939,0x094d,0x092e,0x0},         {0xF80F,0xF83E,0x0} },
  /* ha + halant + ya -> */
  { {0x0939,0x094d,0x092f,0x0},         {0xF810,0xF83E,0x0} },

  { {0x0939,0x094d,0x0932,0x0},         {0xF84A,0xF83E,0x0} },
  { {0x0939,0x094d,0x0935,0x0},         {0xF84B,0xF83E,0x0} },

  { {0x0958,0x0},                       {0x0958,0xF832,0x0} },
  { {0x0959,0x0},                       {0x0959,0x0} },
  { {0x095a,0x0},                       {0x095a,0x0} },
  { {0x095b,0x0},                       {0x095b,0x0} },
  { {0x095c,0x0},                       {0x095c,0xF837,0x0} },
  { {0x095d,0x0},                       {0x095d,0xF838,0x0} },
  { {0x095e,0x0},                       {0x095e,0xF832,0x0} },
  { {0x095f,0x0},                       {0x095f,0x0} },

  /*"\xd8\x", "", */

  /* misc 5 */

  /* nukta */
  { {0x093c,0x0},                       {0x093c,0x0} },
  /* nukta + u matra */
  { {0x093c,0x0941,0x0},                {0xF81E,0x0} },
  /* nukta + Uu matra -> */
  { {0x093c,0x0942,0x0},                {0xF821,0x0} },

  /* halant */
  { {0x094d,0x0},                       {0x094d,0x0} },
  /* halant + ya -> */
  { {0x094d,0x092f,0x0},                {0xF7FE,0x0} },
  /* halant + ra  */
  { {0x094d,0x0930,0x0},                {0xF811,0x0} },
  /* halant + ra + halant */
  { {0x094d,0x0930,0x094d,0x0},         {0x094d,0x0930,0x0930,0x0} },
  /* halant + ra + u matra -> */
  { {0x094d,0x0930,0x0941,0x0},         {0xF81F,0x0} },
  /* halant + ra + Uu matra ->  */
  { {0x094d,0x0930,0x0942,0x0},         {0xF822,0x0} },

  /* Vedic Characters */
  { {0x093d,0x0},                       {0x093d,0x0} },
  { {0x0950,0x0},                       {0x0950,0x0} },
  { {0x0951,0x0},                       {0x0951,0x0} },
  { {0x0952,0x0},                       {0x0952,0x0} },
  { {0x0953,0x0},                       {0x0953,0x0} },
  { {0x0954,0x0},                       {0x0954,0x0} },
  { {0x0964,0x0},                       {0x0964,0x0} },
  { {0x0965,0x0},                       {0x0965,0x0} },
  { {0x0970,0x0},                       {0x0970,0x0} },

  /* Dig09its */
  { {0x0966,0x0},                       {0x0966,0x0} },
  { {0x0967,0x0},                       {0x0967,0x0} },
  { {0x0968,0x0},                       {0x0968,0x0} },
  { {0x0969,0x0},                       {0x0969,0x0} },
  { {0x096a,0x0},                       {0x096a,0x0} },
  { {0x096b,0x0},                       {0x096b,0x0} },
  { {0x096c,0x0},                       {0x096c,0x0} },
  { {0x096d,0x0},                       {0x096d,0x0} },
  { {0x096e,0x0},                       {0x096e,0x0} },
  { {0x096f,0x0},                       {0x096f,0x0} }
};

/* Returns a structure with information we will use to render given the
 * #PangoliteFont. This is computed once per font and cached for retrieval.
 */
static DvngFontInfo *
get_font_info(const char *fontCharset)
{
  static const char *charsets[] = {
    "sun.unicode.india-0",
  };

  static const int charset_types[] = {
    DVNG_FONT_SUN
  };
  
  DvngFontInfo *font_info = g_new(DvngFontInfo, 1);
  guint        i; 

  font_info->type = DVNG_FONT_NONE;
  for (i = 0; i < G_N_ELEMENTS(charsets); i++) {
    if (strcmp(fontCharset, charsets[i]) == 0) {    
      font_info->type = (DvngFontType)charset_types[i];
      font_info->subfont = (PangoliteXSubfont)i;
      break;
    }
  }
  
  return font_info;
}

static void
add_glyph(PangoliteGlyphString *glyphs,
          gint                 clusterStart,
          PangoliteGlyph       glyph,
          gint                 combining)
{
  gint index = glyphs->num_glyphs;

  if ((clusterStart == 0) && (index != 0))
    clusterStart++;

  pangolite_glyph_string_set_size (glyphs, index + 1);  
  glyphs->glyphs[index].glyph = glyph;
  glyphs->glyphs[index].is_cluster_start = combining;
  glyphs->log_clusters[index] = clusterStart;
}

static void
GetBaseConsGlyphs(gunichar2  *cluster,
                  gint       numCoreCons,
                  PangoliteGlyph *glyphList,
                  gint       *nGlyphs)
{
  int i, j, delta, nMin, nMaxRuleCt, ruleIdx;
  gboolean  StillMatching;
  gunichar2 temp_out;
  gint      tmpCt = *nGlyphs;

  i = 0;
  while (i < numCoreCons) {
    
    nMaxRuleCt = GetDvngRuleCt(cluster[i]);    
    while (nMaxRuleCt) {
      nMin = MIN(nMaxRuleCt, numCoreCons);      
      ruleIdx = 0;

NotFound:
      j = delta = 0;
      StillMatching = FALSE;
      while (((delta < nMin) || sunGlyphTbl[ruleIdx].ISCII[j]) &&
             (ruleIdx < MAP_SIZE) ) {
        StillMatching = TRUE;
        if ((delta < nMin) && (j < MAX_CORE_CONS) &&
            (cluster[i + delta] != sunGlyphTbl[ruleIdx].ISCII[j])) {
          ruleIdx++;
          goto NotFound;
        }
        delta++;
        j++;
      }
      
      if (StillMatching) /* Found */
        break;
      else
        nMaxRuleCt--;
    }
    
    i += nMin;
    
    /* Can't find entry in the table */
    if ((StillMatching == FALSE) || (ruleIdx >= MAP_SIZE)) {
      for (j = 0; j < numCoreCons; j++)
        glyphList[tmpCt++] = PANGO_MOZ_MAKE_GLYPH(cluster[j]);
    }
    else if (((tmpCt > 0) && IsKern(glyphList[tmpCt - 1])) &&
             IsMatraAtStem(sunGlyphTbl[ruleIdx].ISFOC[0])) {
      temp_out = glyphList[tmpCt - 1];
      
      for (j=0; sunGlyphTbl[ruleIdx].ISFOC[j]; j++)
        glyphList[tmpCt - 1] = PANGO_MOZ_MAKE_GLYPH(sunGlyphTbl[ruleIdx].ISFOC[j]);
        
      glyphList[tmpCt++] = PANGO_MOZ_MAKE_GLYPH(temp_out);
    }
    else {
      for (j=0; sunGlyphTbl[ruleIdx].ISFOC[j]; j++)
        glyphList[tmpCt++] = PANGO_MOZ_MAKE_GLYPH(sunGlyphTbl[ruleIdx].ISFOC[j]);
    }
  }
  *nGlyphs = tmpCt;
}

static gint
get_adjusted_glyphs_list(DvngFontInfo *fontInfo,
                         gunichar2    *cluster,
                         gint         nChars,
                         PangoliteGlyph   *gLst,
                         StateType    *DvngClusterState)
{
  int i, k, len;
  gint      nGlyphs = 0;
  gunichar2 dummy;
  
  switch (*DvngClusterState) {
  case St1:
    if (IsDvngCharCls(cluster[0], _IM)) {
      GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF7C0);
    }
    else {
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF7C0);
      GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);
    }
    break;

    case St2:
    case St3:
    case St4:
    case St5:
    case St6:
      GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);
      break;
      
    case St7:
      if (IsDvngCharCls(cluster[nChars - 1], _UP)) {

        if (IsDvngCharCls(cluster[nChars - 2], _RM))
          GetBaseConsGlyphs(cluster, nChars - 2, gLst, &nGlyphs);
        else
          GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);

        if (IsDvngCharCls(cluster[nChars - 2], _RM)) {

          if (IsDvngCharCls(cluster[nChars - 2], _II_M))
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF81B);

          else if (IsDvngCharCls(cluster[nChars - 2], _AYE_M)) {
            dummy = gLst[nGlyphs - 1];
            gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(0xF82D);
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
          }

          else if (IsDvngCharCls(cluster[nChars - 2], _EE_M))
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF824);
          
          else if (IsDvngCharCls(cluster[nChars - 2], _EY_M))
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF827);

          else if (IsDvngCharCls(cluster[nChars - 2], _AI_M))
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82A);

          else if (IsDvngCharCls(cluster[nChars - 2], _AWE_M)) {
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82D);
          }

          else if (IsDvngCharCls(cluster[nChars - 2], _O_M)) {
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF824);
          }

          else if (IsDvngCharCls(cluster[nChars - 2], _OW1_M)) {
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF827);
          }

          else if (IsDvngCharCls(cluster[nChars - 2], _OW2_M)) {
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
            gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82A);
          }
        }
      }
      else {
        GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);
      }
      break;

    case St8:
      GetBaseConsGlyphs(cluster, nChars - 1, gLst, &nGlyphs);
      if (IsKern(gLst[nGlyphs - 1])) {
        dummy = gLst[nGlyphs - 1];
        gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(cluster[nChars - 1]);
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
      }
      else
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(cluster[nChars - 1]);
      break;
      
    case St9:
      if (IsDvngCharCls(cluster[0], _MS))
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093F);
      else
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF817);        

      GetBaseConsGlyphs(cluster, nChars - 1, gLst, &nGlyphs);
      break;

  case St10:
    if (IsDvngCharCls(cluster[0], _MS))
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF814);
    else
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF818);

    GetBaseConsGlyphs(cluster, nChars - 2, gLst, &nGlyphs);
    break;
    
  case St11:
    GetBaseConsGlyphs(cluster, nChars, gLst, &nGlyphs);
    break;
    
  case St12:
    gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x0930);
    gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF83A);
    break;
    
  case St13:
    gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x0930);
    gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x094D);
    gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF83A);
    break;
    
  case St14:    
    GetBaseConsGlyphs(cluster+2, nChars - 2, gLst, &nGlyphs);
    if (IsKern(gLst[nGlyphs - 1])) {
      dummy = gLst[nGlyphs - 1];
      gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(0xF812);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
    }
    else
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF812);
    break;
    
  case St15:    
    GetBaseConsGlyphs(cluster+2, nChars - 3, gLst, &nGlyphs);
    if (IsKern(gLst[nGlyphs - 1])) {      
      dummy = gLst[nGlyphs - 2];
      gLst[nGlyphs - 2] = PANGO_MOZ_MAKE_GLYPH(0xF812);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x094D);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
    }
    else {
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF812);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x094D);
    }
    break;
    
  case St16:
    if (IsDvngCharCls(cluster[nChars - 1], _RM))
      GetBaseConsGlyphs(cluster+2, nChars - 3, gLst, &nGlyphs);
    else
      GetBaseConsGlyphs(cluster+2, nChars - 2, gLst, &nGlyphs);

    if (IsDvngCharCls(cluster[nChars - 1], ~(_RM))){

      if (IsKern(gLst[nGlyphs - 1])) {
        dummy = gLst[nGlyphs - 1];
        gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(0xF812);
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
      }
      else
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF812);
    }
    else {

      if (IsDvngCharCls(cluster[nChars - 1], _II_M))
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF81C);

      else if (IsDvngCharCls(cluster[nChars - 1], _EY_M))
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF828);

      else if (IsDvngCharCls(cluster[nChars -1], _AI_M))
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82B);

      else if (IsDvngCharCls(cluster[nChars - 1], _OW1_M)) {
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF828);
      }

      else if (IsDvngCharCls(cluster[nChars - 1], _OW2_M)) {
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
        gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82B);
      }
    }
    break;
    
  case St17:
    if (IsDvngCharCls(cluster[nChars - 1], _UP)) {

      if (IsDvngCharCls(cluster[nChars - 2], _RM))
        GetBaseConsGlyphs(cluster+2, nChars - 4, gLst, &nGlyphs);
      else
        GetBaseConsGlyphs(cluster+2, nChars - 3, gLst, &nGlyphs);

      if (IsDvngCharCls(cluster[nChars - 2], ~(_RM))) {

        if (IsKern(gLst[nGlyphs - 1])) {
          dummy = gLst[nGlyphs - 1];
          gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(0xF813);
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
        }
        else
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF813);
      }
      else {
        if (IsDvngCharCls(cluster[nChars - 2], _II_M))
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF81D);

        else if (IsDvngCharCls(cluster[nChars - 2], _EY_M))
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF829);

        else if (IsDvngCharCls(cluster[nChars - 2], _AI_M))
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82C);

        else if (IsDvngCharCls(cluster[nChars - 2], _OW1_M)) {
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF829);
        }
        else if (IsDvngCharCls(cluster[nChars - 2], _OW2_M)) {
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0x093E);
          gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF82C);
        }
      } /* ? else GetBaseConsGlyphs(); */
    break;
    
  case St18:
    GetBaseConsGlyphs(cluster-2, nChars-3, gLst, &nGlyphs);
    if (IsKern(gLst[nGlyphs - 1])) {
      dummy = gLst[nGlyphs - 1];
      gLst[nGlyphs - 1] = PANGO_MOZ_MAKE_GLYPH(0xF813);
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(dummy);
    }
    else
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF813);
    break;
    
  case St19:
    if (IsDvngCharCls(cluster[0], _MS))
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF815);
    else
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF819);

    GetBaseConsGlyphs(cluster+2, nChars-3, gLst, &nGlyphs);
    break;
    
  case St20:
    if (IsDvngCharCls(cluster[0], _MS))
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF816);
    else
      gLst[nGlyphs++] = PANGO_MOZ_MAKE_GLYPH(0xF81A);

    GetBaseConsGlyphs(cluster+2, nChars - 4, gLst, &nGlyphs);
    break;
    }
  }

  return nGlyphs;
}

static gint
get_glyphs_list(DvngFontInfo *fontInfo,
                gunichar2    *cluster,
                gint          numChars,
                PangoliteGlyph   *glyphLst,
                StateType    *clustState)
{
  PangoliteGlyph glyph;
  gint       i;

  switch (fontInfo->type) {
  case DVNG_FONT_NONE:
    for (i = 0; i < numChars; i++)
      glyphLst[i] = 0; /*pangolite_x_get_unknown_glyph(fontInfo->font);*/
    return numChars;
    
  case DVNG_FONT_SUN:
    return get_adjusted_glyphs_list(fontInfo, cluster, numChars, glyphLst, clustState);
  }
  
  return 0; /* Quiet GCC */
}

static void
add_cluster(DvngFontInfo     *fontInfo,
            PangoliteGlyphString *glyphs,
            gint              clusterBeg,
            gunichar2        *cluster,
            gint              numChars,
            StateType        *clustState)
{
  PangoliteGlyph glyphsList[MAX_GLYPHS];
  gint           i, numGlyphs, ClusterStart=0;
  
  numGlyphs = get_glyphs_list(fontInfo, cluster, numChars, glyphsList, clustState);
  for (i = 0; i < numGlyphs; i++) {

    ClusterStart = (gint)GLYPH_COMBINING;
    if (i == 0)
      ClusterStart = numChars;
    add_glyph(glyphs, clusterBeg, glyphsList[i], ClusterStart);
  }
}

static const gunichar2 *
get_next_cluster(const gunichar2 *text,
                 gint             length,
                 gunichar2       *cluster,
                 gint            *numChars,
                 StateType       *clustState)
{
  const gunichar2 *p;
  gint            n_chars = 0;
  StateType       aSt = *clustState;

  p = text;
  while (p < text + length) {
    gunichar2 cur = *p;
    
    if ((n_chars == 0) ||
        ((n_chars > 0) && IsComposible(cluster[n_chars - 1], cur))) {
      cluster[n_chars++] = cur;
      aSt = SetClusterState(aSt, cur);
      p++;
    }
    else
      break;
  }
  
  *numChars = n_chars;
  *clustState = aSt;
  return p;
}

static void 
dvng_engine_shape(const char       *fontCharset,
                  const gunichar2  *text,
                  gint             length,
                  PangoliteAnalysis    *analysis,
                  PangoliteGlyphString *glyphs)
{
  DvngFontInfo    *fontInfo;
  const gunichar2 *p, *log_cluster;
  gunichar2       cluster[MAX_CLUSTER_CHRS];
  gint            num_chrs;
  StateType       aSt = St0;

  fontInfo = get_font_info(fontCharset);

  p = text;
  while (p < text + length) {
    log_cluster = p;
    aSt = St0;
    p = get_next_cluster(p, text + length - p, cluster, &num_chrs, &aSt);
    add_cluster(fontInfo, glyphs, log_cluster-text, cluster, num_chrs, &aSt);
  }
}

static PangoliteCoverage *
dvng_engine_get_coverage(const char *fontCharset,
                         const char *lang)
{
  PangoliteCoverage *result = pangolite_coverage_new ();  
  DvngFontInfo  *fontInfo = get_font_info (fontCharset);
  
  if (fontInfo->type != DVNG_FONT_NONE) {
    gunichar2 wc;
 
    for (wc = 0x901; wc <= 0x903; wc++)
      pangolite_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    for (wc = 0x905; wc <= 0x939; wc++)
      pangolite_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    for (wc = 0x93c; wc <= 0x94d; wc++)
      pangolite_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    for (wc = 0x950; wc <= 0x954; wc++)
      pangolite_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    for (wc = 0x958; wc <= 0x970; wc++)
      pangolite_coverage_set (result, wc, PANGO_COVERAGE_EXACT);
    /*    pangolite_coverage_set (result, ZWJ, PANGO_COVERAGE_EXACT); */
  }
  
  return result;
}

static PangoliteEngine *
dvng_engine_x_new ()
{
  PangoliteEngineShape *result;
  
  result = g_new (PangoliteEngineShape, 1);
  result->engine.id = SCRIPT_ENGINE_NAME;
  result->engine.type = PANGO_ENGINE_TYPE_SHAPE;
  result->engine.length = sizeof (result);
  result->script_shape = dvng_engine_shape;
  result->get_coverage = dvng_engine_get_coverage;
  return (PangoliteEngine *)result;
}

/* The following three functions provide the public module API for
 * Pangolite. 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 Pangolite, then we need them to
 * to have distinct names for this module, so we prepend
 * _pangolite_thai_x_
 */
#ifdef X_MODULE_PREFIX
#define MODULE_ENTRY(func) _pangolite_dvng_x_##func
#else
#define MODULE_ENTRY(func) func
#endif

/* List the engines contained within this module
 */
void 
MODULE_ENTRY(script_engine_list) (PangoliteEngineInfo **engines, gint *n_engines)
{
  *engines = script_engines;
  *n_engines = G_N_ELEMENTS (script_engines);
}

/* Load a particular engine given the ID for the engine
 */
PangoliteEngine *
MODULE_ENTRY(script_engine_load) (const char *id)
{
  if (!strcmp (id, SCRIPT_ENGINE_NAME))
    return dvng_engine_x_new ();
  else
    return NULL;
}

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

